/** * Fills object with all standard items of a Notification record * * @param cbpaidPayHandler $payHandler * @param int $test_ipn * @param string $log_type * @param string $paymentStatus * @param string $paymentType * @param string $reasonCode * @param int $paymentTime * @param string $charset */ public function initNotification($payHandler, $test_ipn, $log_type, $paymentStatus, $paymentType, $reasonCode, $paymentTime, $charset = 'utf-8') { $this->payment_method = $payHandler->getPayName(); $this->gateway_account = $payHandler->getAccountParam('id'); $this->log_type = $log_type; $this->time_received = Application::Database()->getUtcDateTime(); $this->ip_addresses = cbpaidRequest::getIPlist(); $this->geo_ip_country_code = cbpaidRequest::getGeoIpCountryCode(); $this->notify_version = '2.1'; $this->user_id = (int) cbGetParam($_GET, 'user', 0); $this->charset = $charset; $this->test_ipn = $test_ipn; $this->payer_status = 'unverified'; $this->payment_status = $paymentStatus; if (in_array($paymentStatus, array('Completed', 'Pending', 'Processed', 'Failed', 'Reversed', 'Refunded', 'Partially-Refunded', 'Canceled_Reversal'))) { if (in_array($paymentStatus, array('Completed', 'Reversed', 'Refunded', 'Partially-Refunded', 'Canceled_Reversal'))) { $this->payment_date = gmdate('H:i:s M d, Y T', $paymentTime); // paypal-style } $this->payment_type = $paymentType; } if ($reasonCode) { $this->reason_code = $reasonCode; } }
/** * create a new subscription object and corresponding object in database * * @param int|null $user_id CB user id * @param cbpaidProduct $plan plan object of this subscription * @param string $status like status class variable * @param boolean $store true (default) if should be stored into db * @param int $subscriptionTime time of subscription */ protected function createMerchandiseRecord( $user_id, &$plan, $status = 'R', $store = true, $subscriptionTime = null ) { global $_CB_framework; if ( $subscriptionTime === null ) { $subscriptionTime = $_CB_framework->now(); } $this->reset(); $this->user_id = $user_id; $this->plan_id = $plan->get( 'id' ); $this->payment_date = date( 'Y-m-d H:i:s', $subscriptionTime ); $this->getCurrencyAmount( $plan ); $this->status = $status; if ( is_object( $plan->_integrations ) ) { $this->integrations = $plan->_integrations->asJson(); } else { $this->integrations = ''; } if ( $store ) { $this->ip_addresses = cbpaidRequest::getIPlist(); $this->historySetMessage( $this->recordName() . ' record created' ); $this->store(); } $this->_plan = $plan; }
/** * 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) { $key = $this->_tbl_key; if (!$this->{$key}) { $this->event_time = $this->_db->getUtcDateTime(); $this->user_id = Application::MyUser()->getUserId(); $this->ip_addresses = cbpaidRequest::getIPlist(); $this->log_version = 1; } return parent::store($updateNulls); }
/** * 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; $key = $this->_tbl_key; if ( ! $this->$key ) { $this->event_time = date( 'Y-m-d H:i:s', $_CB_framework->now() ); $this->user_id = $_CB_framework->myId(); $this->ip_addresses = cbpaidRequest::getIPlist(); $this->log_version = 1; } return parent::store( $updateNulls ); }
/** * create a new subscription object and corresponding object in database * * @param int|null $user_id CB user id * @param cbpaidProduct $plan plan object of this subscription * @param string $status like status class variable * @param boolean $store true (default) if should be stored into db * @param int $subscriptionTime time of subscription */ protected function createMerchandiseRecord($user_id, $plan, $status = 'R', $store = true, $subscriptionTime = null) { if ($subscriptionTime === null) { $subscriptionTime = cbpaidTimes::getInstance()->startTime(); } $this->reset(); $this->user_id = $user_id; $this->plan_id = $plan->get('id'); $this->payment_date = $this->_db->getUtcDateTime($subscriptionTime); $this->getCurrencyAmount($plan); $this->status = $status; if (is_object($plan->_integrations)) { $this->integrations = $plan->_integrations->asJson(); } else { $this->integrations = ''; } if ($store) { $this->ip_addresses = cbpaidRequest::getIPlist(); $this->historySetMessage($this->recordName() . ' record created'); $this->store(); } $this->_plan = $plan; }
/** * create a new subscription object and corresponding object in database * * @param int|null $user_id CB user id * @param cbpaidProduct $plan plan object of this subscription * @param array|null $replacesSubscriptionId array( planId, subscriptionId ) or NULL of the replaced subscription * @param array|null $parentSubId array( PlanId, SubscriptionId ) or NULL of the parent subscription * @param string $status like status class variable * @param boolean $store true (default) if should be stored into db * @param int $subscriptionTime time of subscription */ public function createSubscription( $user_id, &$plan, $replacesSubscriptionId = null, $parentSubId = null, $status = 'R', $store = true, $subscriptionTime = null ) { global $_CB_framework, $_CB_database; if ( $subscriptionTime === null ) { $subscriptionTime = $_CB_framework->now(); } $this->reset(); $this->user_id = $user_id; $this->plan_id = $plan->get( 'id' ); if ( $replacesSubscriptionId ) { $this->replaces_plan = $replacesSubscriptionId[0]; $this->replaces_subscription = $replacesSubscriptionId[1]; } if ( $parentSubId ) { $this->parent_plan = $parentSubId[0]; $this->parent_subscription = $parentSubId[1]; } else { $this->parent_plan = 0; $this->parent_subscription = 0; } $this->subscription_date = date( 'Y-m-d H:i:s', $subscriptionTime ); $this->last_renewed_date = $this->subscription_date; $this->expiry_date = null; $this->status = $status; $this->autorenew_type = 0; // will be changed later at payment time $this->autorecurring_type = 0; // will be changed later at payment time $this->regular_recurrings_total = $plan->get( 'recurring_max_times' ); $this->regular_recurrings_used = 0; $this->previous_recurrings_used = 0; if ( is_object( $plan->_integrations ) ) { $this->integrations = $plan->_integrations->asJson(); } else { $this->integrations = ''; } if ( $store ) { $this->ip_addresses = cbpaidRequest::getIPlist(); $this->historySetMessage( 'User subscription record created with status: ' . $status ); if (!$this->store() ) { trigger_error( 'subscription store error:'.htmlspecialchars($_CB_database->getErrorMsg()), E_USER_ERROR ); } } $this->_plan = $plan; }
/** * 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; }
/** * Logs notification * * @param string $log_type * @param int $now * @param cbpaidPaymentBasket $paymentBasket * @return cbpaidPaymentNotification */ private function _logNotification( $log_type, $now, $paymentBasket ) { global $_CB_database; $ipn = new cbpaidPaymentNotification($_CB_database); $ipn->payment_method = $this->getPayName(); $ipn->gateway_account = $this->getAccountParam( 'id' ); $ipn->log_type = $log_type; $ipn->time_received = date( 'Y-m-d H:i:s', $now ); $ipn->payment_basket_id = $paymentBasket->id; $ipn->raw_data = '$_POST=' . var_export( $_POST, true ) . ';\n'; $ipn->raw_result = 'FREE_TRIAL'; $ipn->ip_addresses = cbpaidRequest::getIPlist(); $ipn->notify_version = '2.1'; $ipn->user_id = (int) cbGetParam( $_GET, 'user', 0 ); $ipn->charset = 'utf-8'; $ipn->test_ipn = 0; $ipn->first_name = $paymentBasket->first_name; $ipn->last_name = $paymentBasket->last_name; $ipn->payer_status = 'unverified'; $ipn->item_name = $paymentBasket->item_name; $ipn->item_number = $paymentBasket->item_number; $ipn->quantity = $paymentBasket->quantity; $ipn->custom = $paymentBasket->id; $ipn->invoice = $paymentBasket->invoice; $ipn->mc_currency = $paymentBasket->mc_currency; $ipn->tax = '0.00'; $ipn->mc_gross = '0.00'; $ipn->payment_status = 'Completed'; $ipn->payment_date = date( 'H:i:s M d, Y T', $now ); // paypal-style $ipn->payment_type = 'Free trial'; $ipn->txn_id = null; $ipn->txn_type = 'web_accept'; $ipn->recurring = 0; $_CB_database->insertObject( $ipn->getTableName(), $ipn, $ipn->getKeyName() ); return $ipn; }
/** * create a paymentBasket in database * * @param UserTable $user * @param float $price * @param string $currency * @param int $quantity * @param string $item_number * @param string $item_name * @param boolean $store default: TRUE: store object in database, FALSE: keep in memory only * @param int $now unix time * @param int $owner basket owner (seller) * @param string $reason payment reason: 'N'=new subscription (default), 'R'=renewal, 'U'=update */ public function createPaymentBasket( &$user, $price, $currency, $quantity, $item_number, $item_name, $store, $now, $owner, /** @noinspection PhpUnusedParameterInspection */ $reason ) { global $_CB_database; $this->reset(); $this->user_id = (int) $user->id; $this->owner = (int) $owner; $this->payment_status = 'NotInitiated'; $this->time_initiated = date( 'Y-m-d H:i:s', $now ); $this->ip_addresses = cbpaidRequest::getIPlist(); $this->mc_gross = $price; // for now, later sum... $this->mc_currency = $currency; $this->quantity = $quantity; $this->item_number = $item_number; $this->item_name = $item_name; $this->setRandom_shared_secret(); $this->_setInvoicingAddress( $user ); if ( $store ) { $this->historySetMessage( 'Creating new payment basket' ); if ( ! $this->store() ) { trigger_error( 'payment_basket store error:' . htmlspecialchars( $_CB_database->getErrorMsg() ), E_USER_ERROR ); } } }
/** * Prepares AIM request * * @param string $aimRequestType : AUTH_CAPTURE, AUTH_ONLY, CAPTURE_ONLY, CREDIT, VOID, PRIOR_AUTH_CAPTURE * @param array $card : $card['type'], $card['number'], $card['firstname'], $card['lastname'], $card['expmonth'], $card['expyear'], and optionally: $card['address'], $card['zip'], $card['country'] * @param cbpaidPaymentBasket $paymentBasket : WARNING: Using mc_amount3 as price as it's a subscription, instead of mc_gross. * @param boolean $authnetSubscription true if it is a subscription and amount is in mc_amount1 and not in mc_gross * @return mixed string of XML request */ private function _encodeAIMPostRequest( $aimRequestType, $card, &$paymentBasket, $authnetSubscription ) { $authorize_login_id = $this->ISOtoUtf8( $this->getAccountParam( 'authorize_login_id' ) ); $authorize_transaction_key = $this->ISOtoUtf8( $this->getAccountParam( 'authorize_transaction_key' ) ); $invoiceNum = $this->ISOtoUtf8( $paymentBasket->invoice ? $paymentBasket->invoice : $paymentBasket->id ); $refId = $this->ISOtoUtf8( $paymentBasket->id ); // $subscriptionName = $this->_cbp_utf8_substr( $this->ISOtoUtf8( $paymentBasket->item_name ), 0, 20 ); $subscriptionDescription = $this->_cbp_utf8_substr( $this->ISOtoUtf8( $paymentBasket->item_name ), 0, 255 ); //TBD Check if really needed ! $subscriptionTiming = $this->_computeSubscriptionTiming( $paymentBasket, 'noUpfrontFirstCharge', $card ); if ( $authnetSubscription > 1 ) { if ( $paymentBasket->period1 ) { $amount = sprintf( '%.2f', $paymentBasket->mc_amount1 ); } else { $amount = sprintf( '%.2f', $paymentBasket->mc_amount3 ); } } else { $amount = sprintf( '%.2f', $paymentBasket->mc_gross ); } $cardNumber = substr( preg_replace ( '/[^0-9]+/', '', strval( $card['number'] ) ), 0, 22 ); $cardExpirationDateMM_YYYY = substr( sprintf( '%02d', intval( $card['expmonth'] ) ), 0, 2 ) . '-' . substr( strval( intval( $card['expyear'] ) ), 0, 4 ); $cardCVV = substr( preg_replace ( '/[^0-9]+/', '', strval( $card['cvv'] ) ), 0, 4 ); if ( ! $cardCVV ) { $cardCVV = ''; } $firstName = $this->_cbp_utf8_substr( $this->ISOtoUtf8( $card['firstname'] ), 0, 50 ); $lastName = $this->_cbp_utf8_substr( $this->ISOtoUtf8( $card['lastname'] ), 0, 50 ); $email = $this->_cbp_utf8_substr( $this->ISOtoUtf8( $paymentBasket->payer_email ), 0, 255 ); $country = ( isset( $card['country'] ) ? $this->_cbp_utf8_substr( $this->ISOtoUtf8( $card['country'] ), 0, 60 ) : null ); $zip = ( isset( $card['zip'] ) ? $this->_cbp_utf8_substr( $this->ISOtoUtf8( $card['zip'] ), 0, 20 ) : null ); $address = ( isset( $card['address'] ) ? $this->_cbp_utf8_substr( $this->ISOtoUtf8( $card['address'] ), 0, 60 ) : null ); $ipAddressesArray = cbpaidRequest::getIParray(); $ipAddress = substr( array_pop( $ipAddressesArray ), 0, 15 ); // last one is the address we get from our own (trusted) webserver switch ( $aimRequestType ) { case 'AUTH_CAPTURE': $authnet_values = array ( 'x_version' => '3.1', 'x_relay_response' => 'FALSE', 'x_delim_data' => 'TRUE', 'x_delim_char' => '*', 'x_encap_char' => '|', 'x_duplicate_window' => '28800', // no reason to pay twice same basket => 8 hours, maximum allowed by authorize.net 'x_recurring_billing' => 'NO', 'x_login' => $authorize_login_id, 'x_tran_key' => $authorize_transaction_key, 'x_amount' => $amount, 'x_currency_code' => $paymentBasket->mc_currency, 'x_method' => 'CC', 'x_card_num' => $cardNumber, 'x_exp_date' => $cardExpirationDateMM_YYYY, 'x_card_code' => $cardCVV, 'x_type' => $aimRequestType, 'x_first_name' => $firstName, 'x_last_name' => $lastName, 'x_email' => $email, 'x_cust_id' => $paymentBasket->user_id, 'x_customer_ip' => $ipAddress, /* 'x_address' => '342 N. Main Street #150', 'x_city' => 'Ft. Worth', 'x_state' => 'TX', 'x_zip' => '12345', */ 'x_invoice_num' => $invoiceNum, 'x_description' => $subscriptionDescription, 'cb_custom' => $refId, ); if ( $country !== null ) { $authnet_values['x_country'] = $country; } if ( $zip !== null ) { $authnet_values['x_zip'] = $zip; } if ( $address !== null ) { $authnet_values['x_address'] = $address; } break; default: $authnet_values = array(); break; } return $authnet_values; }
/** * Returns default invoice country code of this user * * @param boolean $onlyGeoIp * @return string|null */ public function getDefaultInvoiceCountryCode($onlyGeoIp = true) { $buyerCountryCode = cbpaidRequest::getGeoIpCountryCode(); if ($buyerCountryCode) { return $buyerCountryCode; } if ($onlyGeoIp) { return null; } $params = cbpaidApp::settingsParams(); if ($params->get('integration_cbsubstax_enabled') != 1) { return null; } return $params->get('integration_cbsubstax_system_buyer_country_iso_code2'); }
/** * Attempts to authorize and capture a credit card for a single payment of a payment basket * * @param array $card contains type, number, firstname, lastname, expmonth, expyear, and optionally: address, zip, country * @param cbpaidPaymentBasket $paymentBasket * @param int $now unix timestamp of now * @param cbpaidsubscriptionsNotification $ipn returns the stored notification * @param boolean $authnetSubscription true if it is a subscription and amount is in mc_amount1 and not in mc_gross * @return array|bool subscriptionId if subscription request succeeded, otherwise ARRAY( 'level' => 'spurious' or 'fatal', 'errorText', 'errorCode' => string ) of error to display */ protected function processSinglePayment( $card, $paymentBasket, $now, &$ipn, $authnetSubscription ) { if ( $this->hasPaypalApi() ) { $countries = new cbpaidCountries(); if ( $authnetSubscription > 1 ) { if ( $paymentBasket->period1 ) { $amount = sprintf( '%.2f', $paymentBasket->mc_amount1 ); } else { $amount = sprintf( '%.2f', $paymentBasket->mc_amount3 ); } } else { $amount = sprintf( '%.2f', $paymentBasket->mc_gross ); } $ipAddresses = cbpaidRequest::getIParray(); $requestParams = array( 'METHOD' => 'DoDirectPayment', 'PAYMENTACTION' => 'Sale', 'IPADDRESS' => substr( array_pop( $ipAddresses ), 0, 15 ), 'CREDITCARDTYPE' => cbIsoUtf_substr( $card['type'], 0, 10 ), 'ACCT' => substr( preg_replace ( '/[^0-9]+/', '', strval( $card['number'] ) ), 0, 22 ), 'EXPDATE' => substr( sprintf( '%02d', intval( $card['expmonth'] ) ), 0, 2 ) . substr( strval( intval( $card['expyear'] ) ), 0, 4 ), 'CVV2' => substr( preg_replace ( '/[^0-9]+/', '', strval( $card['cvv'] ) ), 0, 4 ), 'EMAIL' => cbIsoUtf_substr( $paymentBasket->payer_email, 0, 127 ), 'FIRSTNAME' => cbIsoUtf_substr( $card['firstname'], 0, 25 ), 'LASTNAME' => cbIsoUtf_substr( $card['lastname'], 0, 25 ), 'STREET' => cbIsoUtf_substr( $paymentBasket->address_street, 0, 100 ), 'CITY' => cbIsoUtf_substr( $paymentBasket->address_city, 0, 40 ), 'STATE' => cbIsoUtf_substr( substr( $paymentBasket->address_state, -2 ), 0, 2 ), 'COUNTRYCODE' => $countries->countryToTwoLetters( $paymentBasket->address_country ), 'ZIP' => cbIsoUtf_substr( $paymentBasket->address_zip, 0, 20 ), 'AMT' => $amount, 'CURRENCYCODE' => $paymentBasket->mc_currency, 'DESC' => $paymentBasket->item_name, 'CUSTOM' => $paymentBasket->id, 'INVNUM' => $paymentBasket->invoice, 'NOTIFYURL' => $this->getNotifyUrl( $paymentBasket ) ); $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 ); } if ( $error || ( $status != 200 ) || ( ! $response ) ) { $return = array( 'level' => 'spurious', 'errorText' => CBPTXT::T( "Submitted subscription payment didn't return an error but didn't complete." ) . ' ' . CBPTXT::T( 'Please contact site administrator to check error log.' ), 'errorCode' => '8888' ); $logType = 'B'; } else { if ( cbGetParam( $results, 'ACK' ) == 'Success' ) { $return = cbGetParam( $results, 'TRANSACTIONID' ); $logType = 'P'; } else { $return = array( 'level' => 'fatal', 'errorText' => cbGetParam( $results, 'L_SHORTMESSAGE0' ) . '. ' . CBPTXT::T( 'Please contact site administrator to check error log.' ), 'errorCode' => cbGetParam( $results, 'L_ERRORCODE0' ) ); $logType = 'V'; } } $ipn = $this->_logNotification( $logType, $now, $paymentBasket, $card, $requestParams, $response, $results, $return ); } else { $return = array( 'level' => 'fatal', 'errorText' => CBPTXT::T( 'Needed Paypal API username, password and signature not set.' ) . ' ' . CBPTXT::T( 'Please contact site administrator to check error log.' ), 'errorCode' => '8888' ); } return $return; }
/** * Gets a comma-separated list of IP addresses taking in account the proxys on the way. * An array is needed because FORWARDED_FOR can be facked as well. * * @obsolete since CBSubs 2.1 * * @return string of IP addresses, first one being host, and last one last proxy (except fackings) */ function cbpaidGetIPlist() { return cbpaidRequest::getIPlist(); }
/** * 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; }