/** * This is the frontend or backend method used directly * @see cbpaidPaymentBasket::store() * * If table key (id) is NULL : inserts a new row * otherwise updates existing row in the database table * * Can be overridden or overloaded by the child class * * @param boolean $updateNulls TRUE: null object variables are also updated, FALSE: not. * @return boolean TRUE if successful otherwise FALSE */ public function store($updateNulls = false) { global $_CB_framework, $_CB_database; // 1) check: if (!in_array($this->payment_status, array('Pending', 'Refunded', 'NotInitiated'))) { $this->setError(CBPTXT::T("This payment basket is not pending.")); return false; } if ($this->txn_id == '') { $this->txn_id = 'None'; // needed for updatePayment to generate payment record. } $paymentBasket = new cbpaidPaymentBasket($_CB_database); $paymentBasket->load($this->id); if (!$paymentBasket->gateway_account) { $this->setError(CBPTXT::T("This payment basket has no gateway associated so can not be paid manually.")); return false; } $ipn = new cbpaidPaymentNotification($_CB_database); $ipn->bindObjectToThisObject($paymentBasket, 'id'); $ipn->mc_currency = $this->mc_currency; $ipn->mc_gross = $this->mc_gross; if (!preg_match('/^[1-9][0-9]{3}-[01][0-9]-[0-3][0-9]/', $this->time_completed)) { $this->time_completed = Application::Database()->getUtcDateTime(); } $paymentBasket->time_completed = $this->time_completed; $ipn->payment_type = $this->payment_type; $paymentBasket->payment_type = $this->payment_type; $ipn->txn_id = $this->txn_id; $paymentBasket->txn_id = $this->txn_id; $ipn->payment_status = 'Completed'; $ipn->txn_type = 'web_accept'; $ipn->payment_method = $this->payment_method; $ipn->gateway_account = $this->gateway_account; $ipn->log_type = 'P'; $ipn->time_received = $_CB_database->getUtcDateTime(); $ipn->payment_date = gmdate('H:i:s M d, Y T', $this->time_completed ? cbpaidTimes::getInstance()->strToTime($this->time_completed) : cbpaidTimes::getInstance()->startTime()); // paypal-style //TBD FIXME: WE SHOULD CHANGE THIS OLD DATE STYLE ONCE WITH UTC timezone inputed $ipn->payment_basket_id = $this->id; $ipn->raw_result = 'manual'; $ipn->raw_data = ''; $ipn->ip_addresses = cbpaidRequest::getIPlist(); $ipn->user_id = Application::MyUser()->getUserId(); $ipn->txn_id = $this->txn_id; $ipn->payment_type = $this->payment_type; $ipn->charset = $_CB_framework->outputCharset(); //TBD /* $paymentBasket->first_name = $ipn->first_name = cbGetParam( $_POST, 'txtBTitle' ); $paymentBasket->first_name = $ipn->first_name = cbGetParam( $_POST, 'txtBFirstName' ); $paymentBasket->last_name = $ipn->last_name = cbGetParam( $_POST, 'txtBLastName' ); $paymentBasket->address_street = $ipn->address_street = cbGetParam( $_POST, 'txtBAddr1' ); $paymentBasket->address_zip = $ipn->address_zip = cbGetParam( $_POST, 'txtBZipCode' ); $paymentBasket->address_city = $ipn->address_city = cbGetParam( $_POST, 'txtBCity' ); $paymentBasket->address_country = $ipn->address_country = cbGetParam( $_POST, 'txtBCountry' ); //TBD? $paymentBasket->phone = $ipn->phone = cbGetParam( $_POST, 'txtBTel' ); //TBD? $paymentBasket->fax = $ipn->fax = cbGetParam( $_POST, 'txtBFax' ); $paymentBasket->payer_email = $ipn->payer_email = cbGetParam( $_POST, 'txtBEmail' ); */ if (!$_CB_database->insertObject($ipn->getTableName(), $ipn, $ipn->getKeyName())) { trigger_error('store error:' . htmlspecialchars($_CB_database->getErrorMsg()), E_USER_ERROR); //TBD also in paypal: error code 500 !!! } $payAccount = cbpaidControllerPaychoices::getInstance()->getPayAccount($paymentBasket->gateway_account); if (!$payAccount) { $this->setError(CBPTXT::T("This payment basket's associated gateway account is not active, so can not be paid manually.")); return false; } $payClass = $payAccount->getPayMean(); $payClass->updatePaymentStatus($paymentBasket, 'web_accept', 'Completed', $ipn, 1, 0, 0, 'singlepayment'); return true; }
/** * loads the basket of this payment item: cbSubscription + ->reason from payment_items (N=New, R=Renewal) * caches in object * * @return cbpaidPaymentBasket if subscription is loaded or already loaded, or NULL otherwise */ public function loadBasket() { if ($this->payment_basket_id) { $basket = new cbpaidPaymentBasket($this->_db); if ($basket->load((int) $this->payment_basket_id)) { return $basket; } } $false = false; return $false; }
/** * If table key (id) is NULL : inserts a new row * otherwise updates existing row in the database table * * Can be overridden or overloaded by the child class * * @param boolean $updateNulls TRUE: null object variables are also updated, FALSE: not. * @return boolean TRUE if successful otherwise FALSE */ public function store($updateNulls = false) { if (!cbpaidApp::authoriseAction('cbsubs.refunds')) { $this->setError(CBPTXT::T("Not authorized")); return false; } // 1) check: if (!in_array($this->payment_status, array('Completed', 'Pending', 'Partially-Refunded'))) { $this->setError(CBPTXT::T("This payment is not completed, pending or partially refunded.")); return false; } if ($this->txn_id == '') { $this->txn_id = 'None'; // needed for updatePayment to generate payment record. } $payment = new cbpaidPayment(); if (!$payment->load((int) $this->id)) { $this->setError(CBPTXT::T("This payment does not exist.")); return false; } $paymentBasket = new cbpaidPaymentBasket(); if (!$paymentBasket->load($this->payment_basket_id)) { $this->setError(CBPTXT::T("This payment has no associated payment basket and cannot be refunded from here. Maybe from your PSP online terminal ?")); return false; } if (!$this->gateway_account) { $this->setError(CBPTXT::T("This payment has no gateway associated so can not be refunded.")); return false; } $payAccount = cbpaidControllerPaychoices::getInstance()->getPayAccount($this->gateway_account); if (!$payAccount) { $this->setError(CBPTXT::T("This payment's payment basket's associated gateway account is not active, so can not be refunded from here.")); return false; } $payClass = $payAccount->getPayMean(); $returnText = null; $amount = sprintf('%.2f', (double) $this->refund_gross); if (is_callable(array($payClass, 'refundPayment'))) { $success = $payClass->refundPayment($paymentBasket, $payment, null, $this->refund_is_last, $amount, $this->refund_reason, $returnText); } else { $success = false; } $user = CBuser::getUserDataInstance($paymentBasket->user_id); $username = $user ? $user->username : '******'; $replacements = array('[REFUNDAMOUNT]' => $payment->mc_currency . ' ' . $amount, '[PAYMENTID]' => $payment->id, '[PAYMENTAMOUNT]' => $payment->mc_currency . ' ' . $payment->mc_gross, '[BASKETID]' => $paymentBasket->id, '[ORDERID]' => $paymentBasket->sale_id, '[FULLNAME]' => $paymentBasket->first_name . ' ' . $paymentBasket->last_name, '[USERNAME]' => $username, '[USERID]' => $paymentBasket->user_id, '[PAYMENTMETHOD]' => $payClass->getPayName(), '[TXNID]' => $payment->txn_id, '[AUTHID]' => $payment->auth_id, '[ERRORREASON]' => $paymentBasket->reason_code); if ($success) { // Success Message ? // $returnText = CBPTXT::P("Refunded [REFUNDAMOUNT] for payment id [PAYMENTID] of [PAYMENTAMOUNT] for basket id [BASKETID], Order id [ORDERID] of [FULLNAME] (username [USERNAME] - user id [USERID]) using [PAYMENTMETHOD] with txn_id [TXNID] and auth_id [AUTHID].", $replacements ); } else { $this->setError(CBPTXT::T($payClass->getErrorMSG()) . '. ' . CBPTXT::P("Refund request of [REFUNDAMOUNT] for payment id [PAYMENTID] of [PAYMENTAMOUNT] for basket id [BASKETID], Order id [ORDERID] of [FULLNAME] (username [USERNAME] - user id [USERID]) using [PAYMENTMETHOD] with txn_id [TXNID] and auth_id [AUTHID] failed for reason: [ERRORREASON].", $replacements)); return false; } return true; }
/** * Handles the gateway-specific result of payments (redirects back to this site and gateway notifications). WARNING: unchecked access ! * * @param cbpaidPaymentBasket $paymentBasket New empty object. returning: includes the id of the payment basket of this callback (strictly verified, otherwise untouched) * @param array $postdata _POST data for saving edited tab content as generated with getEditTab * @param string $result result= get parameter, other than 'notify', 'success' or 'cancel'. * @return string HTML to display if frontend, text to return to gateway if notification, FALSE if registration cancelled and ErrorMSG generated, or NULL if nothing to display */ protected function handleOtherResult( $paymentBasket, $postdata, $result ) { global $_CB_framework; $privateVarsList = 'id user_id time_initiated time_completed ip_addresses mc_gross mc_currency ' . 'quantity item_number item_name shared_secret payment_status ' . 'invoice period1 period2 period3 mc_amount1 mc_amount2 mc_amount3'; $ret = null; // $privateVarsList = 'id payment_method gateway_account user_id time_initiated time_completed ip_addresses mc_gross mc_currency quantity item_number item_name shared_secret payment_status'; if ( $result == 'freetrial' ) { $paymentBasketId = (int) $this->_getReqParam( 'basket' ); if ( $paymentBasketId ) { $exists = $paymentBasket->load( (int) $paymentBasketId ); if ( $exists && ( $this->_getReqParam( 'cbpid' ) == $paymentBasket->shared_secret ) && ( $paymentBasket->payment_status == 'NotInitiated' ) ) { $isAnyAutoRecurring = $paymentBasket->isAnyAutoRecurring(); if ( ( $isAnyAutoRecurring == 2 && ( ( $paymentBasket->period1 ) && ( $paymentBasket->mc_amount1 == 0 ) ) ) || ( ( $paymentBasket->mc_amount1 == 0 ) && ( $paymentBasket->mc_amount3 == 0 ) && ( $paymentBasket->mc_gross == 0 ) ) ) { // user-choice: no need to wait for payment basket completed to activate subscriptions: $paymentBasket->payment_method = $this->getPayName(); // $paymentBasket->gateway_account = $this->getAccountParam( 'id' ); // $ipn = null; $ipn = $this->_logNotification( 'P', $_CB_framework->now(), $paymentBasket ); $paymentBasket->bindObjectToThisObject( $ipn, $privateVarsList ); $this->updatePaymentStatus( $paymentBasket, 'web_accept', 'FreeTrial', $ipn, 1, 0, 0, true ); } } } } return $ret; }
/** * WARNING: UNCHECKED ACCESS! On purpose unchecked access for M2M operations * Generates the HTML to display for a specific component-like page for the tab. WARNING: unchecked access ! * @param TabTable|null $tab the tab database entry * @param UserTable $user the user being displayed * @param int $ui 1 for front-end, 2 for back-end * @param array $postdata _POST data for saving edited tab content as generated with getEditTab * @return mixed either string HTML for tab content, or false if ErrorMSG generated */ public function getTabComponent( /** @noinspection PhpUnusedParameterInspection */ $tab, $user, $ui, $postdata ) { global $_CB_database, $_CB_framework, $_POST; $return = ''; $paid = false; $oldignoreuserabort = ignore_user_abort(true); $allowHumanHtmlOutput = true; // this will be reverted in case of M2M server-to-server notifications $act = $this->base->_getReqParam( 'act' ); $actPosted = isset($_POST[$this->base->_getPagingParamName('act')]); if ( $act === null ) { $act = $this->base->input( 'act', null, GetterInterface::COMMAND ); $actPosted = $this->base->input( 'post/act', null, GetterInterface::COMMAND ) !== null; } $post_user_id = (int) cbGetParam( $_GET, 'user', 0 ); if ( $actPosted && ( $post_user_id > 0 ) ) { $access = false; $myId = $_CB_framework->myId(); if ( is_object( $user ) ) { if ( $myId == 0 ) { if ( in_array( $act, array( 'saveeditinvoiceaddress', 'saveeditbasketintegration', 'showbskt' ) ) ) { $access = true; } else { $paidsubsManager =& cbpaidSubscriptionsMgr::getInstance(); if ( ! $paidsubsManager->checkExpireMe( __FUNCTION__, $user->id, false ) ) { // expired subscriptions: we will allow limited access to: if ( in_array( $act, array( 'upgrade', 'pay', 'reactivate', 'resubscribe', 'display_subscriptions' ) ) ) { $access = true; } } } } else { if ( ( $ui == 1 && ( $user->id == $myId ) ) || ( cbpaidApp::authoriseAction( 'cbsubs.usersubscriptionmanage' ) ) ) { $access = true; } } } else { $return = CBPTXT::T("User does not exist") . '.'; } if ( ! $access ) { $return .= '<br />' . CBPTXT::T("Not authorized action") . '.'; return $return; } cbSpoofCheck( 'plugin' ); // anti-spoofing check // renew or upgrade subscription payment form: $params = $this->params; $now = $_CB_framework->now(); $subscriptionsGUI = new cbpaidControllerUI(); $subscriptionIds = $subscriptionsGUI->getEditPostedBoxes( 'id' ); if ( $subscriptionIds == array( 0 ) ) { $subscriptionIds = array(); } if ( $post_user_id && ( $user->id == $post_user_id ) ) { outputCbTemplate(); $this->base->outputRegTemplate(); outputCbJs(); switch ( $act ) { case 'upgrade': // upgrade an existing subscription // display basket and payment buttons or redirect for payment depending if multiple payment choices or intro text present: $chosenPlans = $subscriptionsGUI->getAndCheckChosenUpgradePlans( $postdata, $user, $now ); if ( ( ! is_array( $chosenPlans ) ) || ( count( $chosenPlans ) == 0 ) ) { $subTxt = CBPTXT::T( $params->get( 'subscription_name', 'subscription' ) ); $return .= ( is_string( $chosenPlans ) ? $chosenPlans . '<br />' : '' ) . sprintf( CBPTXT::Th("Please press back button and select the %s plan to which you would like to upgrade."), $subTxt ); break; } $introText = CBPTXT::Th( $params->get( 'intro_text_upgrade', null ) ); //TBD: check if already exists (reload protection): $paymentBasket = cbpaidControllerOrder::createSubscriptionsAndPayment( $user, $chosenPlans, $postdata, $subscriptionIds, null, 'R', CBPTXT::T("Upgrade"), 'U' ); if ( is_object( $paymentBasket ) ) { $return = cbpaidControllerOrder::showBasketForPayment( $user, $paymentBasket, $introText ); } else { $return = $paymentBasket; // show messages as nothing to pay. } break; case 'pay': // pay for an unpaid subscription // display basket and payment buttons or redirect for payment depending if multiple payment choices or intro text present: $plan = $this->base->_getReqParam( 'plan' ); if ( ( ! $plan ) || ( ! isset( $subscriptionIds[$plan] ) ) || ( ! $subscriptionIds[$plan] ) ) { $subTxt = CBPTXT::T( $params->get( 'subscription_name', 'subscription' ) ); $return .= sprintf( CBPTXT::Th("Please press back button and select a %s plan."), $subTxt ); break; } $plansMgr =& cbpaidPlansMgr::getInstance(); $chosenPlans = array(); $chosenPlans[(int) $plan] = $plansMgr->loadPlan( (int) $plan ); $introText = CBPTXT::Th( $params->get( 'intro_text', null ) ); $paymentStatus = null; $return = cbpaidControllerOrder::showPaymentForm( $user, $chosenPlans, $introText, $subscriptionIds, $paymentStatus ); break; case 'renew': // renew a still valid subscription case 'reactivate': // reactivate an expired subscription case 'resubscribe': // resubscribe a cancelled subscription // display basket and payment buttons or redirect for payment depending if multiple payment choices or intro text present: $plan = $this->base->_getReqParam( 'plan' ); if ( ( ! $plan ) || ( ! isset( $subscriptionIds[$plan] ) ) || ( ! $subscriptionIds[$plan] ) ) { $subTxt = CBPTXT::T( $params->get( 'subscription_name', 'subscription' ) ); $return .= sprintf( CBPTXT::Th("Please press back button and select a %s plan."), $subTxt ); break; } $plansMgr =& cbpaidPlansMgr::getInstance(); $chosenPlans = array(); $chosenPlans[(int) $plan] = $plansMgr->loadPlan( (int) $plan ); $paidSomethingMgr =& cbpaidSomethingMgr::getInstance(); $subscription = $paidSomethingMgr->loadSomething( $subscriptionIds[$plan][0], $subscriptionIds[$plan][1] ); global $_PLUGINS; $_PLUGINS->loadPluginGroup( 'user', 'cbsubs.' ); $_PLUGINS->loadPluginGroup('user/plug_cbpaidsubscriptions/plugin'); $_PLUGINS->trigger( 'onCPayAfterPlanRenewalSelected', array( &$chosenPlans[(int) $plan], &$subscription, $act ) ); if ( $_PLUGINS->is_errors() ) { $return .= $_PLUGINS->getErrorMSG(); break; } $introText = CBPTXT::Th( $params->get( 'intro_text_renew', null ) ); //TBD: check if already exists (reload protection): $paymentBasket = cbpaidControllerOrder::createSubscriptionsAndPayment( $user, $chosenPlans, $postdata, $subscriptionIds, null, null, CBPTXT::T("Renew"), 'R' ); if ( is_object( $paymentBasket ) ) { $return = cbpaidControllerOrder::showBasketForPayment( $user, $paymentBasket, $introText ); } else { $return = $paymentBasket; // show messages as nothing to pay. } break; case 'unsubscribe': // request to unsubscribe an active subscription // display unsubscribe confirmation form: $plan = $this->base->_getReqParam( 'plan' ); if ( ( ! $plan ) || ( ! isset( $subscriptionIds[$plan] ) ) || ( ! $subscriptionIds[$plan] ) ) { $subTxt = CBPTXT::T( $params->get( 'subscription_name', 'subscription' ) ); $return .= sprintf( CBPTXT::Th("Please press back button and select a %s plan."), $subTxt ); break; } $introText = CBPTXT::Th( $params->get( 'unsubscribe_intro_text' , null ) ); $return = $subscriptionsGUI->showUnsubscribeForm( $user, $introText, (int) $plan, (int) $subscriptionIds[$plan][1] ); break; case 'confirm_unsubscribe': // confirm previous request to unsubscribe an active subscription // unsubscribe confirmed: $plan = $this->base->_getReqParam( 'plan' ); if ( ( ! $plan ) || ( ! isset( $subscriptionIds[$plan] ) ) || ( ! $subscriptionIds[$plan] ) ) { $subTxt = CBPTXT::T( $params->get( 'subscription_name', 'subscription' ) ); $return .= sprintf( CBPTXT::Th("Please press back button and select a %s plan."), $subTxt ); break; } if ( ( $plan ) && ( count( $subscriptionIds ) == 1 ) ) { $unsubscribeConfText = CBPTXT::Th( $params->get( 'unsubscribe_confirmation_text', null ) ); $return = cbpaidControllerOrder::doUnsubscribeConfirm( $user, $unsubscribeConfText, (int) $plan, (int) $subscriptionIds[$plan][1] ); } break; case 'display_subscriptions': // unsubscribe cancelled: display subscriptions: $return = $this->base->displayUserTab( $user ); break; case 'showinvoice': // shows a particular user invoice: if ( $params->get( 'show_invoices', 1 ) ) { $invoiceNo = $this->base->_getReqParam( 'invoice' ); $return = $this->showInvoice( $invoiceNo, $user ); } break; case 'saveeditinvoiceaddress': case 'editinvoiceaddress': // this is the case of reload of invoicing address $invoicingAddressQuery = $params->get( 'invoicing_address_query' ); if ( $invoicingAddressQuery > 0 ) { $basketId = $this->base->_getReqParam( 'basket', 0 ); $hashToCheck = $this->base->_getReqParam( 'bck' ); $paymentBasket = new cbpaidPaymentBasket( $_CB_database ); if ( $basketId && $paymentBasket->load( (int) $basketId ) && ( $paymentBasket->payment_status == 'NotInitiated' ) && ( $hashToCheck == $paymentBasket->checkHashUser( $hashToCheck ) ) ) { if ( ( $act == 'saveeditinvoiceaddress' ) && $this->base->input( 'actbutton', null, GetterInterface::COMMAND ) ) { // IE7-8 will return text instead of value and IE6 will return button all the time http://www.dev-archive.net/articles/forms/multiple-submit-buttons.html $return = $paymentBasket->saveInvoicingAddressForm( $user ); if ( $return === null ) { $paymentBasket->storeInvoicingDefaultAddress(); $introText = CBPTXT::Th( $params->get( 'intro_text', null ) ); $return .= cbpaidControllerOrder::showBasketForPayment( $user, $paymentBasket, $introText ); } } else { // invoice has reloaded itself (e.g. for country change): $return = $paymentBasket->renderInvoicingAddressForm( $user ); } } else { $this->base->_setErrorMSG( CBPTXT::T("No unpaid payment basket found.") ); } } else { $this->base->_setErrorMSG( CBPTXT::T("Not authorized action") ); } break; case 'saverecordpayment': case 'editrecordpayment': // this is the case of reload of the form $basketId = $this->base->_getReqParam( 'basket', 0 ); $hashToCheck = $this->base->_getReqParam( 'bck' ); $paymentBasket = new cbpaidPaymentBasket( $_CB_database ); if ( $basketId && $paymentBasket->load( (int) $basketId ) && ( $paymentBasket->payment_status != 'Completed' ) && ( $hashToCheck == $paymentBasket->checkHashUser( $hashToCheck ) ) ) { if ( $paymentBasket->authoriseAction( 'cbsubs.recordpayments' ) ) { if ( ( $act == 'saverecordpayment' ) && $this->base->input( 'actbutton', null, GetterInterface::COMMAND ) ) { // IE7-8 will return text instead of value and IE6 will return button all the time http://www.dev-archive.net/articles/forms/multiple-submit-buttons.html $return = cbpaidRecordBasketPayment::saveRecordPayment( $paymentBasket->id ); if ( $return === null ) { $return .= CBPTXT::T("Payment recorded.") . ' <a href="' . $_CB_framework->userProfileUrl( $paymentBasket->user_id, true ) . '">' . CBPTXT::Th("View user profile") . '</a>'; } } else { // invoice has reloaded itself (e.g. for country change): $return = cbpaidRecordBasketPayment::displayRecordPaymentForm( $paymentBasket->id ); } } else { $this->base->_setErrorMSG( CBPTXT::T("Not authorized action") ); } } else { $this->base->_setErrorMSG( CBPTXT::T("No unpaid payment basket found.") ); } break; default: cbNotAuth(); return ''; break; } } } elseif ( $this->base->_getReqParam( 'account' ) && ( ( (int) cbGetParam( $_GET, 'user', 0 ) ) > 0 ) ) { $account = $this->base->_getReqParam( 'account' ); $post_user_id = (int) cbGetParam( $_GET, 'user', 0 ); $user = CBuser::getUserDataInstance( (int) $post_user_id ); if ( $user->id ) { if ( isset( $_SESSION['cbsubs']['expireduser'] ) && ( $_SESSION['cbsubs']['expireduser'] == $user->id ) ) { // expired subscriptions of membership: show possibilities: $subscriptionsGUI = new cbpaidControllerUI(); outputCbTemplate(); $this->base->outputRegTemplate(); outputCbJs(); switch ( $account ) { case 'expired': $paidsubsManager =& cbpaidSubscriptionsMgr::getInstance(); if ( ! $paidsubsManager->checkExpireMe( __FUNCTION__, $user->id, false ) ) { // no valid membership: $return = $subscriptionsGUI->getShowSubscriptionUpgrades( $user, true ); } break; default: break; } } else { $return = CBPTXT::Th("Browser cookies must be enabled."); } } } elseif ( in_array( $act, array( 'setbsktpmtmeth', 'setbsktcurrency' ) ) ) { cbSpoofCheck( 'plugin' ); // anti-spoofing check $params = $this->params; outputCbTemplate(); $this->base->outputRegTemplate(); outputCbJs(); $basketId = $this->base->_getReqParam( 'bskt', 0 ); $hashToCheck = $this->base->_getReqParam( 'bck' ); $paymentBasket = new cbpaidPaymentBasket( $_CB_database ); if ( $basketId && $paymentBasket->load( (int) $basketId ) && ( $paymentBasket->payment_status == 'NotInitiated' ) && ( $hashToCheck == $paymentBasket->checkHashUser( $hashToCheck ) ) ) { switch ( $act ) { case 'setbsktpmtmeth': if ( $params->get( 'payment_method_selection_type' ) == 'radios' ) { $chosenPaymentMethod = cbGetParam( $_POST, 'payment_method' ); $introText = CBPTXT::Th( $params->get( 'intro_text', null ) ); $return = $paymentBasket->saveBasketPaymentMethodForm( $user, $introText, $chosenPaymentMethod ); if ( $return === null ) { $return .= cbpaidControllerOrder::showBasketForPayment( $user, $paymentBasket, $introText ); } } else { $this->base->_setErrorMSG( CBPTXT::T("Not authorized action") ); } break; case 'setbsktcurrency': if ( $params->get( 'allow_select_currency', '0' ) ) { $newCurrency = cbGetParam( $_POST, 'currency' ); if ( $newCurrency ) { if ( in_array( $newCurrency, cbpaidControllerPaychoices::getInstance()->getAllCurrencies() ) ) { $paymentBasket->changeCurrency( $newCurrency ); } else { $this->base->_setErrorMSG( CBPTXT::T("This currency is not allowed") ); } $introText = CBPTXT::Th( $params->get( 'intro_text', null ) ); $return .= cbpaidControllerOrder::showBasketForPayment( $user, $paymentBasket, $introText ); } else { $this->base->_setErrorMSG( CBPTXT::T("Not authorized action") ); } } else { $this->base->_setErrorMSG( CBPTXT::T("Changes of currency of orders are not authorized") ); } break; default: cbNotAuth(); return ''; break; } } else { $this->base->_setErrorMSG( CBPTXT::T("No unpaid payment basket found.") ); } } elseif ( $act == 'cbsubsclass' ) { $pluginName = $this->base->_getReqParam( 'class' ); if ( preg_match( '/^[a-z]+$/', $pluginName ) ) { $element = 'cbsubs.' . $pluginName; global $_PLUGINS; $_PLUGINS->loadPluginGroup('user/plug_cbpaidsubscriptions/plugin', $element ); $loadedPlugins =& $_PLUGINS->getLoadedPluginGroup( 'user/plug_cbpaidsubscriptions/plugin' ); $params = $this->params; foreach ($loadedPlugins as $p ) { if ( $p->element == $element ) { $pluginId = $p->id; $args = array( &$user, &$params, &$postdata ); /** @noinspection PhpUndefinedCallbackInspection */ $return = $_PLUGINS->call( $pluginId, 'executeTask', 'getcbsubs' . $pluginName . 'Tab', $args, null ); break; } } } } elseif ( $act && ( ! in_array( $act, array( 'showbskt', 'setbsktpmtmeth' ) ) ) && ( ( (int) cbGetParam( $_GET, 'user', 0 ) ) > 0 ) ) { if ( ! is_object( $user ) ) { return CBPTXT::T("User does not exist."); } $params = $this->params; $post_user_id = (int) cbGetParam( $_GET, 'user', 0 ); if ( $post_user_id && ( ( $user->id == $post_user_id ) || ( cbpaidApp::authoriseAction( 'cbsubs.usersubscriptionmanage' ) ) ) ) { outputCbTemplate(); $this->base->outputRegTemplate(); outputCbJs(); switch ( $act ) { case 'showinvoice': if ( $params->get( 'show_invoices', 1 ) ) { $invoiceNo = $this->base->_getReqParam( 'invoice', 0 ); // This also checks for cbpaidApp::authoriseAction on cbsubs.sales or cbsubs.financial access permissions: $return = $this->showInvoice( $invoiceNo, $user ); } else { $this->base->_setErrorMSG( CBPTXT::T("Not authorized action") ); } break; case 'showinvoiceslist': $showInvoices = $params->get( 'show_invoices', 1 ); $invoicesShowPeriod = $params->get( 'invoices_show_period', '0000-06-00 00:00:00' ); $itsmyself = ( $_CB_framework->myId() == $user->id ); if ( $showInvoices && ( $itsmyself || ( cbpaidApp::authoriseAction( 'cbsubs.sales' ) || cbpaidApp::authoriseAction( 'cbsubs.financial' ) ) ) ) { $subscriptionsGUI = new cbpaidControllerUI(); $invoices = $this->_getInvoices( $user, $invoicesShowPeriod, false ); if ( $invoicesShowPeriod && ( $invoicesShowPeriod != '0000-00-00 00:00:00' ) ) { $cbpaidTimes =& cbpaidTimes::getInstance(); $periodText = $cbpaidTimes->renderPeriod( $invoicesShowPeriod, 1, false ); } else { $periodText = ''; } $return .= $subscriptionsGUI->showInvoicesList( $invoices, $user, $itsmyself, $periodText ); } else { $this->base->_setErrorMSG( CBPTXT::T("Not authorized action") ); } break; case 'editinvoiceaddress': // this is the case of the initial edit address link if ( $params->get( 'invoicing_address_query' ) > 0 ) { $basketId = $this->base->_getReqParam( 'basket', 0 ); $hashToCheck = $this->base->_getReqParam( 'bck' ); $paymentBasket = new cbpaidPaymentBasket( $_CB_database ); if ( $basketId && $paymentBasket->load( (int) $basketId ) && ( $paymentBasket->payment_status == 'NotInitiated' ) && ( $hashToCheck == $paymentBasket->checkHashUser( $hashToCheck ) ) ) { $return = $paymentBasket->renderInvoicingAddressForm( $user ); } else { $this->base->_setErrorMSG( CBPTXT::T("No unpaid payment basket found.") ); } } else { $this->base->_setErrorMSG( CBPTXT::T("Not authorized action") ); } break; case 'showrecordpayment': $paymentBasketId = $this->base->_getReqParam( 'recordpayment', 0 ); if ( $paymentBasketId ) { $paymentBasket = new cbpaidPaymentBasket(); if ( $paymentBasket->load( (int) $paymentBasketId ) && $paymentBasket->authoriseAction( 'cbsubs.recordpayments' ) ) { // Auto-loads class: and authorization is checked inside: $return = cbpaidRecordBasketPayment::displayRecordPaymentForm( $paymentBasketId ); } else { $this->base->_setErrorMSG( CBPTXT::T("Not authorized action") ); } } else { $this->base->_setErrorMSG( CBPTXT::T("Not authorized action") ); } break; default: $this->base->_setErrorMSG( CBPTXT::T("Not authorized action") ); break; } } } elseif ( $act == 'showbskt' && ( ( ( (int) cbGetParam( $_GET, 'user', 0 ) ) > 0 ) ) || ( $this->base->_getReqParam( 'bskt', 0 ) && $this->base->_getReqParam( 'bck' ) ) ) { $basketId = $this->base->_getReqParam( 'bskt', 0 ); $hashToCheck = $this->base->_getReqParam( 'bck' ); // Basket integrations saving/editing url: if ( in_array($act, array( 'saveeditbasketintegration', 'editbasketintegration' ) ) ) { // edit is the case of edit or reload of integration form $integration = $this->base->_getReqParam( 'integration' ); $paymentBasket = new cbpaidPaymentBasket( $_CB_database ); if ( preg_match( '/^[a-z]+$/', $integration ) && $basketId && $paymentBasket->load( (int) $basketId ) && ( $paymentBasket->payment_status == 'NotInitiated' ) && ( $hashToCheck == $paymentBasket->checkHashUser( $hashToCheck ) ) ) { global $_PLUGINS; $element = 'cbsubs.' . $integration; $_PLUGINS->loadPluginGroup('user/plug_cbpaidsubscriptions/plugin', $element ); $results = $_PLUGINS->trigger( 'onCPayEditBasketIntegration', array( $integration, $act, &$paymentBasket ) ); $return = null; foreach ( $results as $r ) { if ( $r ) { $return .= $r; } } if ( $act == 'editbasketintegration' ) { if ( $return !== null ) { return $return; } } } else { $this->base->_setErrorMSG( CBPTXT::T("No unpaid payment basket found.") ); } } $post_user_id = (int) cbGetParam( $_GET, 'user', 0 ); if ( $post_user_id && ! ( ( is_object( $user ) && ( $user->id == $post_user_id ) ) ) ) { return CBPTXT::T("User does not exist."); } outputCbTemplate(); $this->base->outputRegTemplate(); outputCbJs(); $params = $this->params; $paymentBasket = new cbpaidPaymentBasket( $_CB_database ); if ( $basketId && $paymentBasket->load( (int) $basketId ) && ( $paymentBasket->payment_status == 'NotInitiated' ) ) { if ( ! $post_user_id ) { $cbUser =& CBuser::getInstance( (int) $paymentBasket->user_id ); $user =& $cbUser->getUserData(); if ( ( ! is_object( $user ) ) || ! $user->id ) { return CBPTXT::T("User does not exist."); } } if ( ( $hashToCheck && $hashToCheck == $paymentBasket->checkHashUser( $hashToCheck ) ) || ( ( ! $hashToCheck ) && $paymentBasket->user_id && ( $paymentBasket->user_id == $_CB_framework->myId() ) ) ) { $introText = CBPTXT::Th( $params->get( 'intro_text', null ) ); $return .= cbpaidControllerOrder::showBasketForPayment( $user, $paymentBasket, $introText ); } else { $this->base->_setErrorMSG( CBPTXT::T("Not authorized action") ); } } else { $this->base->_setErrorMSG( CBPTXT::T("No unpaid payment basket found.") ); } // } elseif ( isset($_REQUEST['result']) && isset( $_REQUEST['user'] ) && ( $_REQUEST['user'] > 0 ) ) { } elseif ( isset($_REQUEST['result']) && ( $this->base->_getReqParam('method') || $this->base->_getReqParam('gacctno') ) ) { // don't check license here so initiated payments can complete ! $params = $this->params; $method = $this->base->_getReqParam('method'); if ( ( $method == 'freetrial' ) || ( $method == 'cancelpay' ) ) { cbpaidApp::import( 'processors.freetrial.freetrial' ); cbpaidApp::import( 'processors.cancelpay.cancelpay' ); $className = 'cbpaidGatewayAccount' . $method; $payAccount = new $className( $_CB_database ); } else { $gateAccount = $this->base->_getReqParam('gacctno'); $payAccount = cbpaidControllerPaychoices::getInstance()->getPayAccount( $gateAccount ); if ( ! $payAccount ) { return ''; } } $payClass = $payAccount->getPayMean(); $paymentBasket = new cbpaidPaymentBasket($_CB_database); if ( $payClass && ( ( $this->base->_getReqParam('method') == $payClass->getPayName() ) || ( $this->base->_getReqParam('method') == null ) ) && $payClass->hashPdtBackCheck( $this->base->_getReqParam('pdtback') ) ) { // output for resultNotification: $return and $allowHumanHtmlOutput $return = $payClass->resultNotification( $paymentBasket, $postdata, $allowHumanHtmlOutput ); } if ( ! $paymentBasket->id ) { $this->base->_setErrorMSG(CBPTXT::T("No suitable basket found.")); } else { $user =& CBuser::getUserDataInstance( (int) $paymentBasket->user_id ); if ( $paymentBasket->payment_status == 'RegistrationCancelled' ) { // registration cancelled: delete payment basket and delete user after checking that he is not yet active: if ( $paymentBasket->load( (int) $paymentBasket->id ) ) { if ( $payClass->hashPdtBackCheck( $this->base->_getReqParam('pdtback') ) && ( ( $paymentBasket->payment_status == 'NotInitiated' ) || ( ( $paymentBasket->payment_status === 'Pending' ) && ( $paymentBasket->payment_method === 'offline' ) ) ) ) { $notification = new cbpaidPaymentNotification(); $notification->initNotification( $payClass, 0, 'P', $paymentBasket->payment_status, $paymentBasket->payment_type, null, $_CB_framework->now(), $paymentBasket->charset ); $payClass->updatePaymentStatus( $paymentBasket, 'web_accept', 'RegistrationCancelled', $notification, 0, 0, 0, true ); // This is a notification or a return to site after payment, we want to log any error happening in third-party stuff in case: cbpaidErrorHandler::keepTurnedOn(); } } } if ( $allowHumanHtmlOutput ) { // If frontend, we display result, otherwise, If Server-to-server notification: do not display any additional text here ! switch ( $paymentBasket->payment_status ) { case 'Completed': // PayPal recommends including the following information with the confirmation: // - Item name // - Amount paid // - Payer email // - Shipping address $newMsg = sprintf( CBPTXT::Th("Thank you for your payment of %s for the %s %s."), $paymentBasket->renderPrice(), $paymentBasket->item_name, htmlspecialchars( $payClass->getTxtUsingAccount( $paymentBasket ) ) ) // ' using your paypal account ' . $paymentBasket->payer_email . ' ' . $payClass->getTxtNextStep( $paymentBasket ); // . "Your transaction has been completed, and a receipt for your purchase has been emailed to you by PayPal. " // . "You may log into your account at www.paypal.com to view details of this transaction.</p>\n"; if ( $params->get( 'show_invoices' ) ) { $itsmyself = ( $_CB_framework->myId() == $user->id ); $subscriptionsGUI = new cbpaidControllerUI(); $newMsg .= '<p id="cbregviewinvoicelink">' . $subscriptionsGUI->getInvoiceShowAhtml( $paymentBasket, $user, $itsmyself, CBPTXT::Th("View printable invoice") ) . '</p>' ; } $paid = true; break; case 'Pending': $newMsg = sprintf( CBPTXT::Th("Thank you for initiating the payment of %s for the %s %s."), $paymentBasket->renderPrice(), $paymentBasket->item_name, htmlspecialchars( $payClass->getTxtUsingAccount( $paymentBasket ) ) ) // ' using your paypal account ' . $paymentBasket->payer_email . ' ' . $payClass->getTxtNextStep( $paymentBasket ); // . "Your payment is currently being processed. " // . "A receipt for your purchase will be emailed to you by PayPal once processing is complete. " // . "You may log into your account at www.paypal.com to view status details of this transaction.</p>\n"; break; case 'RegistrationCancelled': $newMsg = $payClass->getTxtNextStep( $paymentBasket ); break; case 'FreeTrial': $newMsg = CBPTXT::Th("Thank you for subscribing to") . ' ' . $paymentBasket->item_name . '.' . ' ' . $payClass->getTxtNextStep( $paymentBasket ); break; case null: $newMsg = CBPTXT::T("Payment basket does not exist."); break; case 'NotInitiated': $newMsg = ''; break; case 'RedisplayOriginalBasket': if ( $paymentBasket->load( (int) $paymentBasket->id ) && ( $paymentBasket->payment_status == 'NotInitiated' ) ) { $introText = CBPTXT::Th( $params->get( 'intro_text', null ) ); $return .= cbpaidControllerOrder::showBasketForPayment( $user, $paymentBasket, $introText ); } $newMsg = ''; break; case 'Processed': case 'Denied': case 'Reversed': case 'Refunded': case 'Partially-Refunded': default: $newMsg = $payClass->getTxtNextStep( $paymentBasket ); // "<p>Your transaction is not cleared and has currently following status: <strong>" . $paymentBasket->payment_status . ".</strong></p>" // . "<p>You may log into your account at www.paypal.com to view status details of this transaction.</p>"; break; } if ( in_array( $paymentBasket->payment_status, array( 'Completed', 'Pending' ) ) ) { $subscriptions = $paymentBasket->getSubscriptions(); $texts = array(); // avoid repeating several times identical texts: if ( is_array( $subscriptions ) ) { foreach ( $subscriptions as $sub ) { /** @var $sub cbpaidSomething */ $thankYouParam = ( $paymentBasket->payment_status == 'Completed') ? 'thankyoutextcompleted' : 'thankyoutextpending'; $thankYouText = $sub->getPersonalized( $thankYouParam, true ); if ( $thankYouText && ! in_array( $thankYouText, $texts ) ) { $texts[] = $thankYouText; if ( strpos( $thankYouText, '<' ) === false ) { $msgTag = 'p'; } else { $msgTag = 'div'; } $newMsg .= '<' . $msgTag . ' class="cbregThanks" id="cbregThanks' . $sub->plan_id . '">' . $thankYouText . '</' . $msgTag . ">\n"; } } } } if ( $newMsg ) { $return .= '<div>' . $newMsg . '</div>'; } if ( $paid && ( $_CB_framework->myId() < 1 ) && ( cbGetParam( $_REQUEST, 'user', 0 ) == $paymentBasket->user_id ) ) { $_CB_database->setQuery( "SELECT * FROM #__comprofiler c, #__users u WHERE c.id=u.id AND c.id=".(int) $paymentBasket->user_id ); if ( $_CB_database->loadObject( $user ) && ( $user->lastvisitDate == '0000-00-00 00:00:00' ) ) { $return = '<p>' . implode( '', getActivationMessage( $user, 'UserRegistration' ) ) . '</p>' . $return; } } } } } else { cbNotAuth(); return ' ' . CBPTXT::T("No result."); } if ( $allowHumanHtmlOutput ) { $allErrorMsgs = $this->base->getErrorMSG( '</div><div class="error">' ); if ( $allErrorMsgs ) { $errorMsg = '<div class="error">' . $allErrorMsgs . '</div>'; } else { $errorMsg = null; } /** @var string $return */ if ( ( $return == '' ) && ( $errorMsg ) ) { $this->base->outputRegTemplate(); $return = $errorMsg . '<br /><br />' . $return; $return .= cbpaidControllerOrder::showBasketForPayment( $user, $paymentBasket, '' ); } else { $return = $errorMsg . $return; } } if ( ! is_null( $oldignoreuserabort ) ) { ignore_user_abort($oldignoreuserabort); } return $return; }
/** * The user got redirected back from the payment service provider with a success message: let's see how successfull it was * * @param cbpaidPaymentBasket $paymentBasket New empty object. returning: includes the id of the payment basket of this callback (strictly verified, otherwise untouched) * @param array $requestdata Data returned by gateway * @param string $type Type of return ('R' for PDT, 'I' for INS, 'A' for Autorecurring payment (Vault) ) * @param array $additionalLogData Additional strings to log with IPN * @return string HTML to display if frontend, text to return to gateway if notification, FALSE if registration cancelled and ErrorMSG generated, or NULL if nothing to display */ private function _returnParamsHandler( $paymentBasket, $requestdata, $type, $additionalLogData = null ) { global $_CB_framework, $_GET, $_POST; $ret = null; $paymentBasketId = (int) cbGetParam( $requestdata, 'USER1', null ); if ( $paymentBasketId ) { $exists = $paymentBasket->load( (int) $paymentBasketId ); if ( $exists && ( ( cbGetParam( $requestdata, $this->_getPagingParamName( 'id' ), 0 ) == $paymentBasket->shared_secret ) && ( ! ( ( ( $type == 'R' ) || ( $type == 'I' ) ) && ( $paymentBasket->payment_status == 'Completed' ) ) ) ) ) { // Log the return record: $log_type = $type; $reason = null; $paymentStatus = $this->_mapPaymentStatus( $requestdata, $reason ); $paymentType = $this->_getPaymentType( $requestdata ); $paymentTime = $_CB_framework->now(); if ( $paymentStatus == 'Error' ) { $errorTypes = array( 'I' => 'D', 'R' => 'E', '3' => 'V', '4' => 'V' ); if ( isset( $errorTypes[$type] ) ) { $log_type = $errorTypes[$type]; } } $ipn =& $this->_prepareIpn( $log_type, $paymentStatus, $paymentType, $reason, $paymentTime, 'utf-8' ); if ( $paymentStatus == 'Refunded' ) { // in case of refund we need to log the payment as it has same TnxId as first payment: so we need payment_date for discrimination: $ipn->payment_date = date( 'H:i:s M d, Y T', $paymentTime ); // paypal-style } $ipn->test_ipn = ( $this->getAccountParam( 'normal_gateway' ) == '0' ? 1 : 0 ); $ipn->raw_data = '$message_type="' . ( $type == 'R' ? 'RETURN_TO_SITE' : ( $type == 'I' ? 'NOTIFICATION' : ( $type == '3' ? 'REFUND' : ( $type == '4' ? 'PARTIAL_REFUND' : 'UNKNOWN' ) ) ) ) . '";' . "\n"; if ( $additionalLogData ) { foreach ( $additionalLogData as $k => $v ) { $ipn->raw_data .= '$' . $k . '="' . var_export( $v, true ) . '";' . "\n"; } } $ipn->raw_data .= /* cbGetParam() not needed: we want raw info */ '$requestdata=' . var_export( $requestdata, true ) . ";\n" . /* cbGetParam() not needed: we want raw info */ '$_GET=' . var_export( $_GET, true ) . ";\n" . /* cbGetParam() not needed: we want raw info */ '$_POST=' . var_export( $_POST, true ) . ";\n"; if ( $paymentStatus == 'Error' ) { $paymentBasket->reason_code = $reason; $this->_storeIpnResult( $ipn, 'ERROR:' . $reason ); $this->_setLogErrorMSG( 4, $ipn, $this->getPayName() . ': ' . $reason, CBPTXT::T( 'Sorry, the payment server replied with an error.' ) . ' ' . CBPTXT::T( 'Please contact site administrator to check payment status and error log.' ) ); $ret = false; } else { $ipn->bindBasket( $paymentBasket ); $ipn->sale_id = $paymentBasketId; $insToIpn = array( 'sale_id' => 'PPREF', 'txn_id' => 'PNREF', 'first_name' => 'FIRSTNAME', 'last_name' => 'LASTNAME', 'address_street' => 'ADDRESS', 'address_zip' => 'ZIP', 'address_city' => 'CITY', 'address_country' => 'COUNTRY', 'address_state' => 'STATE', 'payer_email' => 'EMAIL', 'contact_phone' => 'PHONE', 'payer_id' => 'PAYERID', 'auth_id' => 'CORRELATIONID', 'tax' => 'TAXAMT', 'mc_fee' => 'FEEAMT', 'mc_gross' => 'AMT' ); foreach ( $insToIpn as $k => $v ) { $ipn->$k = cbGetParam( $requestdata, $v ); } $ipn->mc_currency = $paymentBasket->mc_currency; $ipn->user_id = (int) $paymentBasket->user_id; $recurring = ( ( cbGetParam( $requestdata, 'USER4' ) == 'R' ) && ( cbGetParam( $requestdata, 'TENDER' ) != 'P' ) ? true : false ); if ( $recurring ) { if ( ( $paymentStatus == 'Completed' ) && ( ! $paymentBasket->subscr_id ) ) { if ( $this->hasPaypalPayflow() ) { list( /*$p3 */, $t3, $start ) = $this->_paypalPeriodsLimits( explode( ' ', $paymentBasket->period3 ), $paymentTime ); if ( $paymentBasket->period1 ) { list( /*$p2*/, /*$t2*/, $start ) = $this->_paypalPeriodsLimits( explode( ' ', $paymentBasket->period1 ), $paymentTime ); } $request = array( 'PARTNER' => 'PayPal', 'VENDOR' => $this->getAccountParam( 'paypal_payflow_vendor' ), 'USER' => $this->getAccountParam( 'paypal_payflow_user' ), 'PWD' => $this->getAccountParam( 'paypal_payflow_password' ), 'TENDER' => cbGetParam( $requestdata, 'TENDER' ), 'TRXTYPE' => 'R', 'ACTION' => 'A', 'PROFILENAME' => $paymentBasket->item_name, 'AMT' => sprintf( '%.2f', $paymentBasket->mc_amount3 ), 'START' => date( 'mdY', $start ), 'TERM' => ( $paymentBasket->recur_times ? $paymentBasket->recur_times : 0 ), 'PAYPERIOD' => $t3, 'ORIGID' => $ipn->txn_id ); $formUrl = array(); foreach ( $request as $k => $v ) { $formUrl[$k] = $k . '=' . $v; } $formUrl = implode( '&', $formUrl ); $results = array(); $response = null; $status = null; $error = $this->_httpsRequest( $this->gatewayUrl( 'psp' ), $formUrl, 105, $response, $status, 'post', 'normal' ); if ( $response ) { parse_str( $response, $results ); } if ( $error || ( $status != 200 ) || ( ! $response ) ) { $ipn->txn_type = 'web_accept'; $this->_setLogErrorMSG( 3, $ipn, $this->getPayName() . ' HTTPS POST request to payment gateway server failed.', CBPTXT::T( "Submitted refund request didn't return an error but didn't complete." ) . ' ' . CBPTXT::T( 'Please contact site administrator to check error log.' ) ); } else { if ( cbGetParam( $results, 'RESULT' ) == 0 ) { $ipn->txn_type = 'subscr_signup'; $ipn->subscr_id = cbGetParam( $results, 'PROFILEID' ); $ipn->subscr_date = $ipn->payment_date; } else { $ipn->txn_type = 'web_accept'; $this->_setLogErrorMSG( 3, $ipn, $this->getPayName() . ' Paypal Payflow error returned. ERROR: ' . cbGetParam( $results, 'RESPMSG' ), CBPTXT::T( 'Please contact site administrator to check error log.' ) ); } } } else { $ipn->txn_type = 'web_accept'; $this->_setLogErrorMSG( 3, $ipn, $this->getPayName() . ' Needed Paypal Payflow vendor, user and password not set.', CBPTXT::T( 'Needed Paypal Payflow vendor, user and password not set.' ) . ' ' . CBPTXT::T( 'Please contact site administrator to check error log.' ) ); } } elseif ( $paymentStatus == 'Denied' ) { if ( ( $paymentBasket->reattempts_tried + 1 ) <= cbpaidScheduler::getInstance( $this )->retries ) { $ipn->txn_type = 'subscr_failed'; } else { $ipn->txn_type = 'subscr_cancel'; } } elseif ( in_array( $paymentStatus, array( 'Completed', 'Processed', 'Pending' ) ) ) { $ipn->txn_type = 'subscr_payment'; } } else { $ipn->txn_type = 'web_accept'; } // validate payment from PDT or IPN $valid = $this->_validateIPN( $ipn, $paymentBasket ); if ( $valid === true ) { if ( in_array( $ipn->payment_status, array( 'Completed', 'Processed', 'Pending', 'Refunded', 'Denied' ) ) ) { $this->_storeIpnResult( $ipn, 'SUCCESS' ); $autorecurring_type = ( in_array( $ipn->txn_type, array( 'subscr_payment', 'subscr_signup', 'subscr_modify', 'subscr_eot', 'subscr_cancel', 'subscr_failed' ) ) ? 2 : 0 ); $autorenew_type = ( $autorecurring_type ? ( ( ( $this->getAccountParam( 'enabled', 0 ) == 3 ) && ( $paymentBasket->isAnyAutoRecurring() == 2 ) ) ? 1 : 2 ) : 0 ); if ( $autorecurring_type && ( $ipn->txn_type == 'subscr_signup' ) && ( ( $paymentBasket->period1 ) && ( $paymentBasket->mc_amount1 == 0 ) ) && ( $ipn->payment_status == '' ) ) { $ipn->payment_status = 'Completed'; } if ( ( $ipn->payment_status == 'Refunded' ) && ( $paymentBasket->mc_gross != ( - $ipn->mc_gross ) ) ) { $ipn->payment_status = 'Partially-Refunded'; } $this->_bindIpnToBasket( $ipn, $paymentBasket ); // add the gateway to the basket: $paymentBasket->payment_method = $this->getPayName(); $paymentBasket->gateway_account = $this->getAccountParam( 'id' ); $this->updatePaymentStatus( $paymentBasket, $ipn->txn_type, $ipn->payment_status, $ipn, 1, $autorecurring_type, $autorenew_type, false ); if ( in_array( $ipn->payment_status, array( 'Completed', 'Processed', 'Pending' ) ) ) { $ret = true; } } else { $this->_storeIpnResult( $ipn, 'FAILED' ); $paymentBasket->payment_status = $ipn->payment_status; $this->_setErrorMSG( '<div class="message">' . $this->getTxtNextStep( $paymentBasket ) . '</div>' ); $paymentBasket->payment_status = 'RedisplayOriginalBasket'; $ret = false; } } else { $this->_storeIpnResult( $ipn, 'MISMATCH' ); $this->_setLogErrorMSG( 3, $ipn, $this->getPayName() . ' Paypal IPN fraud attempt. ERROR: ' . $valid, CBPTXT::T( 'Invalid transaction.' ) . ' ' . CBPTXT::T( 'Please contact site administrator to check error log.' ) ); $ret = false; } } } } else { $this->_setLogErrorMSG( 3, null, $this->getPayName() . ': USER1 is missing in the return URL: ' . var_export( $_GET, true ), CBPTXT::T( 'Please contact site administrator to check error log.' ) ); } if ( ( $type == 'R' ) && in_array( $this->getAccountParam( 'template_layout', 'MINLAYOUT' ), array( 'MINLAYOUT', 'MOBILE' ) ) ) { $js = "if ( top != self ) {" . "document.body.style.display = 'none';" . "parent.location = '" . addslashes( ( $ret ? $this->getSuccessUrl( $paymentBasket ) : $this->getCancelUrl( $paymentBasket ) ) ) . "';" . "}"; echo '<script type="text/javascript">' . $js . '</script>'; } return $ret; }
/** * Handles a gateway notification * * @param cbpaidPaymentBasket $paymentBasket * @param array $postdata * @return bool */ protected function handleNotify( $paymentBasket, $postdata ) { global $_CB_framework; $transactionId = cbGetParam( $postdata, 'txn_id', null ); $subscriptionId = cbGetParam( $postdata, 'recurring_payment_id', null ); $return = false; if ( $transactionId || $subscriptionId ) { if ( $this->hasPaypalApi() ) { $paymentStatus = cbGetParam( $postdata, 'payment_status', null ); $paymentType = cbGetParam( $postdata, 'payment_type', null ); $ipn =& $this->_prepareIpn( 'I', $paymentStatus, $paymentType, null, $_CB_framework->now(), 'utf-8' ); if ( $subscriptionId ) { $exists = $paymentBasket->loadThisMatching( array( 'subscr_id' => $subscriptionId ) ); } else { $custom = (int) cbGetParam( $postdata, 'custom', null ); if ( $custom ) { $exists = $paymentBasket->load( $custom ); } else { $exists = $paymentBasket->loadThisMatching( array( 'txn_id' => $transactionId ) ); } } if ( $exists ) { $ipn->bindBasket( $paymentBasket ); $ipn->user_id = (int) $paymentBasket->user_id; } $ipn->bind( $postdata ); if ( $exists ) { $ipn->item_number = $paymentBasket->item_number; } if ( $subscriptionId ) { if ( ! $ipn->payment_status ) { $profileStatus = cbGetParam( $postdata, 'profile_status', null ); if ( $profileStatus == 'Cancelled' ) { $ipn->payment_status = 'Unsubscribed'; $ipn->payment_date = cbGetParam( $postdata, 'time_created', null ); } elseif ( $profileStatus == 'Active' ) { $ipn->payment_status = 'Completed'; $ipn->payment_date = cbGetParam( $postdata, 'time_created', null ); } elseif ( $profileStatus == 'Expired' ) { $ipn->payment_status = 'Unsubscribed'; $ipn->payment_date = cbGetParam( $postdata, 'time_created', null ); } elseif ( $profileStatus == 'Suspended' ) { $ipn->payment_status = 'Denied'; $ipn->payment_date = cbGetParam( $postdata, 'time_created', null ); } elseif ( $profileStatus == 'Pending' ) { $ipn->payment_status = 'Pending'; $ipn->payment_date = cbGetParam( $postdata, 'time_created', null ); } } $requestParams = array( 'METHOD' => 'GetRecurringPaymentsProfileDetails', 'PROFILEID' => $subscriptionId ); } else { $requestParams = array( 'METHOD' => 'GetTransactionDetails', 'TRANSACTIONID' => $transactionId ); } $this->_signRequestParams( $requestParams ); $results = array(); $response = null; $status = null; $error = $this->_httpsRequest( str_replace( 'www', 'api-3t', $this->gatewayUrl( 'psp' ) . '/nvp' ), $requestParams, 105, $response, $status, 'post', 'normal' ); if ( $response ) { parse_str( $response, $results ); } $rawData = '$response="' . preg_replace( '/([^\s]{100})/', '$1 ', $response ) . "\"\n" . '$results=' . var_export( $results, true ) . ";\n" . '$_GET=' . var_export( $_GET, true ) . ";\n" . '$_POST=' . var_export( $_POST, true ) . ";\n"; $ipn->setRawData( $rawData ); if ( $error || ( $status != 200 ) || ( ! $response ) ) { $ipn->log_type = 'D'; $ipn->setRawResult( 'COMMUNICATION ERROR' ); $this->_setLogErrorMSG( 3, $ipn, $this->getPayName() . ' HTTPS POST request to payment gateway server failed.', CBPTXT::T( "Submitted transaction details request didn't return an error but didn't complete." ) . ' ' . CBPTXT::T( 'Please contact site administrator to check error log.' ) ); } else { $insToIpn = array( 'address_street' => 'STREET', 'address_city' => 'CITY', 'address_state' => 'STATE', 'address_zip' => 'ZIP', 'address_country' => 'COUNTRY', 'address_country_code' => 'COUNTRYCODE', 'address_status' => 'ADDRESSSTATUS', 'first_name' => 'FIRSTNAME', 'last_name' => 'LASTNAME', 'payer_email' => 'EMAIL', 'payer_id' => 'PAYERID', 'payer_status' => 'PAYERSTATUS', 'auth_id' => 'CORRELATIONID', 'tax' => 'TAXAMT', 'mc_currency' => 'CURRENCYCODE', 'mc_fee' => 'FEEAMT', 'mc_gross' => 'AMT' ); if ( $ipn->payment_status == 'Refunded' ) { unset( $insToIpn['mc_fee'] ); unset( $insToIpn['mc_gross'] ); } foreach ( $insToIpn as $k => $v ) { $apiValue = cbGetParam( $results, $v ); if ( $apiValue && ( ! in_array( $apiValue, array( '0.00', 'None' ) ) ) ) { $ipn->$k = $apiValue; } } switch ( $ipn->txn_type ) { case 'recurring_payment': $ipn->txn_type = 'subscr_payment'; break; case 'recurring_payment_profile_created': $ipn->txn_type = 'subscr_signup'; break; case 'recurring_payment_profile_cancel': $ipn->txn_type = 'subscr_cancel'; break; case 'recurring_payment_expired': $ipn->txn_type = 'subscr_eot'; break; case 'recurring_payment_skipped': $ipn->txn_type = 'subscr_failed'; break; } $valid = $this->_validateIPN( $ipn, $paymentBasket, cbGetParam( $_REQUEST, 'cbpid' ) ); if ( $valid === true ) { if ( cbGetParam( $results, 'ACK' ) == 'Success' ) { if ( $exists ) { $ipn->setRawResult( 'SUCCESS' ); if ( $ipn->txn_type != 'subscr_signup' ) { $autorecurring_type = ( in_array( $ipn->txn_type, array( 'subscr_payment', 'subscr_signup', 'subscr_modify', 'subscr_eot', 'subscr_cancel', 'subscr_failed' ) ) ? 2 : 0 ); $autorenew_type = ( $autorecurring_type ? ( ( ( $this->getAccountParam( 'enabled', 0 ) == 3 ) && ( $paymentBasket->isAnyAutoRecurring() == 2 ) ) ? 1 : 2 ) : 0 ); if ( $autorecurring_type && ( $ipn->txn_type == 'subscr_signup' ) && ( ( $paymentBasket->period1 ) && ( $paymentBasket->mc_amount1 == 0 ) ) && ( $ipn->payment_status == '' ) ) { $ipn->payment_status = 'Completed'; } if ( ( $ipn->payment_status == 'Refunded' ) && ( $paymentBasket->mc_gross != ( - $ipn->mc_gross ) ) ) { $ipn->payment_status = 'Partially-Refunded'; } if ( in_array( $ipn->txn_type, array( 'subscr_eot', 'subscr_cancel', 'subscr_failed' ) ) ) { $autorecurring_type = 0; } $this->updatePaymentStatus( $paymentBasket, $ipn->txn_type, $ipn->payment_status, $ipn, 1, $autorecurring_type, $autorenew_type, false ); $return = true; $ipn->store(); } else { $return = null; } } else { $ipn->log_type = 'J'; $ipn->setRawResult( 'FAILED' ); } } else { $ipn->log_type = 'M'; $ipn->setRawResult( 'FAILED' ); $this->_setLogErrorMSG( 3, $ipn, $this->getPayName() . ' Paypal API error returned. ERROR: ' . cbGetParam( $results, 'L_LONGMESSAGE0' ) . ' CODE: ' . cbGetParam( $results, 'L_ERRORCODE0' ), cbGetParam( $results, 'L_SHORTMESSAGE0' ) . '. ' . CBPTXT::T( 'Please contact site administrator to check error log.' ) ); } } else { $ipn->log_type = 'O'; $ipn->setRawResult( 'MISMATCH' ); $this->_setLogErrorMSG( 3, $ipn, $this->getPayName() . ' Paypal IPN fraud attempt. ERROR: ' . $valid, CBPTXT::T( 'Invalid transaction.' ) . ' ' . CBPTXT::T( 'Please contact site administrator to check error log.' ) ); } } } else { $this->_setLogErrorMSG( 3, null, $this->getPayName() . ' Needed Paypal API username, password and signature not set.' . "\n" . '$_GET=' . var_export( $_GET, true ) . "\n" . '$_POST=' . var_export( $_POST, true ) . "\n", CBPTXT::T( 'Needed Paypal API username, password and signature not set.' ) . ' ' . CBPTXT::T( 'Please contact site administrator to check error log.' ) ); } } else { $this->_setLogErrorMSG( 3, null, $this->getPayName() . ' Needed Paypal Transaction ID (txn_id) missing.' . "\n" . '$_GET=' . var_export( $_GET, true ) . "\n" . '$_POST=' . var_export( $_POST, true ) . "\n", CBPTXT::T( 'Transaction not found.' ) . ' ' . CBPTXT::T( 'Please contact site administrator to check error log.' ) ); } return $return; }
/** * The user cancelled his payment * * @param cbpaidPaymentBasket $paymentBasket New empty object. returning: includes the id of the payment basket of this callback (strictly verified, otherwise untouched) * @param array $postdata _POST data for saving edited tab content as generated with getEditTab * @return null */ private function handleCancel($paymentBasket, $postdata) { global $_GET; // The user cancelled his payment (and registration): if ($this->hashPdtBackCheck($this->_getReqParam('pdtback'))) { $paymentBasketId = (int) $this->_getReqParam('basket'); $exists = $paymentBasket->load((int) $paymentBasketId); if ($exists && $this->_getReqParam('id') == $paymentBasket->shared_secret && $paymentBasket->payment_status != 'Completed') { $paymentBasket->payment_status = 'RegistrationCancelled'; $messageToUser = $this->_getCancelledErrorText($_GET); $this->_setErrorMSG($messageToUser); } } return null; }
/** * Handle Paypal PDT * * @param cbpaidPaymentBasket $paymentBasket New empty object. returning: includes the id of the payment basket of this callback (strictly verified, otherwise untouched) * @param array $postdata _POST data for saving edited tab content as generated with getEditTab * @return string HTML to display if frontend, text to return to gateway if notification, FALSE if registration cancelled and ErrorMSG generated, or NULL if nothing to display */ private function handlePaypalPDT( $paymentBasket, /** @noinspection PhpUnusedParameterInspection */ $postdata ) { global $_CB_framework, $_CB_database, $_GET, $_POST; $ret = null; // The user got redirected back from paypal with a success message: if ( isset( $_GET['tx'] ) && isset( $_GET['st'] ) && isset( $_GET['amt'] ) && isset( $_GET['cc'] ) ) { /// P D T : Process Payment Data Transaction (PDT): // check if PDT not already processed: $pbTmp = new cbpaidPaymentBasket( $_CB_database ); $paymentBasketId = (int) $this->_getReqParam('basket'); if ( $paymentBasketId && $pbTmp->load( (int) $paymentBasketId ) && ( $pbTmp->payment_status == cbGetParam( $_GET, 'st' ) ) && ( $pbTmp->txn_id == cbGetParam( $_GET, 'tx' ) ) && ( $pbTmp->shared_secret ==cbGetParam( $_GET, 'cbpid' ) ) ) { // this PDT has already been treated...probably a Nth reload or bookmarked page: $paymentBasket->load( (int) $pbTmp->id ); } else { $ipn = new cbpaidPaymentNotification($_CB_database); $ipn->payment_method = $this->getPayName(); $ipn->gateway_account = $this->getAccountParam( 'id' ); // done below: $ipn->log_type = 'R'; $ipn->time_received = date( 'Y-m-d H:i:s', $_CB_framework->now() ); $ipn->raw_data = /* cbGetParam() not needed: we want raw info */ '$_GET=' . var_export( $_GET, true ) . ";\n"; $ipn->raw_data .= /* cbGetParam() not needed: we want raw info */ '$_POST=' . var_export( $_POST, true ) . ";\n"; $ipn->ip_addresses = cbpaidRequest::getIPlist(); $ipn->user_id = $pbTmp->user_id; // post back to PayPal system to validate: $formvars = array( 'cmd' => '_notify-synch', 'tx' => cbGetParam( $_REQUEST, 'tx', '' ), 'at' => trim($this->getAccountParam('paypal_identity_token')) ); $results = null; $status = null; $error = $this->_httpsRequest( $this->_paypalUrl() . '/cgi-bin/webscr', $formvars, 30, $results, $status, 'post', 'normal', '*/*', true, 443, '', '', true, null ); $transaction_info = urldecode($results); //FIXME: urldecode is done below already! if ( $error || ( $status != 200 ) ) { $ipn->raw_result = 'COMMUNICATION ERROR'; // $ipn->raw_data = 'Error: '. $error . ' Status: ' . $status . ' Transaction info: ' . $transaction_info; $ipn->raw_data .= '$error=\''. $error . "';\n"; $ipn->raw_data .= '$status=\'' . $status . "';\n"; $ipn->raw_data .= '$formvars=' . var_export( $formvars, true ) . ";\n"; $ipn->raw_data .= '$transaction_info=\'' . $transaction_info . "';\n"; $ipn->log_type = 'E'; $ipn->time_received = date( 'Y-m-d H:i:s', $_CB_framework->now() ); $_CB_database->insertObject( $ipn->getTableName(), $ipn, $ipn->getKeyName() ); $this->_setLogErrorMSG( 3, $ipn, 'Paypal: Error at notification received: could not reach Paypal gateway for notification check at ' . $this->_paypalUrl() . '. ' . $ipn->raw_result, null ); $this->_setErrorMSG( sprintf( CBPTXT::T("Sorry no response for your payment from payment server (error %s). Please check your email and status later."), $error ) ); $ret = false; } else { // echo $transaction_info; $input = explode("\n", $transaction_info); foreach ($input as $k => $in) { $input[$k] = trim( $in, "\n\r" ); } $resultMessage = array_shift( $input ); $output = array(); foreach ($input as $in) { $posEqualSign = strpos($in, '='); if ($posEqualSign === false) { $output[] = $in; } else { $output[substr($in,0,$posEqualSign)] = substr($in, $posEqualSign+1); } } if ( isset( $output['charset'] ) && ( $resultMessage == 'SUCCESS' ) ) { if ( strtolower( $output['charset'] ) != strtolower( $_CB_framework->outputCharset() ) ) { foreach ($output as $k => $v ) { $output[$k] = $this->_charsetConv( $v, $output['charset'], $_CB_framework->outputCharset() ); } $output['charset'] = $_CB_framework->outputCharset(); } } $ipn->bind( $output ); $ipn->raw_result = $resultMessage; $ipn->raw_data .= '$transaction_info=\'' . $transaction_info . "';\n"; $ipn->raw_data .= '$PDT_RESULT=' . var_export( $output, true ) . ";\n"; $ipn->payment_basket_id = (int) $ipn->custom; /* if(!$_CB_database->updateObject( $ipn->_tbl, $ipn, $ipn->_tbl_key, false)) { echo 'update error:'.htmlspecialchars($_CB_database->stderr(true))."\n"; exit(); } */ if ( $resultMessage == 'SUCCESS' ) { $ipn->log_type = 'R'; $_CB_database->insertObject( $ipn->getTableName(), $ipn, $ipn->getKeyName() ); $paymentBasketId = (int) $ipn->custom; $exists = $paymentBasket->load( (int) $paymentBasketId ); if ( $exists ) { $this->_fixPayPalIpnBugs( $ipn, $paymentBasket ); $noFraudCheckResult = $this->_checkNotPayPalFraud( $ipn, $paymentBasket, cbGetParam( $_REQUEST, 'cbpid', '' ) ); if ( $noFraudCheckResult === true ) { $autorecurring_type = ( in_array( $ipn->txn_type, array( 'subscr_payment', 'subscr_signup', 'subscr_modify', 'subscr_eot', 'subscr_cancel', 'subscr_failed' ) ) ? 2 : 0 ); $paypalUserChoicePossible = ( ( $this->getAccountParam( 'enabled', 0 ) == 3 ) && ( $paymentBasket->isAnyAutoRecurring() == 2 ) ); $autorenew_type = ( $autorecurring_type ? ( $paypalUserChoicePossible ? 1 : 2 ) : 0 ); $this->_bindIpnToBasket( $ipn, $paymentBasket ); $paymentBasket->payment_method = $this->getPayName(); $paymentBasket->gateway_account = $this->getAccountParam( 'id' ); $this->updatePaymentStatus( $paymentBasket, $ipn->txn_type, $ipn->payment_status, $ipn, 1, $autorecurring_type, $autorenew_type, false ); } else { $this->_setLogErrorMSG( 3, $ipn, 'Received back from paypal: ' . var_export( $ipn, true ), CBPTXT::T("Payment notification mismatch: ") . $noFraudCheckResult . '.' ); $ret = false; //TBD: update notification record ! $ipn->log_type = 'G'; // PDT FRAUD detected $ipn->raw_result = $noFraudCheckResult; $_CB_database->updateObject( $ipn->getTableName(), $ipn, $ipn->getKeyName(), false); } } } elseif ( $resultMessage == 'FAIL' ) { $ipn->log_type = 'L'; $_CB_database->insertObject( $ipn->getTableName(), $ipn, $ipn->getKeyName() ); $this->_setLogErrorMSG( 3, $ipn, 'Paypal: Error: Received FAIL result message from Paypal', CBPTXT::T("Sorry your payment has not been processed. Transaction result:") . $transaction_info . '. ' . CBPTXT::T("Please try again and notify system administrator.") ); $ret = false; } else { $ipn->log_type = 'N'; $_CB_database->insertObject( $ipn->getTableName(), $ipn, $ipn->getKeyName() ); $this->_setLogErrorMSG( 3, $ipn, 'Paypal: Error: Received following unknown result message from Paypal: ' . $resultMessage, CBPTXT::T("Sorry no response for your payment. Please check your email and status later.") ); $ret = false; } } } } else { // result=success but not a PDT from paypal: // it could be a subscription with a free trial period: in that case, as there is no initial transaction, we get returned without txn_id. // we must either guess that user subscribed (if he is allowed for free trials, or must wait for IPN: $paymentBasketId = (int) $this->_getReqParam( 'basket' ); if ( $paymentBasketId ) { if ( $paymentBasket->load( (int) $paymentBasketId ) ) { $cbpid = cbGetParam( $_REQUEST, 'cbpid', '' ); if ( $cbpid == $paymentBasket->shared_secret ) { $enable_paypal = $this->getAccountParam( 'enabled', 0 ); $isAnyAutoRecurring = $paymentBasket->isAnyAutoRecurring(); $pay1subscribe2 = $this->_getPaySubscribePossibilities( $enable_paypal, $paymentBasket ); if ( $isAnyAutoRecurring && ( ( $pay1subscribe2 & 0x2 ) != 0 ) && $paymentBasket->period1 ) { // Free first period: Wait for IPN for 20 times 1 second: for ( $i = 0; $i < 20; $i++ ) { if ( $paymentBasket->load( (int) $paymentBasketId ) ) { if ( $paymentBasket->payment_status == 'Completed' ) { break; } } else { break; } sleep( 1 ); } if ( $paymentBasket->payment_status != 'Completed' ) { if ( ( $isAnyAutoRecurring == 1 ) || ( ( $isAnyAutoRecurring == 2 ) && ( $paymentBasket->mc_amount1 != 0 ) ) ) { // 1: forced subscription: error if no IPN came to update payment basket: // 2: not forced subscription but no free initial value: we really need IPN to know status: $this->_setErrorMSG(CBPTXT::T("Sorry, payment has not been confirmed by Paypal (no IPN received). IPN must be enabled for auto-recurring payment subscriptions.")); } else { // user-choice: no need to wait for payment basket completed to activate subscriptions: $ipn = new cbpaidPaymentNotification($_CB_database); $ipn->payment_method = $this->getPayName(); $ipn->gateway_account = $this->getAccountParam( 'id' ); $ipn->log_type = 'S'; $ipn->time_received = date( 'Y-m-d H:i:s', $_CB_framework->now() ); $ipn->raw_data = /* cbGetParam() not needed: we want raw info */ '$_GET=' . var_export( $_GET, true ) . ";\n"; $ipn->raw_data .= /* cbGetParam() not needed: we want raw info */ '$_POST=' . var_export( $_POST, true ) . ";\n"; $ipn->ip_addresses = cbpaidRequest::getIPlist(); $ipn->payment_basket_id = $paymentBasket->id; $ipn->user_id = $paymentBasket->user_id; $_CB_database->insertObject( $ipn->getTableName(), $ipn, $ipn->getKeyName() ); if ( $isAnyAutoRecurring == 2 ) { $autorecurring_type = 2; $paypalUserChoicePossible = ( ( $this->getAccountParam( 'enabled', 0 ) == 3 ) && ( $paymentBasket->isAnyAutoRecurring() == 2 ) ); $autorenew_type = ( $autorecurring_type ? ( $paypalUserChoicePossible ? 1 : 2 ) : 0 ); } else { $autorecurring_type = 0; $autorenew_type = 0; } $paymentBasket->payment_method = $this->getPayName(); $paymentBasket->gateway_account = $this->getAccountParam( 'id' ); $this->updatePaymentStatus( $paymentBasket, 'web_accept', 'Completed', $ipn, 1, $autorecurring_type, $autorenew_type, false ); } } } } } } } return $ret; }
/** * The user got redirected back from the payment service provider with a success message: let's see how successfull it was * * @param cbpaidPaymentBasket $paymentBasket New empty object. returning: includes the id of the payment basket of this callback (strictly verified, otherwise untouched) * @param array $requestdata Data returned by gateway * @param string $type Type of return ('R' for PDT, 'I' for INS, 'A' for Autorecurring payment (Vault) ) * @param array $additionalLogData Additional strings to log with IPN * @return string HTML to display if frontend, text to return to gateway if notification, FALSE if registration cancelled and ErrorMSG generated, or NULL if nothing to display */ private function _returnParamsHandler($paymentBasket, $requestdata, $type, $additionalLogData = null) { global $_CB_framework, $_GET, $_POST; $oid = $requestdata["ordernumber"]; $qp = new QuickpayApi(); $qp->setOptions($this->api_key); if ($this->getAccountParam('enabled') == 2) { $qp->mode = 'subscriptions?order_id='; } else { $qp->mode = 'payments?order_id='; } // Commit the status request, checking valid transaction id $str = $qp->status($oid); $str["operations"][0] = array_reverse($str["operations"][0]); $qp_status = $str[0]["operations"][0]["qp_status_code"]; $qp_type = strtolower($str[0]["type"]); $qp_status_msg = $str[0]["operations"][0]["qp_status_msg"]; $qp_vars = $str[0]["variables"]; $qp_id = $str[0]["id"]; $qp_order_id = $str[0]["order_id"]; $qp_aq_status_code = $str[0]["aq_status_code"]; $qp_aq_status_msg = $str[0]["aq_status_msg"]; $qp_cardtype = $str[0]["metadata"]["brand"]; $qp_cardnumber = "xxxx-xxxxxx-" . $str[0]["metadata"]["last4"]; $qp_amount = $str[0]["operations"][0]["amount"]; $qp_currency = $str[0]["currency"]; $qp_pending = $str[0]["pending"] == "true" ? " - pending " : ""; $qp_expire = $str[0]["metadata"]["exp_month"] . "-" . $str[0]["metadata"]["exp_year"]; $ret = null; $paymentBasketId = $requestdata["cbpbasket"]; if ($paymentBasketId) { $exists = $paymentBasket->load((int) $paymentBasketId); if ($exists && ($requestdata["cbpid"] == $paymentBasket->shared_secret && !(($type == 'R' || $type == 'I') && $paymentBasket->payment_status == 'Completed'))) { // PDT doesn't return transacton information; lets request for it: /* if ( $type == 'R' ) { $requestdata = $str; $formvars = array(); $formvars['protocol'] = '3'; $formvars['msgtype'] = 'status'; $formvars['merchant'] = $this->getAccountParam( 'pspid' ); $formvars['ordernumber'] = $this->_prepareOrderNumber( $paymentBasket->id, true ); $formvars['splitpayment'] = '0'; $this->_signRequestParams( $formvars ); $response = null; $status = null; $error = $this->_httpsRequest( $this->_pspApiUrl(), $formvars, 30, $response, $status, 'post', 'normal', '*/ /*', true, 443, '', '', true, null ); */ /* if ( ( ! $error ) && ( $status == 200 ) && $response ) { $xml_response = $this->xmlTagValuesToArray( new SimpleXMLElement( $response, LIBXML_NONET | ( defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0 ) ) ); if ( $xml_response ) { $requestdata = $xml_response; } } } */ // Log the return record: $log_type = $type; $reason = null; $paymentStatus = $this->_paymentStatus($qp_status, $this->reason); $paymentType = $qp_cardtype; $paymentTime = $_CB_framework->now(); if ($paymentStatus == 'Error') { $errorTypes = array('I' => 'D', 'R' => 'E'); if (isset($errorTypes[$type])) { $log_type = $errorTypes[$type]; } } $ipn = $this->_prepareIpn($log_type, $paymentStatus, $paymentType, $this->reason, $paymentTime, 'utf-8'); if ($qp_type == 'refund') { // in case of refund we need to log the payment as it has same TnxId as first payment: so we need payment_date for discrimination: $ipn->payment_date = gmdate('H:i:s M d, Y T', $paymentTime); // paypal-style } $ipn->test_ipn = 0; $ipn->raw_data = '$message_type="' . ($type == 'R' ? 'RETURN_TO_SITE' : ($type == 'I' ? 'NOTIFICATION' : 'UNKNOWN')) . '";' . "\n"; if ($additionalLogData) { foreach ($additionalLogData as $k => $v) { $ipn->raw_data .= '$' . $k . '="' . var_export($v, true) . '";' . "\n"; } } $ipn->raw_data .= '$requestdata=' . var_export($requestdata, true) . ";\n" . '$_GET=' . var_export($_GET, true) . ";\n" . '$_POST=' . var_export($_POST, true) . ";\n"; if ($paymentStatus == 'Error') { $paymentBasket->reason_code = $this->reason; $this->_storeIpnResult($ipn, 'ERROR:' . $this->reason); $this->_setLogErrorMSG(4, $ipn, $this->getPayName() . ': ' . $this->reason, CBPTXT::T('Sorry, the payment server replied with an error.') . ' ' . CBPTXT::T('Please contact site administrator to check payment status and error log.')); $ret = false; } else { $ipn->bindBasket($paymentBasket); $ipn->sale_id = $paymentBasketId; $insToIpn = array('txn_id' => $qp_id, 'mc_currency' => $qp_currency, 'receiver_email' => $qp_vars["merchant_email"], 'first_name' => $qp_vars['first_name'], 'last_name' => $qp_vars['last_name'], 'address_street' => $qp_vars['address_one'], 'address_zip' => $qp_vars['postal_code'], 'address_city' => $qp_vars['city'], 'address_country' => $qp_vars['country'], 'address_state' => $qp_vars['state_or_province'], 'contact_phone' => $qp_vars['phone'], 'payer_email' => $qp_vars['email']); foreach ($insToIpn as $k => $v) { $ipn->{$k} = $v; } $ipn->mc_gross = sprintf('%.2f', $qp_amount / 100); $ipn->user_id = (int) $paymentBasket->user_id; // check what type of purchase this is: $recurring = in_array($qp_type, array('subscription', 'recurring')) ? true : false; //subscription handling // handle recurring subscriptions properly or default to single payment: if ($recurring) { $qp->mode = "subscriptions/"; $addlink = $qp_id . "/recurring/"; $process_parameters["amount"] = $qp_amount; $process_parameters["order_id"] = $paymentBasket->item_number; $process_parameters["auto_capture"] = TRUE; $storder = $qp->createorder($qp_order_id, $qp_currency_code, $process_parameters, $addlink); if ($paymentStatus == 'Completed' && !$paymentBasket->subscr_id) { $ipn->txn_type = 'subscr_signup'; $ipn->subscr_id = $qp_id; $ipn->subscr_date = $ipn->payment_date; } elseif ($paymentStatus == 'Denied') { if ($paymentBasket->reattempts_tried + 1 <= cbpaidScheduler::getInstance($this)->retries) { $ipn->txn_type = 'subscr_failed'; } else { $ipn->txn_type = 'subscr_cancel'; } } elseif (in_array($paymentStatus, array('Completed', 'Processed', 'Pending'))) { $ipn->txn_type = 'subscr_payment'; } } else { $ipn->txn_type = 'web_accept'; } // validate payment from PDT or IPN $apiReplyRaw = null; $apiReplyArray = null; if ($qp_status == 20000) { $ipn->raw_data .= '$apiReplyRaw=\'' . str_replace(array('\\', '\''), array('\\\\', '\\\''), $apiReplyRaw) . "';\n" . '$apiReplyFormattedArray=' . var_export($apiReplyArray, true) . ";\n"; if ($paymentBasketId == $requestdata["cbpbasket"] && (sprintf('%.2f', $paymentBasket->mc_gross) == $ipn->mc_gross || $ipn->payment_status == 'Refunded') && $paymentBasket->mc_currency == $ipn->mc_currency) { if (in_array($ipn->payment_status, array('Completed', 'Processed', 'Pending', 'Refunded', 'Denied'))) { $this->_storeIpnResult($ipn, 'SUCCESS'); $this->_bindIpnToBasket($ipn, $paymentBasket); // add the gateway to the basket: $paymentBasket->payment_method = $this->getPayName(); $paymentBasket->gateway_account = $this->getAccountParam('id'); // 0: not auto-recurring, 1: auto-recurring without payment processor notifications, 2: auto-renewing with processor notifications updating $expiry_date: $autorecurring_type = in_array($ipn->txn_type, array('subscr_payment', 'subscr_signup', 'subscr_modify', 'subscr_eot', 'subscr_cancel', 'subscr_failed')) ? 2 : 0; // 0: not auto-renewing (manual renewals), 1: asked for by user, 2: mandatory by configuration: $autorenew_type = $autorecurring_type ? $this->getAccountParam('enabled', 0) == 3 && $paymentBasket->isAnyAutoRecurring() == 2 ? 1 : 2 : 0; if ($recurring) { $paymentBasket->reattempt = 1; // we want to reattempt auto-recurring payment in case of failure } $this->updatePaymentStatus($paymentBasket, $ipn->txn_type, $ipn->payment_status, $ipn, 1, $autorecurring_type, $autorenew_type, false); if (in_array($ipn->payment_status, array('Completed', 'Processed', 'Pending'))) { $ret = true; } } else { $this->_storeIpnResult($ipn, 'FAILED'); $paymentBasket->payment_status = $ipn->payment_status; $this->_setErrorMSG('<div class="message">' . $this->getTxtNextStep($paymentBasket) . '</div>'); $paymentBasket->payment_status = 'RedisplayOriginalBasket'; $ret = false; } } else { $this->_storeIpnResult($ipn, 'MISMATCH'); $this->_setLogErrorMSG(3, $ipn, $this->getPayName() . ': amount or currency missmatch', CBPTXT::T('Sorry, the payment does not match the basket.') . ' ' . CBPTXT::T('Please contact site administrator to check error log.')); $ret = false; } } else { $this->_storeIpnResult($ipn, 'SIGNERROR'); $this->_setLogErrorMSG(3, $ipn, $this->getPayName() . ': Transaction does not match with gateway. Please check API Key setting', CBPTXT::T('The API Key is incorrect.') . ' ' . CBPTXT::T('Please contact site administrator to check error log.')); $ret = false; } } } } else { $this->_setLogErrorMSG(3, null, $this->getPayName() . ': ordernumber is missing in the return URL: ' . var_export($_GET, true), CBPTXT::T('Please contact site administrator to check error log.')); } return $ret; }
/** * Handles the gateway-specific result of payments (redirects back to this site and gateway notifications). WARNING: unchecked access ! * * @param cbpaidPaymentBasket $paymentBasket New empty object. returning: includes the id of the payment basket of this callback (strictly verified, otherwise untouched) * @param array $postdata _POST data for saving edited tab content as generated with getEditTab * @param string $result result= get parameter, other than 'notify', 'success' or 'cancel'. * @return string HTML to display if frontend, text to return to gateway if notification, FALSE if registration cancelled and ErrorMSG generated, or NULL if nothing to display */ protected function handleOtherResult($paymentBasket, $postdata, $result) { global $_CB_framework, $_POST; $ret = null; // $privateVarsList = 'id payment_method gateway_account user_id time_initiated time_completed ip_addresses mc_gross mc_currency quantity item_number item_name shared_secret payment_status'; switch ($result) { case 'showinstructions': //We need to display payment instructions: $paymentBasketId = (int) cbGetParam($_POST, 'custom', 0); if ($paymentBasketId == 0) { $paymentBasketId = (int) cbGetParam($_GET, 'custom', 0); } $exists = $paymentBasket->load((int) $paymentBasketId); if ($exists && cbGetParam($_REQUEST, 'amount', 0.0) == $paymentBasket->mc_gross && cbGetParam($_REQUEST, 'currency_code', 0) == $paymentBasket->mc_currency) { $ret = $this->_outputInstructions($paymentBasket); if ($ret !== false) { outputCbTemplate(); $this->_outputRegTemplate(); $ret = $paymentBasket->displayBasket() . $ret; } } else { $this->_setErrorMSG(CBPTXT::T("Payment basket does not login.")); $ret = false; } break; case 'showslip': // The user asked to see the payment slip. if (isset($_GET['user']) && isset($_GET['cbpid'])) { $paymentBasketId = (int) cbGetParam($_GET, 'cbpid', 0); $exists = $paymentBasket->load((int) $paymentBasketId); if ($exists && (int) cbGetParam($_GET, 'user', 0) == $paymentBasket->user_id && (int) cbGetParam($_GET, 'cbpbhs') == md5($paymentBasket->shared_secret)) { // log: $ipn = $this->_logNotification('R', $_CB_framework->now(), $paymentBasket); if ($paymentBasket->payment_status == 'NotInitiated' && $paymentBasket->payment_method == $this->getPayName() && $paymentBasket->gateway_account == $this->getAccountParam('id')) { $privateVarsList = 'id user_id time_initiated time_completed ip_addresses mc_gross mc_currency ' . 'quantity item_number item_name shared_secret payment_status ' . 'invoice period1 period2 period3 mc_amount1 mc_amount2 mc_amount3'; $paymentBasket->bindObjectToThisObject($ipn, $privateVarsList); $newPaymentState = $this->getAccountParam('pending_payment_state', 'Pending'); $this->updatePaymentStatus($paymentBasket, 'web_accept', $newPaymentState, $ipn, 1, 0, 0, true, false); } if (in_array($paymentBasket->payment_status, array('NotInitiated', 'Pending'))) { $ret = $this->_outputSlip($paymentBasket); } } } break; default: $ret = CBPTXT::Th("Unexpected result"); break; } return $ret; }