| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- <?php defined('BX_DOL') or die('hack attempt');
- /**
- * Copyright (c) UNA, Inc - https://una.io
- * MIT License - https://opensource.org/licenses/MIT
- *
- * @defgroup Payment Payment
- * @ingroup UnaModules
- *
- * @{
- */
- /**
- * Usefull links related to the integration:
- *
- * HTML Form Basics for PayPal Payments Standard
- * https://developer.paypal.com/docs/classic/paypal-payments-standard/integration-guide/formbasics/
- *
- * HTML Variables for PayPal Payments Standard
- * https://developer.paypal.com/docs/classic/paypal-payments-standard/integration-guide/Appx_websitestandard_htmlvariables/
- *
- */
- define('PP_MODE_LIVE', 1);
- define('PP_MODE_TEST', 2);
- define('PP_PRC_TYPE_DIRECT', 1);
- define('PP_PRC_TYPE_PDT', 2);
- define('PP_PRC_TYPE_IPN', 3);
- class BxPaymentProviderPayPal extends BxBaseModPaymentProvider implements iBxBaseModPaymentProvider
- {
- function __construct($aConfig)
- {
- $this->MODULE = 'bx_payment';
- parent::__construct($aConfig);
- $this->_bRedirectOnResult = false;
- $this->_sLogFile = BX_DIRECTORY_PATH_LOGS . 'bx_pp_' . $this->_sName . '.log';
- }
- public function initializeCheckout($iPendingId, $aCartInfo, $bRecurring = false, $iRecurringDays = 0)
- {
- $iMode = (int)$this->getOption('mode');
- $sActionURL = $iMode == PP_MODE_LIVE ? 'https://www.paypal.com/cgi-bin/webscr' : 'https://www.sandbox.paypal.com/cgi-bin/webscr';
- if($bRecurring)
- $aFormData = array(
- 'cmd' => '_xclick-subscriptions',
- 'a3' => sprintf("%.2f", (float)$aCartInfo['items_price']),
- 'p3' => $iRecurringDays,
- 't3' => 'D',
- 'src' => '1', // repeat billings unles member cancels subscription
- 'sra' => '1' // reattempt on failure
- );
- else
- $aFormData = array(
- 'cmd' => '_cart',
- 'upload' => '1',
- 'amount' => sprintf( "%.2f", (float)$aCartInfo['items_price'])
- );
- $aFormData = array_merge($aFormData, array(
- 'business' => $iMode == PP_MODE_LIVE ? $this->getOption('business') : $this->getOption('sandbox'),
- 'currency_code' => $aCartInfo['vendor_currency_code'],
- 'item_name' => _t($this->_sLangsPrefix . 'txt_payment_to_for', $aCartInfo['vendor_name']),
- 'item_number' => $iPendingId,
- 'no_note' => '1',
- 'no_shipping' => '1',
- 'custom' => md5($aCartInfo['vendor_id'] . $iPendingId)
- ));
- foreach($aCartInfo['items'] as $aItem)
- $aFormData['item_name'] .= ' ' . $aItem['title'] . ',';
- $aFormData['item_name'] = trim($aFormData['item_name'], ', ');
- switch($this->getOption('prc_type')) {
- case PP_PRC_TYPE_PDT:
- case PP_PRC_TYPE_DIRECT:
- $aFormData = array_merge($aFormData, array(
- 'return' => $this->getReturnDataUrl($aCartInfo['vendor_id']),
- 'rm' => '2'
- ));
- break;
- case PP_PRC_TYPE_IPN:
- $aFormData = array_merge($aFormData, array(
- 'return' => $this->getReturnUrl(),
- 'notify_url' => $this->getReturnDataUrl($aCartInfo['vendor_id']),
- 'rm' => '1'
- ));
- break;
- }
- $this->_oModule->_oTemplate->displayPageCodeRedirect($sActionURL, $aFormData);
- exit;
- }
- public function finalizeCheckout(&$aData)
- {
- $aResult = $this->_registerCheckout($aData);
- if(!isset($aResult['code']) || (int)$aResult['code'] != BX_PAYMENT_RESULT_SUCCESS) {
- $this->log('Finalize Checkout Failed');
- $this->log($aData);
- }
- return $aResult;
- }
- /**
- *
- * @param $aData - data from payment provider.
- * @param $bSubscription - Is not needed. May be used in the future for subscriptions.
- * @param $iPendingId - Is not needed. May be used in the future for subscriptions.
- * @return array with results.
- */
- protected function _registerCheckout(&$aData, $bSubscription = false, $iPendingId = 0)
- {
- if(empty($this->_aOptions) && isset($aData['item_number']))
- $this->_aOptions = $this->getOptionsByPending($aData['item_number']);
- if(empty($this->_aOptions))
- return array('code' => 2, 'message' => $this->_sLangsPrefix . 'err_unknown_vendor');
- $iPrcType = (int)$this->getOption('prc_type');
- if(($iPrcType == PP_PRC_TYPE_IPN || $iPrcType == PP_PRC_TYPE_DIRECT) && (!isset($aData['item_number']) || !isset($aData['txn_id'])))
- return array('code' => 1, 'message' => $this->_sLangsPrefix . 'pp_err_no_data_given');
- else if($iPrcType == PP_PRC_TYPE_PDT && !isset($aData['tx']))
- return array('code' => 1, 'message' => $this->_sLangsPrefix . 'pp_err_no_data_given');
- $aResult = $this->_validateCheckout($aData);
- if(!$bSubscription || empty($iPendingId))
- $iPendingId = (int)$aData['item_number'];
- $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'id', 'id' => $iPendingId));
- if(!empty($aPending['order']) || !empty($aPending['error_code']) || !empty($aPending['error_msg']) || (int)$aPending['processed'] != 0)
- return array('code' => 3, 'message' => $this->_sLangsPrefix . 'err_already_processed');
- //--- Update pending transaction ---//
- $this->_oModule->_oDb->updateOrderPending($iPendingId, array(
- 'order' => $aData['txn_id'],
- 'error_code' => $aResult['code'],
- 'error_msg' => _t($aResult['message'])
- ));
- $aResult['pending_id'] = $iPendingId;
- $aResult['client_name'] = _t($this->_sLangsPrefix . 'txt_buyer_name_mask', bx_process_input($aData['first_name']), bx_process_input($aData['last_name']));
- $aResult['client_email'] = bx_process_input($aData['payer_email']);
- $aResult['paid'] = true;
- return $aResult;
- }
- protected function _validateCheckout(&$aData)
- {
- $iMode = (int)$this->getOption('mode');
- if($iMode == PP_MODE_LIVE) {
- $sBusiness = $this->getOption('business');
- $sConnectionUrl = 'www.paypal.com';
- } else {
- $sBusiness = $this->getOption('sandbox');
- $sConnectionUrl = 'www.sandbox.paypal.com';
- }
- $iPrcType = $this->getOption('prc_type');
- if($iPrcType == PP_PRC_TYPE_DIRECT || $iPrcType == PP_PRC_TYPE_IPN) {
- if($aData['payment_status'] != 'Completed' )
- return array('code' => 4, 'message' => $this->_sLangsPrefix . 'pp_err_not_completed');
- if($aData['business'] != $sBusiness)
- return array('code' => 5, 'message' => $this->_sLangsPrefix . 'pp_err_wrong_business');
- $sRequest = 'cmd=_notify-validate';
- foreach($aData as $sKey => $sValue) {
- if(in_array($sKey, array('cmd')))
- continue;
- $sRequest .= '&' . urlencode($sKey) . '=' . urlencode(bx_process_pass($sValue));
- }
- $aResponse = $this->_readValidationData($sConnectionUrl, $sRequest);
- if((int)$aResponse['code'] !== 0)
- return $aResponse;
- array_walk($aResponse['content'], create_function('&$arg', "\$arg = trim(\$arg);"));
- if(strcmp($aResponse['content'][0], "INVALID") === 0)
- return array('code' => 7, 'message' => $this->_sLangsPrefix . 'pp_err_wrong_transaction');
- else if(strcmp($aResponse['content'][0], "VERIFIED") !== 0)
- return array('code' => 8, 'message' => $this->_sLangsPrefix . 'pp_err_wrong_verification_status');
- }
- else if($iPrcType == PP_PRC_TYPE_PDT) {
- $sRequest = "cmd=_notify-synch&tx=" . $aData['tx'] . "&at=" . $this->getOption('token');
- $aResponse = $this->_readValidationData($sConnectionUrl, $sRequest);
- if((int)$aResponse['code'] !== 0)
- return $aResponse;
- if(strcmp($aResponse['content'][0], "FAIL") === 0)
- return array('code' => 7, 'message' => $this->_sLangsPrefix . 'pp_err_wrong_transaction');
- else if(strcmp($aResponse['content'][0], "SUCCESS") !== 0)
- return array('code' => 8, 'message' => $this->_sLangsPrefix . 'pp_err_wrong_verification_status');
- $aKeys = array();
- foreach($aResponse['content'] as $sLine) {
- list($sKey, $sValue) = explode("=", $sLine);
- $aKeys[urldecode($sKey)] = urldecode($sValue);
- }
- $aData = array_merge($aData, $aKeys);
- if($aData['payment_status'] != 'Completed' )
- return array('code' => 4, 'message' => $this->_sLangsPrefix . 'pp_err_not_completed');
- if($aData['business'] != $sBusiness)
- return array('code' => 5, 'message' => $this->_sLangsPrefix . 'pp_err_wrong_business');
- }
- $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'id', 'id' => $aData['item_number']));
- $aVendor = $this->_oModule->getVendorInfo($aPending['seller_id']);
- $fAmount = (float)$this->_getReceivedAmount($aVendor['currency_code'], $aData);
- if($fAmount != (float)$aPending['amount'])
- return array('code' => 9, 'message' => $this->_sLangsPrefix . 'pp_err_wrong_amount');
- if($aData['custom'] != md5($aPending['seller_id'] . $aPending['id']))
- return array('code' => 10, 'message' => $this->_sLangsPrefix . 'pp_err_wrong_custom_data');
- return array('code' => BX_PAYMENT_RESULT_SUCCESS, 'message' => $this->_sLangsPrefix . 'pp_msg_verified');
- }
- protected function _readValidationData($sConnectionUrl, $sRequest)
- {
- $rConnect = curl_init('https://' . $sConnectionUrl . '/cgi-bin/webscr');
- curl_setopt($rConnect, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
- curl_setopt($rConnect, CURLOPT_POST, 1);
- curl_setopt($rConnect, CURLOPT_RETURNTRANSFER,1);
- curl_setopt($rConnect, CURLOPT_POSTFIELDS, $sRequest);
- curl_setopt($rConnect, CURLOPT_SSL_VERIFYPEER, 1);
- curl_setopt($rConnect, CURLOPT_SSL_VERIFYHOST, 2);
- curl_setopt($rConnect, CURLOPT_FORBID_REUSE, 1);
- curl_setopt($rConnect, CURLOPT_HTTPHEADER, array('Connection: Close'));
- $sResponse = curl_exec($rConnect);
- if(curl_errno($rConnect) == 60) { // CURLE_SSL_CACERT
- curl_setopt($rConnect, CURLOPT_CAINFO, BX_DIRECTORY_PATH_PLUGINS . 'curl/cacert.pem');
- $sResponse = curl_exec($rConnect);
- }
- if(!$sResponse) {
- $this->log('Validation Data: ' . curl_error($rConnect));
- $this->log($sResponse);
- curl_close($rConnect);
- return array('code' => 6, 'message' => $this->_sLangsPrefix . 'err_cannot_validate');
- }
- curl_close($rConnect);
- return array('code' => 0, 'content' => explode("\n", $sResponse));
- }
- protected function _getReceivedAmount($sCurrencyCode, &$aResultData)
- {
- $fAmount = 0.00;
- $fTax = isset($aResultData['tax']) ? (float)$aResultData['tax'] : 0.00;
- if($aResultData['mc_currency'] == $sCurrencyCode && isset($aResultData['payment_gross']) && !empty($aResultData['payment_gross']))
- $fAmount = (float)$aResultData['payment_gross'] - $fTax;
- else if($aResultData['mc_currency'] == $sCurrencyCode && isset($aResultData['mc_gross']) && !empty($aResultData['mc_gross']))
- $fAmount = (float)$aResultData['mc_gross'] - $fTax;
- else if($aResultData['settle_currency'] == $sCurrencyCode && isset($aResultData['settle_amount']) && !empty($aResultData['settle_amount']))
- $fAmount = (float)$aResultData['settle_amount'] - $fTax;
- return $fAmount;
- }
- }
- /** @} */
|