/** * Asks the gateway to do whatever it does to process the payment. Onsite gateways will * usually send a request directly to the payment provider and update the payment's status based on that; * whereas offsite gateways will usually just update the payment with the URL and query parameters to use * for sending the request via http_remote_request() * @param EEI_Payment $payment * @param array $billing_info { * @type $credit_card string * @type $cvv string * @type $exp_month string * @type $exp_year string * @see parent::do_direct_payment * } * @return EE_Payment updated */ public function do_direct_payment($payment, $billing_info = null) { // Enable test mode if needed //4007000000027 <-- test successful visa //4222222222222 <-- test failure card number $item_num = 1; $transaction = $payment->transaction(); $order_description = ''; $primary_registrant = $transaction->primary_registration(); //if we're are charging for the full amount, show the normal line items //and the itemized total adds up properly if ($this->_can_easily_itemize_transaction_for($payment)) { $total_line_item = $transaction->total_line_item(); foreach ($total_line_item->get_items() as $line_item) { $this->addLineItem($item_num++, $line_item->name(), $line_item->desc(), $line_item->quantity(), $line_item->unit_price(), 'N'); $order_description .= $line_item->desc() . ', '; } foreach ($total_line_item->tax_descendants() as $tax_line_item) { $this->addLineItem($item_num++, $tax_line_item->name(), $tax_line_item->desc(), 1, $tax_line_item->total(), 'N'); } } else { //partial payment $order_description = sprintf(__("Payment of %s for %s", "event_espresso"), $payment->amount(), $primary_registrant->reg_code()); } //start transaction $this->setField('amount', $this->format_currency($payment->amount())); $this->setField('description', substr(rtrim($order_description, ', '), 0, 255)); $this->setField('card_num', $billing_info['credit_card']); $this->setField('exp_date', $billing_info['exp_month'] . $billing_info['exp_year']); $this->setField('card_code', $billing_info['cvv']); $this->setField('first_name', $billing_info['first_name']); $this->setField('last_name', $billing_info['last_name']); $this->setField('email', $billing_info['email']); $this->setField('address', $billing_info['address'] . ' ' . $billing_info['address2']); $this->setField('city', $billing_info['city']); $this->setField('state', $billing_info['state']); $this->setField('country', $billing_info['country']); $this->setField('zip', $billing_info['zip']); $this->setField('cust_id', $primary_registrant->ID()); $this->setField('phone', $billing_info['phone']); //invoice_num would be nice to have itbe unique per SPCO page-load, taht way if users //press back, they don't submit a duplicate. However, we may be keepin gthe user on teh same spco page //in which case, we need to generate teh invoice num per request right here... $this->setField('invoice_num', wp_generate_password(12, false)); //$billing_info['_reg-page-billing-invoice-'.$this->_gateway_name]['value']); //tell AIM that any duplicates sent in the next 5 minutes are to be ignored $this->setField('duplicate_window', 5 * MINUTE_IN_SECONDS); if ($this->_test_transactions) { $this->test_request = "true"; } //Capture response $this->type = "AUTH_CAPTURE"; $response = $this->_sendRequest($payment); if (!empty($response)) { if ($this->_debug_mode) { $txn_id = $response->invoice_number; } else { $txn_id = $response->transaction_id; } $payment_status = $response->approved ? $this->_pay_model->approved_status() : $this->_pay_model->declined_status(); $payment->set_status($payment_status); //make sure we interpret the AMT as a float, not an international string (where periods are thousand seperators) $payment->set_amount(floatval($response->amount)); $payment->set_gateway_response(sprintf("%s (code: %s)", $response->response_reason_text, $response->response_reason_code)); $payment->set_txn_id_chq_nmbr($txn_id); $payment->set_extra_accntng($primary_registrant->reg_code()); $payment->set_details(print_r($response, true)); } else { $payment->set_status($this->_pay_model->failed_status()); $payment->set_gateway_response(__("There was no response from Authorize.net", 'event_espresso')); $payment->set_details(print_r($response, true)); } return $payment; }
/** * Determines whether or not we can easily itemize the transaction using only * items and taxes (ie, no promotions or surcharges or cancellations needed) * @param EEI_Payment $payment * @return boolean */ protected function _can_easily_itemize_transaction_for(EEI_Payment $payment) { return $this->_money->compare_floats($this->_sum_items_and_taxes($payment->transaction()), $payment->transaction()->total()) && $this->_money->compare_floats($payment->amount(), $payment->transaction()->total()); }
/** * * @param EEI_Payment $payment * @param array $billing_info { * @type $credit_card string * @type $credit_card_type string * @type $exp_month string always 2 characters * @type $exp_year string always 4 characters * @type $cvv string * } @see parent::do_direct_payment for more info * @return \EE_Payment|\EEI_Payment */ public function do_direct_payment($payment, $billing_info = null) { $transaction = $payment->transaction(); $primary_registrant = $transaction->primary_registration(); $order_description = sprintf(__("'Event Registrations from %s", "event_espresso"), get_bloginfo('name')); //charge for the full amount. Show itemized list if ($this->_can_easily_itemize_transaction_for($payment)) { $item_num = 1; $total_line_item = $transaction->total_line_item(); $order_items = array(); foreach ($total_line_item->get_items() as $line_item) { $item = array('l_name' => substr($line_item->name(), 0, 127), 'l_desc' => substr($line_item->desc(), 0, 127), 'l_amt' => $line_item->unit_price(), 'l_number' => $item_num++, 'l_qty' => $line_item->quantity(), 'l_taxamt' => '', 'l_ebayitemnumber' => '', 'l_ebayitemauctiontxnid' => '', 'l_ebayitemorderid' => ''); // add to array of all items array_push($order_items, $item); } $item_amount = $total_line_item->get_items_total(); $tax_amount = $total_line_item->get_total_tax(); } else { $order_items = array(); $item_amount = $payment->amount(); $single_item_desc = sprintf(__("Partial payment of %s for %s", "event_espresso"), $payment->amount(), $primary_registrant->reg_code()); $tax_amount = 0; array_push($order_items, array('l_name' => sprintf(__("Partial payment for registration: %s", 'event_espresso'), $primary_registrant->reg_code()), 'l_desc' => $single_item_desc, 'l_amt' => $payment->amount(), 'l_number' => 1, 'l_qty' => 1)); } // Populate data arrays with order data. $DPFields = array('paymentaction' => 'Sale', 'ipaddress' => $_SERVER['REMOTE_ADDR'], 'returnfmfdetails' => '1'); $CCDetails = array('creditcardtype' => $billing_info['credit_card_type'], 'acct' => $billing_info['credit_card'], 'expdate' => $billing_info['exp_month'] . $billing_info['exp_year'], 'cvv2' => $billing_info['cvv']); $PayerInfo = array('email' => $billing_info['email'], 'payerid' => '', 'payerstatus' => '', 'business' => ''); $PayerName = array('salutation' => '', 'firstname' => substr($billing_info['first_name'], 0, 25), 'middlename' => '', 'lastname' => substr($billing_info['last_name'], 0, 25), 'suffix' => ''); $BillingAddress = array('street' => $billing_info['address'], 'street2' => $billing_info['address2'], 'city' => $billing_info['city'], 'state' => substr($billing_info['state'], 0, 40), 'countrycode' => $billing_info['country'], 'zip' => $billing_info['zip'], 'shiptophonenum' => substr($billing_info['phone'], 0, 20)); $PaymentDetails = array('amt' => $this->format_currency($payment->amount()), 'currencycode' => $payment->currency_code(), 'itemamt' => $this->format_currency($item_amount), 'shippingamt' => '', 'handlingamt' => '', 'taxamt' => $this->format_currency($tax_amount), 'desc' => $order_description, 'custom' => $primary_registrant ? $primary_registrant->ID() : '', 'invnum' => wp_generate_password(12, false), 'notifyurl' => '', 'buttonsource' => 'EventEspresso_SP'); // Wrap all data arrays into a single, "master" array which will be passed into the class function. $PayPalRequestData = array('DPFields' => $DPFields, 'CCDetails' => $CCDetails, 'PayerInfo' => $PayerInfo, 'PayerName' => $PayerName, 'BillingAddress' => $BillingAddress, 'PaymentDetails' => $PaymentDetails, 'OrderItems' => $order_items); $this->_log_clean_request($PayPalRequestData, $payment); try { $PayPalResult = $this->prep_and_curl_request($PayPalRequestData); //remove PCI-sensitive data so it doesn't get stored $PayPalResult = $this->_log_clean_response($PayPalResult, $payment); $message = isset($PayPalResult['L_LONGMESSAGE0']) ? $PayPalResult['L_LONGMESSAGE0'] : $PayPalResult['ACK']; if (empty($PayPalResult['RAWRESPONSE'])) { $payment->set_status($this->_pay_model->failed_status()); $payment->set_gateway_response(__('No response received from Paypal Pro', 'event_espresso')); $payment->set_details($PayPalResult); } else { if ($this->_APICallSuccessful($PayPalResult)) { $payment->set_status($this->_pay_model->approved_status()); } else { $payment->set_status($this->_pay_model->declined_status()); } //make sure we interpret the AMT as a float, not an international string (where periods are thousand separators) $payment->set_amount(isset($PayPalResult['AMT']) ? floatval($PayPalResult['AMT']) : 0); $payment->set_gateway_response($message); $payment->set_txn_id_chq_nmbr(isset($PayPalResult['TRANSACTIONID']) ? $PayPalResult['TRANSACTIONID'] : null); $primary_registration_code = $primary_registrant instanceof EE_Registration ? $primary_registrant->reg_code() : ''; $payment->set_extra_accntng($primary_registration_code); $payment->set_details($PayPalResult); } } catch (Exception $e) { $payment->set_status($this->_pay_model->failed_status()); $payment->set_gateway_response($e->getMessage()); } //$payment->set_status( $this->_pay_model->declined_status() ); //$payment->set_gateway_response( '' ); return $payment; }
/** * Updates the transaction and line items based on the payment IPN data from PayPal, * like the taxes or shipping * @param EEI_Payment $payment */ public function update_txn_based_on_payment($payment) { $update_info = $payment->details(); $transaction = $payment->transaction(); $payment_was_itemized = $payment->get_extra_meta(EEG_Paypal_Standard::itemized_payment_option_name, true, false); if (!$transaction) { $this->log(__('Payment with ID %d has no related transaction, and so update_txn_based_on_payment couldn\'t be executed properly', 'event_espresso'), $payment); return; } if (!is_array($update_info) || !isset($update_info['mc_shipping']) || !isset($update_info['tax'])) { $this->log(array('url' => $this->_process_response_url(), 'message' => __('Could not update transaction based on payment because the payment details have not yet been put on the payment. This normally happens during the IPN or returning from PayPal', 'event_espresso'), 'payment' => $payment->model_field_array()), $payment); return; } if ($payment->status() !== $this->_pay_model->approved_status()) { $this->log(array('url' => $this->_process_response_url(), 'message' => __('We shouldn\'t update transactions taxes or shipping data from non-approved payments', 'event_espresso'), 'payment' => $payment->model_field_array()), $payment); return; } $grand_total_needs_resaving = false; //might paypal have changed the taxes? if ($this->_paypal_taxes && $payment_was_itemized) { //note that we're doing this BEFORE adding shipping; we actually want PayPal's shipping to remain non-taxable $this->_line_item->set_line_items_taxable($transaction->total_line_item(), true, 'paypal_shipping'); $this->_line_item->set_total_tax_to($transaction->total_line_item(), floatval($update_info['tax']), __('Taxes', 'event_espresso'), __('Calculated by Paypal', 'event_espresso'), 'paypal_tax'); $grand_total_needs_resaving = TRUE; } $shipping_amount = floatval($update_info['mc_shipping']); //might paypal have added shipping? if ($this->_paypal_shipping && $shipping_amount && $payment_was_itemized) { $this->_line_item->add_unrelated_item($transaction->total_line_item(), sprintf(__('Shipping for transaction %1$s', 'event_espresso'), $transaction->ID()), $shipping_amount, __('Shipping charges calculated by Paypal', 'event_espresso'), 1, false, 'paypal_shipping_' . $transaction->ID()); $grand_total_needs_resaving = true; } if ($grand_total_needs_resaving) { $transaction->total_line_item()->save_this_and_descendants_to_txn($transaction->ID()); $registration_processor = EE_Registry::instance()->load_class('Registration_Processor'); $registration_processor->update_registration_final_prices($transaction); } $this->log(array('url' => $this->_process_response_url(), 'message' => __('Updated transaction related to payment', 'event_espresso'), 'transaction (updated)' => $transaction->model_field_array(), 'payment (updated)' => $payment->model_field_array(), 'use_paypal_shipping' => $this->_paypal_shipping, 'use_paypal_tax' => $this->_paypal_taxes, 'grand_total_needed_resaving' => $grand_total_needs_resaving), $payment); }
/** * Updates the transaction and line items based on the payment IPN data from PayPal, * like the taxes or shipping * @param EEI_Payment $payment */ public function update_txn_based_on_payment($payment) { $update_info = $payment->details(); $redirect_args = $payment->redirect_args(); $transaction = $payment->transaction(); if (!$transaction) { $this->log(__('Payment with ID %d has no related transaction, and so update_txn_based_on_payment couldn\'t be executed properly', 'event_espresso'), $payment); return; } if (!is_array($update_info) || !isset($update_info['mc_shipping']) || !isset($update_info['tax'])) { $this->log(array('url' => $this->_process_response_url(), 'message' => __('Could not update transaction based on payment because the payment details have not yet been put on the payment. This normally happens during the IPN or returning from PayPal', 'event_espresso'), 'payment' => $payment->model_field_array()), $payment); return; } //take note of whether or not we COULD have allowed PayPal to add taxes and shipping //when we sent the customer to PayPal (because if we couldn't itemize the transaction, we //wouldn't have known what parts were taxable, meaning we would have had to tell PayPal //NONE of it was taxable otherwise it would re-add taxes each time a payment attempt occurred) // $could_allow_paypal_to_add_taxes_and_shipping = $this->_can_easily_itemize_transaction_for( $payment ); $grand_total_needs_resaving = FALSE; //might PayPal have added shipping? if ($this->_paypal_shipping && floatval($update_info['mc_shipping']) != 0) { $this->_line_item->add_unrelated_item($transaction->total_line_item(), __('Shipping', 'event_espresso'), floatval($update_info['mc_shipping']), __('Shipping charges calculated by Paypal', 'event_espresso'), 1, FALSE, 'paypal_shipping'); $grand_total_needs_resaving = TRUE; } //might PayPal have changed the taxes? if ($this->_paypal_taxes && floatval($update_info['tax']) != $redirect_args['tax_cart']) { $this->_line_item->set_total_tax_to($transaction->total_line_item(), floatval($update_info['tax']), __('Taxes', 'event_espresso'), __('Calculated by Paypal', 'event_espresso')); $grand_total_needs_resaving = TRUE; } if ($grand_total_needs_resaving) { $transaction->total_line_item()->save_this_and_descendants_to_txn($transaction->ID()); } $this->log(array('url' => $this->_process_response_url(), 'message' => __('Updated transaction related to payment', 'event_espresso'), 'transaction (updated)' => $transaction->model_field_array(), 'payment (updated)' => $payment->model_field_array(), 'use_paypal_shipping' => $this->_paypal_shipping, 'use_paypal_tax' => $this->_paypal_taxes, 'grand_total_needed_resaving' => $grand_total_needs_resaving), $payment); }
/** * Process the payment. * * @param EEI_Payment $payment * @param array $billing_info * @return EE_Payment|EEI_Payment */ public function do_direct_payment($payment, $billing_info = null) { if ($payment instanceof EEI_Payment) { $transaction = $payment->transaction(); if ($transaction instanceof EE_Transaction) { $order_description = sprintf(__("Event Registrations from %s", 'event_espresso'), get_bloginfo('name')); $primary_registrant = $transaction->primary_registration(); $events_list = array_unique(EEM_Event::instance()->get_col(array(array('Registration.TXN_ID' => $transaction->ID())), 'EVT_name')); // Itemized list. if ($this->_can_easily_itemize_transaction_for($payment)) { $item_num = 1; $total_line_item = $transaction->total_line_item(); $order_l_items = array(); foreach ($total_line_item->get_items() as $line_item) { $l_item = array('L_NAME' . $item_num => substr($line_item->name(), 0, 127), 'L_DESC' . $item_num => substr($line_item->desc(), 0, 127), 'L_COST' . $item_num => number_format($line_item->unit_price(), 2, '.', ''), 'L_QTY' . $item_num => $line_item->quantity(), 'L_TAXAMT' . $item_num => '', 'L_SKU' . $item_num => $item_num++); array_push($order_l_items, $l_item); } $item_amount = $total_line_item->get_items_total(); $tax_amount = $total_line_item->get_total_tax(); } else { $order_l_items = array(); $item_amount = $payment->amount(); $tax_amount = 0; array_push($order_l_items, array('L_NAME1' => sprintf(__("Payment for registration: %s", 'event_espresso'), $primary_registrant->reg_code()), 'L_DESC1' => sprintf(__("Payment of %s for %s", 'event_espresso'), $payment->amount(), $primary_registrant->reg_code()), 'L_COST1' => $payment->amount(), 'L_SKU1' => 1, 'L_QTY1' => 1)); } // Prep Authorization request parameters. $payment_request_args = array('TENDER' => 'C', 'TRXTYPE' => 'A', 'ACCT' => $billing_info['credit_card'], 'EXPDATE' => $billing_info['exp_month'] . $billing_info['exp_year'], 'CVV2' => $billing_info['card_cvv'], 'AMT' => number_format($payment->amount(), 2, '.', ''), 'CURRENCY' => $payment->currency_code(), 'BILLTOFIRSTNAME' => substr($billing_info['first_name'], 0, 30), 'BILLTOLASTNAME' => substr($billing_info['last_name'], 0, 30), 'SHIPTOFIRSTNAME' => substr($billing_info['first_name'], 0, 30), 'SHIPTOLASTNAME' => substr($billing_info['last_name'], 0, 30), 'BILLTOEMAIL' => $billing_info['email'], 'CUSTIP' => $_SERVER['REMOTE_ADDR'], 'BILLTOSTREET' => substr($billing_info['address'], 0, 30), 'BILLTOSTREET2' => substr($billing_info['address2'], 0, 30), 'BILLTOCITY' => substr($billing_info['city'], 0, 20), 'BILLTOSTATE' => substr($billing_info['state'], 0, 30), 'BILLTOZIP' => $billing_info['zip'], 'BILLTOCOUNTRY' => $billing_info['country'], 'SHIPTOSTREET' => substr($billing_info['address'], 0, 30), 'SHIPTOCITY' => substr($billing_info['city'], 0, 20), 'SHIPTOSTATE' => substr($billing_info['state'], 0, 30), 'SHIPTOZIP' => $billing_info['zip'], 'SHIPTOCOUNTRY' => $billing_info['country'], 'ORDERDESC' => $order_description, 'INVNUM' => substr($transaction->ID() . '_' . wp_generate_password(6, false), 0, 9), 'COMMENT1' => 'Reg Code: ' . $primary_registrant->reg_code(), 'COMMENT2' => implode(', ', $events_list)); // Include the items list. foreach ($order_l_items as $li_item) { foreach ($li_item as $li_ref => $item_val) { $payment_request_args[$li_ref] = $item_val; } } try { // Logging. $this->_log_clean_request($payment_request_args, $payment, 'Payflow Pro Authorization Request'); // Do a Authorization request. $authorization = $this->_request_to_payflow($payment_request_args, $transaction); $this->_log_clean_response($authorization, $payment, 'Payflow Pro Authorization Response'); if ($authorization['RESULT'] === '0' && $authorization['RESPMSG'] == 'Approved') { // Prep Delayed Capture request parameters. $capture_request_args['TRXTYPE'] = 'D'; $capture_request_args['ORIGID'] = $authorization['PNREF']; $capture_request_args['AMT'] = $payment_request_args['AMT']; $capture_request_args['CURRENCY'] = $payment_request_args['CURRENCY']; //$capture_request_args['VERBOSITY'] = 'HIGH'; // Logging. $this->_log_clean_request($capture_request_args, $payment, 'Payflow Pro Delayed Capture Request'); // Send Delayed Capture request. $delayed_capture = $this->_request_to_payflow($capture_request_args, $transaction); $this->_log_clean_response($delayed_capture, $payment, 'Payflow Pro Delayed Capture Response'); if ($delayed_capture['RESULT'] === '0' && $delayed_capture['RESPMSG'] == 'Approved') { $payment->set_status($this->_pay_model->approved_status()); if (isset($delayed_capture['AMT'])) { $payment->set_amount(floatval($delayed_capture['AMT'])); } $payment->set_gateway_response($delayed_capture['RESPMSG']); $payment->set_txn_id_chq_nmbr(isset($delayed_capture['PNREF']) ? $delayed_capture['PNREF'] : null); } else { $payment->set_status($this->_pay_model->declined_status()); $payment->set_gateway_response($delayed_capture['RESPMSG']); } } elseif ($authorization['RESULT'] === '126') { // Fraud Protection Services Filter — Flagged for review by filters, so lets set a rejected status. $payment->set_txn_id_chq_nmbr(isset($authorization['PNREF']) ? $authorization['PNREF'] : null); $payment->set_status($this->_pay_model->pending_status()); $payment->set_gateway_response($delayed_capture['RESPMSG']); $payment->set_extra_accntng(substr(__('Requires MANUAL APPROVAL in Event Espresso', 'event_espresso'), 0, 100)); } else { $payment->set_status($this->_pay_model->declined_status()); $payment->set_gateway_response($authorization['RESPMSG']); } } catch (Exception $e) { $payment->set_status($this->_pay_model->failed_status()); $payment->set_gateway_response($e->getMessage()); } } } return $payment; }