1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540 |
- <?php
- /**
- * @file
- * A module used for CyberSource's Silent Order POST
- * and Hosted Order Page methods of payment.
- *
- * Development sponsored by:
- * Acquia - http://acquia.com
- *
- * Harvard Magazine Classifieds through Growing Venture Solutions
- * https://classifieds.harvardmagazine.com/
- * http://growingventuresolutions.com
- */
- /**
- * Implements hook_menu().
- */
- function uc_cybersource_menu() {
- $items['cybersource/hop-post'] = array(
- 'title' => 'Payment received',
- 'page callback' => 'uc_cybersource_hop_post',
- 'access callback' => TRUE,
- 'type' => MENU_CALLBACK,
- );
- // Callback functions for Website Payments Standard.
- $items['cybersource/hop-complete/%uc_order'] = array(
- 'title' => 'CyberSource payment complete',
- 'page callback' => 'uc_cybersource_hop_complete',
- 'page arguments' => array(2),
- 'access arguments' => array('access content'),
- 'type' => MENU_CALLBACK,
- );
- $items['admin/store/orders/%uc_order/cs_tax'] = array(
- 'title' => 'Order Taxes',
- 'page callback' => 'uc_cybersource_tax_test',
- 'page arguments' => array(3),
- 'access arguments' => array('administer store'),
- 'type' => MENU_CALLBACK,
- );
- return $items;
- }
- /**
- * Implements hook_page_alter().
- */
- function uc_cybersource_page_alter(&$page) {
- $block = block_load('system', 'main');
- // Add to the review page hidden form fields with data to post to
- // CyberSource HOP.
- if (isset($page[$block->region]['system_main']['#theme']) && $page[$block->region]['system_main']['#theme'] == 'uc_cart_checkout_review' && ($order_id = intval($_SESSION['cart_order'])) > 0) {
- $order = uc_order_load($order_id);
- if ($order->payment_method == 'cybersource_hop') {
- $page[$block->region]['system_main']['#form'] = drupal_get_form('uc_cybersource_hop_form', $order);
- }
- }
- }
- /**
- * Implements hook_form_FORM_ID_alter() for uc_payment_method_settings_form().
- */
- function uc_cybersource_form_uc_payment_method_settings_form_alter(&$form, &$form_state) {
- if ($form_state['build_info']['args'][0] == 'credit') {
- $form['#submit'][] = 'uc_cybersource_payment_gateway_settings_submit';
- }
- }
- /**
- * Submit handler for payment gateway settings form to encrypt fields.
- */
- function uc_cybersource_payment_gateway_settings_submit($form, &$form_state) {
- // If CC encryption has been configured properly.
- if ($key = uc_credit_encryption_key()) {
- // Setup our encryption object.
- $crypt = new UbercartEncryption();
- // Encrypt the Merchant ID and Transaction key.
- if (!empty($form_state['values']['uc_cybersource_soap_merchant_id'])) {
- variable_set('uc_cybersource_soap_merchant_id', $crypt->encrypt($key, $form_state['values']['uc_cybersource_soap_merchant_id']));
- }
- if (!empty($form_state['values']['uc_cybersource_soap_transaction_key'])) {
- variable_set('uc_cybersource_soap_transaction_key', $crypt->encrypt($key, $form_state['values']['uc_cybersource_soap_transaction_key']));
- }
- // Store any errors.
- uc_store_encryption_errors($crypt, 'uc_cybersource');
- }
- }
- /*******************************************************************************
- * Hook Functions (Ubercart)
- ******************************************************************************/
- /**
- * Implements hook_uc_payment_gateway().
- */
- function uc_cybersource_uc_payment_gateway() {
- // CyberSource APIs other than HOP require uc_credit to be enabled.
- if (!module_exists('uc_credit')) {
- return;
- }
- $gateways['cybersource'] = array(
- 'title' => t('CyberSource Silent Order POST'),
- 'description' => t('Process credit card payments using the Silent Order POST service of CyberSource.'),
- 'settings' => 'uc_cybersource_settings_form',
- 'credit' => 'uc_cybersource_charge',
- 'credit_txn_types' => array(UC_CREDIT_AUTH_ONLY, UC_CREDIT_PRIOR_AUTH_CAPTURE, UC_CREDIT_AUTH_CAPTURE, UC_CREDIT_REFERENCE_TXN),
- );
- return $gateways;
- }
- /**
- * Processes a payment POST from the CyberSource Hosted Order Page API.
- */
- function uc_cybersource_hop_post() {
- if (!uc_cybersource_hop_include()) {
- watchdog('uc_cybersource_hop', 'Unable to receive HOP POST due to missing or unreadable HOP.php file.', array(), 'error');
- drupal_add_http_header('Status', '503 Service unavailable');
- drupal_set_title(t('Unable to receive HOP POST.'));
- print t('The site was unable to receive a HOP post because of a missing or unreadble HOP.php');
- exit();
- }
- $verify = VerifyTransactionSignature($_POST);
- watchdog('uc_cybersource_hop', 'Receiving payment notification at URL for order @orderNumber',
- array('@orderNumber' => $_POST['orderNumber'] ));
- if (!isset($_POST['orderNumber'])) {
- watchdog('uc_cybersource_hop', 'CS HOP attempted with invalid order number.', array(), WATCHDOG_ERROR);
- return;
- }
- if (!$verify) {
- watchdog('uc_cybersource_hop', 'Receiving invalid payment notification at URL for order @orderNumber. <pre>@debug</pre>',
- array('@orderNumber' => $_POST['orderNumber'], '@debug' => print_r($_POST, TRUE) ));
- return;
- }
- // Assign posted variables to local variables.
- $decision = check_plain($_POST['decision']);
- $reason_code = check_plain($_POST['reasonCode']);
- $reason = _parse_cs_reason_code($reason_code);
- $payment_amount = check_plain($_POST['orderAmount']);
- $payment_currency = check_plain($_POST['paymentCurrency']);
- $request_id = check_plain($_POST['requestID']);
- $request_token = check_plain($_POST['orderPage_requestToken']);
- $reconciliation_id = check_plain($_POST['reconciliationID']);
- $order_id = check_plain($_POST['orderNumber']);
- $payer_email = check_plain($_POST['billTo_email']);
- $order = uc_order_load($_POST['orderNumber']);
- switch ($decision) {
- case 'ACCEPT':
- watchdog('uc_cybersource_hop', 'CyberSource verified successful payment.');
- $duplicate = (bool) db_query_range('SELECT 1 FROM {uc_payment_cybersource_hop_post} WHERE order_id = :order_id AND decision = :decision', 0, 1, array(':order_id' => $order_id, ':decision' => 'ACCEPT'))->fetchField();
- if ($duplicate) {
- watchdog('uc_cybersource_hop', 'CS HOP transaction for order @order-id has been processed before.', array('@order_id' => $order_id), WATCHDOG_NOTICE);
- return;
- }
- db_insert('uc_payment_cybersource_hop_post')
- ->fields(array(
- 'order_id' => $order_id,
- 'request_id' => $request_id,
- 'request_token' => $request_token,
- 'reconciliation_id' => $reconciliation_id,
- 'gross' => $payment_amount,
- 'decision' => $decision,
- 'reason_code' => $reason_code,
- 'payer_email' => $payer_email,
- 'received' => REQUEST_TIME,
- ))
- ->execute();
- $comment = t('CyberSource request ID: @txn_id', array('@txn_id' => $request_id));
- uc_payment_enter($order_id, 'cybersource_hop', $payment_amount, $order->uid, NULL, $comment);
- uc_cart_complete_sale($order);
- uc_order_comment_save($order_id, 0, t('Payment of @amount @currency submitted through CyberSource with request ID @rid.', array('@amount' => $payment_amount, '@currency' => $payment_currency, '@rid' => $request_id)), 'order', 'payment_received');
- break;
- case 'ERROR':
- uc_order_comment_save($order_id, 0, t("Payment error:@reason with request ID @rid", array('@reason' => $reason, '@rid' => '@request_id')), 'admin');
- break;
- case 'REJECT':
- uc_order_comment_save($order_id, 0, t("Payment is rejected:@reason with request ID @rid", array('@reason' => $reason, '@rid' => '@request_id')), 'admin');
- break;
- case 'REVIEW':
- uc_order_update_status($order_id, 'review');
- uc_order_comment_save($order_id, 0, t('Payment is in review & not complete: @reason. Request ID @rid', array('@reason' => $reason, '@rid' => '@request_id')), 'admin');
- break;
- }
- }
- /**
- * Checks for HOP.php and includes it or returns FALSE if it cannot be found.
- */
- function uc_cybersource_hop_include() {
- $hop_paths[0] = 'sites/all/libraries/uc_cybersource/HOP.php';
- $hop_paths[1] = drupal_get_path('module', 'uc_cybersource') . '/HOP.php';
- // Loop through possible paths, include and return TRUE when HOP.php is
- // located.
- foreach ($hop_paths as $key => $path) {
- if (file_exists($path)) {
- require_once($path);
- return TRUE;
- }
- }
- // We didn't find HOP.php in any of the possible paths.
- return FALSE;
- }
- /**
- * Adds the CyberSource fields to the payment gateway settings form.
- */
- function uc_cybersource_settings_form($form, &$form_state) {
- // Check for the HOP.php for Silent Order POST.
- if (variable_get('uc_cybersource_method', 'post') == 'post' &&
- !uc_cybersource_hop_include()) {
- drupal_set_message(t('You must download the security script from your CyberSource account (found in Tools & Settings > Hosted Order Page > Security) and place it in the ubercart/payment/uc_cybersource directory to use the Silent Order POST. Remember to open it and replace instances of L( with csL(.'), 'error');
- }
- $form['uc_cybersource_server'] = array(
- '#type' => 'select',
- '#title' => t('Payment server'),
- '#description' => t('CyberSource server used when processing payments.'),
- '#options' => array(
- 'production' => t('Production'),
- 'test' => t('Test'),
- ),
- '#default_value' => variable_get('uc_cybersource_server', 'test'),
- );
- $form['uc_cybersource_method'] = array(
- '#type' => 'radios',
- '#title' => t('Payment method'),
- '#description' => t('You must ensure your CyberSource account and web server are able to use the service you select.<br />Silent Order POST requires cURL support and a modified <a href="!url">HOP.php</a>.<br />The SOAP Toolkit API requires the SOAP and DOM extensions for PHP.', array('!url' => url('http://www.ubercart.org/contrib/139', array('absolute' => TRUE)))),
- '#options' => array(
- 'post' => t('Silent Order POST'),
- // 'api' => t('Simple Order API'),
- 'soap' => t('SOAP Toolkit API'),
- ),
- '#default_value' => variable_get('uc_cybersource_method', 'post'),
- );
- $form['uc_cybersource_avs'] = array(
- '#type' => 'radios',
- '#title' => t('Ensure address verification'),
- '#options' => array(
- 'true' => t('Process transaction only if address passes verification.'),
- 'false' => t('Process transaction regardless of the result of address verification.'),
- ),
- '#default_value' => variable_get('uc_cybersource_avs', 'true'),
- );
- $login = _uc_cybersource_soap_login_data();
- $form['soap'] = array(
- '#type' => 'fieldset',
- '#title' => t('SOAP Toolkit API settings'),
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
- );
- $form['soap']['uc_cybersource_soap_merchant_id'] = array(
- '#type' => 'textfield',
- '#title' => t('Merchant ID'),
- '#default_value' => $login['merchant_id'],
- );
- $form['soap']['uc_cybersource_soap_transaction_key'] = array(
- '#type' => 'textarea',
- '#title' => t('Transaction key'),
- '#default_value' => $login['transaction_key'],
- );
- $form['soap']['uc_cybersource_soap_create_profile'] = array(
- '#type' => 'checkbox',
- '#title' => t('Create a CyberSource Basic Profile for every new credit card order processed.'),
- '#default_value' => variable_get('uc_cybersource_soap_create_profile', FALSE),
- );
- $form['soap']['uc_cybersource_soap_tax_calculate'] = array(
- '#type' => 'checkbox',
- '#title' => t('Enable calculation of taxes through the CyberSource tax service.'),
- '#default_value' => variable_get('uc_cybersource_soap_tax_calculate', FALSE),
- );
- $form['soap']['ship_from'] = array(
- '#type' => 'fieldset',
- '#title' => t('Tax calculation "Ship from" address'),
- '#description' => t('This address will be used when calculating taxes with CyberSource tax service.'),
- );
- $form['soap']['ship_from']['cs_ship_from_first_name'] = array(
- '#type' => 'textfield',
- '#title' => t('First name'),
- '#default_value' => variable_get('cs_ship_from_first_name', ''),
- );
- $form['soap']['ship_from']['cs_ship_from_last_name'] = array(
- '#type' => 'textfield',
- '#title' => t('Last name'),
- '#default_value' => variable_get('cs_ship_from_last_name', ''),
- );
- $form['soap']['ship_from']['cs_ship_from_street1'] = array(
- '#type' => 'textfield',
- '#title' => t('Street address'),
- '#default_value' => variable_get('cs_ship_from_street1', ''),
- );
- $form['soap']['ship_from']['cs_ship_from_city'] = array(
- '#type' => 'textfield',
- '#title' => t('City'),
- '#default_value' => variable_get('cs_ship_from_city', ''),
- );
- $form['soap']['ship_from']['cs_ship_from_zone'] = array(
- '#type' => 'textfield',
- '#title' => t('State/Province'),
- '#description' => t('Enter the 2 letter abbreviation of your state or province.'),
- '#default_value' => variable_get('cs_ship_from_zone', ''),
- '#maxlength' => 2,
- );
- $form['soap']['ship_from']['cs_ship_from_postal_code'] = array(
- '#type' => 'textfield',
- '#title' => t('ZIP/Postal code'),
- '#default_value' => variable_get('cs_ship_from_postal_code', ''),
- );
- $form['soap']['ship_from']['cs_ship_from_country'] = array(
- '#type' => 'textfield',
- '#title' => t('Country code'),
- '#description' => t("Enter the 2 letter ISO 3166-1 code; consult Wikipedia if you don't know yours."),
- '#default_value' => variable_get('cs_ship_from_country', ''),
- '#maxlength' => 2,
- );
- $form['soap']['ship_from']['cs_ship_from_email'] = array(
- '#type' => 'textfield',
- '#title' => t('E-mail address'),
- '#default_value' => variable_get('cs_ship_from_email', ''),
- );
- return $form;
- }
- /**
- * Defines payment method properties.
- *
- * @return
- * An array with property and value pairs of CyberSource payment method.
- */
- function uc_cybersource_payment_method() {
- $methods[] = array(
- 'id' => 'cybersource_hop',
- 'name' => t('CyberSource Hosted Order Page'),
- 'title' => "Credit/Debit card payment processed by CyberSource",
- 'review' => t('Credit/Debit card payment processed by CyberSource'),
- 'desc' => t('Payment with CyberSource HOP Service.'),
- 'callback' => 'uc_payment_method_cybersource_hop',
- 'weight' => 1,
- 'checkout' => FALSE,
- 'no_gateway' => TRUE,
- );
- return $methods;
- }
- /**
- * Payment method callback.
- */
- function uc_payment_method_cybersource_hop($op, &$order) {
- if ($op == 'settings') {
- $form['uc_cybersource_hop_server'] = array(
- '#type' => 'select',
- '#title' => t('CyberSource HOP server'),
- '#description' => t('Select between production/live or test mode.'),
- '#options' => array(
- 'https://orderpagetest.ic3.com/hop/orderform.jsp' => t('Test Center'),
- 'https://orderpage.ic3.com/hop/orderform.jsp' => t('Production/Live')),
- '#default_value' => variable_get('uc_cybersource_hop_server', 'https://orderpagetest.ic3.com/hop/orderform.jsp'),
- );
- $form['uc_cybersource_hop_transaction_type'] = array(
- '#type' => 'radios',
- '#title' => t('CyberSource transaction type'),
- '#description' => t('Authorize and settle, or authorize only for capture on CyberSource.com'),
- '#options' => array(
- 'authorization' => t('Authorize only'),
- 'sale' => t('Authorize and capture'),
- ),
- '#default_value' => variable_get('uc_cybersource_hop_transaction_type', 'sale'),
- );
- $form['uc_cybersource_cs_hop_button_text'] = array(
- '#type' => 'textfield',
- '#title' => t('CyberSource "Buy button" text'),
- '#description' => t('This text appears on the button users press to process their payment on the Hosted Order Page.'),
- '#default_value' => variable_get('uc_cybersource_cs_hop_button_text', t('Process payment')),
- );
- return $form;
- }
- }
- /**
- * Finalizes CyberSource transaction.
- */
- function uc_cybersource_hop_complete($order) {
- // If the order ID specified in the return URL is not the same as the one in
- // the user's session, we need to assume this is either a spoof or that the
- // user tried to adjust the order on this side while at PayPal. If it was a
- // legitimate checkout, the CyberSource POST will still register, so the
- // gets processed correctly. We'll leave an ambiguous message just in case.
- if (intval($_SESSION['cart_order']) != $order->order_id) {
- drupal_set_message(t('Thank you for your order! We will be notified by CyberSource that we have received your payment.'));
- drupal_goto('cart');
- }
- // This lets us know it's a legitimate access of the complete page.
- $_SESSION['uc_checkout'][$_SESSION['cart_order']]['do_complete'] = TRUE;
- drupal_goto('cart/checkout/complete');
- }
- /**
- * Defines values to be posted to CyberSource.
- *
- * @return
- * Transaction data arrays are returned as hidden form values.
- */
- function uc_cybersource_hop_form($form, $form_state, $order) {
- if (!uc_cybersource_hop_include()) {
- drupal_set_message(t('Hosted Order Page requires the HOP.php provided by CyberSource.'));
- // TODO - Does returning false here make sense?
- return array('success' => FALSE);
- }
- $billing_country = uc_get_country_data(array('country_id' => $order->billing_country));
- $delivery_country = uc_get_country_data(array('country_id' => $order->delivery_country));
- $data = array(
- 'billTo_firstName' => $order->billing_first_name,
- 'billTo_lastName' => $order->billing_last_name,
- 'billTo_street1' => $order->billing_street1,
- 'billTo_city' => $order->billing_city,
- 'billTo_country' => $billing_country[0]['country_iso_code_2'],
- 'billTo_state' => uc_get_zone_code($order->billing_zone),
- 'billTo_postalCode' => $order->billing_postal_code,
- 'billTo_email' => $order->primary_email,
- 'billTo_phoneNumber' => $order->billing_phone,
- );
- if (uc_order_is_shippable($order)) {
- $data += array(
- 'shipTo_firstName' => $order->delivery_first_name,
- 'shipTo_lastName' => $order->delivery_last_name,
- 'shipTo_street1' => $order->delivery_street1,
- 'shipTo_street2' => $order->delivery_street2,
- 'shipTo_city' => $order->delivery_city,
- 'shipTo_country' => $delivery_country[0]['country_iso_code_2'],
- 'shipTo_state' => uc_get_zone_code($order->delivery_zone),
- 'shipTo_postalCode' => $order->delivery_postal_code,
- );
- }
- $shipping = 0;
- foreach ($order->line_items as $item) {
- if ($item['type'] == 'shipping') {
- $shipping += $item['amount'];
- }
- }
- $tax = 0;
- if (module_exists('uc_taxes')) {
- foreach (uc_taxes_calculate($order) as $tax_item) {
- $tax += $tax_item->amount;
- }
- }
- $amount = $order->order_total - $shipping - $tax;
- $currency = variable_get('uc_cybersource_hop_currency', 'USD');
- $merchantID = getMerchantID();
- $timestamp = getmicrotime();
- $datax = $merchantID . $amount . $currency . $timestamp;
- $pub = function_exists('getSharedSecret') ? getSharedSecret() : getPublicKey();
- $serialNumber = getSerialNumber();
- $pub_digest = hopHash($datax, $pub);
- $data['amount'] = $amount;
- $data['currency'] = $currency;
- $data['merchantID'] = $merchantID;
- $data['orderNumber'] = $order->order_id;
- $data['orderPage_timestamp'] = $timestamp;
- $data['orderPage_ignoreAVS'] = variable_get('uc_cybersource_hop_avs', 'true') == 'true' ? 'false' : 'true';
- $data['orderPage_signaturePublic'] = $pub_digest;
- $data['orderPage_version'] = '4';
- $data['orderPage_serialNumber'] = $serialNumber;
- $data['orderPage_transactionType'] = variable_get('uc_cybersource_hop_transaction_type', 'sale');
- $data['orderPage_sendMerchantReceiptEmail'] = variable_get('uc_cybersource_hop_merchant_receipt_email', 'true');
- $data['orderPage_sendMerchantURLPost'] = 'true';
- // CyberSource posts payment confirmation to this URL.
- $data['orderPage_merchantURLPostAddress'] = url('cybersource/hop-post', array('absolute' => TRUE));
- $data['orderPage_buyButtonText'] = t('Checkout');
- $receipt_url = url('cybersource/hop-complete/' . $order->order_id, array('absolute' => TRUE));
- $data['orderPage_receiptResponseURL'] = $receipt_url;
- $data['orderPage_buyButtonText'] = variable_get('uc_cybersource_cs_hop_button_text', t('Process payment'));
- $comments = t('Order @order-id at @store-name', array('@order-id' => $order->order_id, '@store-name' => uc_store_name()));
- $alter_data['order'] = $order;
- $alter_data['comments'] = $comments;
- $alter_data['merchant_fields'] = array();
- // Allow other modules to alter the comment & merchant field data stored
- // with CyberSource.
- drupal_alter('uc_cybersource_data', $alter_data);
- $data['comments'] = $alter_data['comments'];
- if (!empty($alter_data['merchant_fields'])) {
- foreach ($alter_data['merchant_fields'] as $key => $value) {
- $data[$key] = $value;
- }
- }
- foreach ($data as $name => $value) {
- if (!empty($value)) {
- $form[$name] = array('#type' => 'hidden', '#value' => $value);
- }
- }
- $form['#action'] = variable_get('uc_cybersource_hop_server', 'https://orderpagetest.ic3.com/hop/orderform.jsp');
- $form['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Submit order'),
- );
- return $form;
- }
- /**
- * Charges card.
- */
- function uc_cybersource_charge($order_id, $amount, $data) {
- global $user;
- $order = uc_order_load($order_id);
- $amount = uc_currency_format($amount, FALSE, FALSE, '.');
- $cc_type = NULL;
- if (isset($order->payment_details['cc_type'])) {
- switch (strtolower($order->payment_details['cc_type'])) {
- case 'amex':
- case 'american express':
- $cc_type = '003';
- break;
- case 'visa':
- $cc_type = '001';
- break;
- case 'mastercard':
- case 'master card':
- $cc_type = '002';
- break;
- case 'discover':
- $cc_type = '004';
- break;
- }
- }
- if (is_null($cc_type)) {
- $cc_type = _uc_cybersource_card_type($order->payment_details['cc_number']);
- if ($cc_type === FALSE && in_array($data['txn_type'], array(UC_CREDIT_AUTH_ONLY, UC_CREDIT_AUTH_CAPTURE))) {
- drupal_set_message(t('The credit card type did not pass validation.'), 'error');
- watchdog('uc_cybersource', 'Could not figure out cc type: @number / @type', array('@number' => $order->payment_details['cc_number'], '@type' => $order->payment_details['cc_type']), WATCHDOG_ERROR);
- return array('success' => FALSE);
- }
- }
- $country = uc_get_country_data(array('country_id' => $order->billing_country));
- if ($country === FALSE) {
- $country = array(0 => array('country_iso_code_2' => 'US'));
- }
- // Process the charge differently depending on the CyberSource method.
- switch (variable_get('uc_cybersource_method', 'post')) {
- // Support for the Silent Order POST.
- case 'post':
- return _uc_cybersource_post_charge($order, $amount, $data, $cc_type, $country);
- // Support for the SOAP Toolkit API.
- case 'soap':
- // TODO: Refactor to use separate function for each API type.
- // - i.e. _uc_cybersource_charge_request_soap($order, $amount, $data);
- // require_once(drupal_get_path('module', 'uc_cybersource') . '/SOAP.php');
- return _uc_cybersource_soap_charge($order, $amount, $data, $cc_type, $country);
- case 'api':
- $config = cybs_load_config('cybs.ini');
- if (variable_get('uc_cybersource_server', 'test') == 'test') {
- $config['sendToProduction'] = 'false';
- }
- $request['ccAuthService_run'] = 'true';
- if (variable_get('uc_cybersource_hop_transaction_type', 'sale') == 'sale') {
- $request['ccCaptureService_run'] = 'true';
- }
- $request['merchantReferenceCode'] = $order_id;
- $request['purchaseTotals_currency'] = 'USD';
- $request['purchaseTotals_grandTotalAmount'] = $amount;
- drupal_set_message('<pre>' . print_r($config, TRUE) . '</pre>');
- drupal_set_message('<pre>' . print_r($request, TRUE) . '</pre>');
- break;
- }
- }
- /**
- * POSTs transaction to CyberSource.
- */
- function _uc_cybersource_post_charge($order, $amount, $data, $cc_type, $country) {
- // Include the HOP.php per the module instructions.
- if (!uc_cybersource_hop_include()) {
- drupal_set_message(t('Silent Order POST requires the HOP.php provided by CyberSource.'));
- return array('success' => FALSE);
- }
- $request = array(
- 'billTo_firstName' => $order->billing_first_name,
- 'billTo_lastName' => $order->billing_last_name,
- 'billTo_street1' => $order->billing_street1,
- 'billTo_city' => $order->billing_city,
- 'billTo_country' => $country[0]['country_iso_code_2'],
- 'billTo_state' => uc_get_zone_code($order->billing_zone),
- 'billTo_postalCode' => $order->billing_postal_code,
- 'billTo_email' => $order->primary_email,
- 'card_accountNumber' => $order->payment_details['cc_number'],
- 'card_cardType' => $cc_type,
- 'card_expirationMonth' => $order->payment_details['cc_exp_month'],
- 'card_expirationYear' => $order->payment_details['cc_exp_year'],
- );
- if (variable_get('uc_credit_cvv_enabled', TRUE)) {
- $request['card_cvNumber'] = $order->payment_details['cc_cvv'];
- }
- $currency = variable_get('uc_cybersource_currency', 'usd');
- $merchantID = getMerchantID();
- $timestamp = getmicrotime();
- $data = $merchantID . $amount . $currency . $timestamp;
- $pub = function_exists('getSharedSecret') ? getSharedSecret() : getPublicKey();
- $serialNumber = getSerialNumber();
- $pub_digest = hopHash($data, $pub);
- $request['amount'] = $amount;
- $request['currency'] = $currency;
- $request['merchantID'] = $merchantID;
- $request['orderNumber'] = $order->order_id;
- $request['orderPage_timestamp'] = $timestamp;
- $request['orderPage_ignoreAVS'] = variable_get('uc_cybersource_avs', 'true') == 'true' ? 'false' : 'true';
- $request['orderPage_signaturePublic'] = $pub_digest;
- $request['orderPage_version'] = '4';
- $request['orderPage_serialNumber'] = $serialNumber;
- $request['orderPage_transactionType'] = variable_get('uc_cybersource_hop_transaction_type', 'sale');
- $data = '';
- while (list($key, $value) = each($request)) {
- $data .= $key . '=' . urlencode(str_replace(',', '', $value)) . '&';
- }
- $data = substr($data, 0, -1);
- if (variable_get('uc_cybersource_server', 'test') == 'test') {
- $url = 'https://orderpagetest.ic3.com/hop/ProcessOrder.do';
- }
- else {
- $url = 'https://orderpage.ic3.com/hop/ProcessOrder.do';
- }
- $ch = curl_init();
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_VERBOSE, 0);
- curl_setopt($ch, CURLOPT_POST, 1);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
- curl_setopt($ch, CURLOPT_NOPROGRESS, 1);
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
- $response = curl_exec($ch);
- if ($error = curl_error($ch)) {
- watchdog('uc_cybersource', '@error', array('@error' => $error), WATCHDOG_ERROR);
- }
- curl_close($ch);
- if (preg_match_all('`name=".+" value=".+"`', $response, $pairs) > 0) {
- for ($i = 0; $i < count($pairs[0]); $i++) {
- list($name, $value) = explode('" value="', substr($pairs[0][$i], 6, strlen($pairs[0][$i]) - 7));
- $nvp[$name] = $value;
- }
- // Create the order and payment ledger comments.
- $o_comment = t('<b>Credit card !type:</b> !amount<br /><b>Decision: @decision</b><br /><b>Reason:</b> !reason', array('!type' => variable_get('uc_cybersource_hop_transaction_type', 'sale'), '!amount' => uc_currency_format($nvp['orderAmount']), '@decision' => $nvp['decision'], '!reason' => _uc_cybersource_parse_reason_code($nvp['reasonCode'])));
- $p_comment = t('!id<br />!decision, Reason: !reason', array('!id' => $nvp['orderPage_serialNumber'], '!decision' => $nvp['decision'], '!reason' => $nvp['reasonCode']));
- if (!empty($nvp['ccAuthReply_avsCode'])) {
- $o_comment .= t('<br /><b>AVS:</b> !avs', array('!avs' => _uc_cybersource_parse_avs_code($nvp['ccAuthReply_avsCode'])));
- $p_comment .= t(', AVS: @avs', array('@avs' => $nvp['ccAuthReply_avsCode']));
- }
- if (!empty($nvp['ccAuthReply_cvCode'])) {
- $o_comment .= t('<br /><b>CVV:</b> !cvv', array('!cvv' => _uc_cybersource_parse_cvv_code($nvp['ccAuthReply_cvCode'])));
- $p_comment .= t(', CVV: @cvv', array('@cvv' => $nvp['ccAuthReply_cvCode']));
- }
- uc_order_comment_save($order->order_id, $user->uid, $o_comment, 'admin');
- if ($nvp['decision'] == 'ACCEPT') {
- $result = array(
- 'success' => TRUE,
- 'comment' => $p_comment,
- 'message' => $o_comment,
- 'uid' => $user->uid,
- );
- }
- else {
- $result = array(
- 'success' => FALSE,
- 'comment' => $p_comment,
- 'message' => $o_comment,
- 'uid' => $user->uid,
- );
- }
- }
- else {
- $result = array(
- 'success' => FALSE,
- 'message' => t('No response returned from CyberSource.'),
- );
- }
- return $result;
- }
- /**
- * Handles the SOAP charge request and Ubercart order save.
- */
- function _uc_cybersource_soap_charge($order, $amount, $data, $cc_type, $country) {
- // Include the SOAP helper file.
- module_load_include('inc', 'uc_cybersource', 'uc_cybersource.soap');
- global $user;
- // Set the URL for the CyberSource SOAP Toolkit API WSDL.
- if (variable_get('uc_cybersource_server', 'test') == 'test') {
- $url = 'https://ics2wstest.ic3.com/commerce/1.x/transactionProcessor/CyberSourceTransaction_1.38.wsdl';
- }
- else {
- $url = 'https://ics2ws.ic3.com/commerce/1.x/transactionProcessor/CyberSourceTransaction_1.38.wsdl';
- }
- // Variable currency... not used at the moment.
- $currency = variable_get('uc_cybersource_currency', 'usd');
- $billing_country = uc_get_country_data(array('country_id' => $order->billing_country));
- $delivery_country = uc_get_country_data(array('country_id' => $order->delivery_country));
- try {
- $soapClient = new CyberSourceSoapClient($url, array());
- // To see the functions and types that the SOAP extension can automatically
- // generate from the WSDL file, uncomment this section and check the logs.
- // $functions = $soapClient->__getFunctions();
- // watchdog('uc_cybersource', '<pre>' . print_r($functions, TRUE) . '</pre>');
- // $types = $soapClient->__getTypes();
- // watchdog('uc_cybersource', '<pre>' . print_r($types, TRUE) . '</pre>');
- $login = _uc_cybersource_soap_login_data();
- // Create the request with some meta data.
- $request = new stdClass();
- $request->merchantID = $login['merchant_id'];
- $request->merchantReferenceCode = $order->order_id;
- $request->clientLibrary = 'PHP';
- $request->clientLibraryVersion = phpversion();
- $request->clientEnvironment = php_uname();
- // Add the credit card authorization service.
- if (in_array($data['txn_type'], array(UC_CREDIT_AUTH_ONLY, UC_CREDIT_AUTH_CAPTURE, UC_CREDIT_REFERENCE_TXN))) {
- $ccAuthService = new stdClass();
- $ccAuthService->run = 'true';
- $request->ccAuthService = $ccAuthService;
- }
- // Add the credit card capture service.
- if (in_array($data['txn_type'], array(UC_CREDIT_PRIOR_AUTH_CAPTURE, UC_CREDIT_AUTH_CAPTURE, UC_CREDIT_REFERENCE_TXN))) {
- $ccCaptureService = new stdClass();
- $ccCaptureService->run = 'true';
- // Add the values for prior authorization capture.
- if ($data['txn_type'] == UC_CREDIT_PRIOR_AUTH_CAPTURE) {
- $ccCaptureService->authRequestID = $data['auth_id'];
- $ccCaptureService->authRequestToken = $order->data['cybersource'][$data['auth_id']];
- }
- $request->ccCaptureService = $ccCaptureService;
- // Add the subscription ID for a reference transaction.
- if ($data['txn_type'] == UC_CREDIT_REFERENCE_TXN) {
- $recurringSubscriptionInfo = new stdClass();
- $recurringSubscriptionInfo->subscriptionID = $data['ref_id'];
- $request->recurringSubscriptionInfo = $recurringSubscriptionInfo;
- $request->merchantReferenceCode .= ' (COF)';
- }
- }
- // If enabled, create a subscription profile for this transaction.
- if (variable_get('uc_cybersource_soap_create_profile', FALSE) && in_array($data['txn_type'], array(UC_CREDIT_AUTH_ONLY, UC_CREDIT_AUTH_CAPTURE))) {
- // Skip if a profile already exists for this order.
- if (!isset($order->data['uc_cybersource']['soap']['subscription_id'])) {
- $recurringSubscriptionInfo = new stdClass();
- $recurringSubscriptionInfo->amount = 0;
- $recurringSubscriptionInfo->frequency = 'on-demand';
- $request->recurringSubscriptionInfo = $recurringSubscriptionInfo;
- $paySubscriptionCreateService = new stdClass();
- $paySubscriptionCreateService->run = 'true';
- $request->paySubscriptionCreateService = $paySubscriptionCreateService;
- }
- }
- // Add the billing information.
- $billTo = new stdClass();
- $billTo->firstName = $order->billing_first_name;
- $billTo->lastName = $order->billing_last_name;
- $billTo->street1 = $order->billing_street1;
- if ($order->billing_street2) {
- $billTo->street2 = $order->billing_street2;
- }
- $billTo->city = $order->billing_city;
- $billTo->state = uc_get_zone_code($order->billing_zone);
- $billTo->postalCode = $order->billing_postal_code;
- $billTo->country = $billing_country[0]['country_iso_code_2'];
- if ($order->billing_phone) {
- $billTo->phoneNumber = $order->billing_phone;
- }
- $billTo->email = $order->primary_email;
- $billTo->customerID = $order->uid;
- $request->billTo = $billTo;
- // Add the credit card details if needed.
- if (in_array($data['txn_type'], array(UC_CREDIT_AUTH_ONLY, UC_CREDIT_AUTH_CAPTURE))) {
- $card = new stdClass();
- $card->accountNumber = $order->payment_details['cc_number'];
- $card->expirationMonth = $order->payment_details['cc_exp_month'];
- $card->expirationYear = $order->payment_details['cc_exp_year'];
- $card->cardType = $cc_type;
- if (variable_get('uc_credit_cvv_enabled', TRUE)) {
- $card->cvNumber = $order->payment_details['cc_cvv'];
- }
- $request->card = $card;
- }
- // Add the order total information.
- $purchaseTotals = new stdClass();
- $purchaseTotals->currency = $currency;
- $purchaseTotals->grandTotalAmount = $amount;
- $request->purchaseTotals = $purchaseTotals;
- // Separately add products and line item into the request items object if
- // we're charging the full order total.
- if (round($amount, 2) == round($order->order_total, 2)) {
- $request->item = array();
- $counter = 0;
- // Add the products to the item array.
- foreach ($order->products as $product) {
- $obj = $request->item[] = new stdClass();
- $obj->productName = $product->title;
- $obj->unitPrice = $product->price;
- $obj->quantity = $product->qty;
- $obj->productSKU = $product->model;
- $obj->productCode = 'default';
- $obj->id = $counter;
- $counter++;
- }
- // Add the line items to the item array.
- $discount_amount = 0;
- foreach ((array) $order->line_items as $line_item) {
- // Handle negative line items.
- if ($line_item['amount'] < 0) {
- $discount_amount += -$line_item['amount'];
- }
- // Skip subtotal line items.
- elseif (strpos($line_item['type'], 'subtotal') === FALSE) {
- $obj = $request->item[] = new stdClass();
- $obj->productName = $line_item['title'];
- $obj->unitPrice = $line_item['amount'];
- $obj->quantity = 1;
- $obj->productSKU = $line_item['type'] . '_' . $line_item['line_item_id'];
- $obj->id = $counter;
- $counter++;
- }
- }
- }
- // Add the total order discount into the request.
- if ($discount_amount != 0) {
- $request->purchaseTotals->discountAmount = $discount_amount;
- }
- // Add business rules.
- $business = new stdClass();
- $business->ignoreAVSResult = variable_get('uc_cybersource_avs', 'true') == 'true' ? 'false' : 'true';
- $request->businessRules = $business;
- // Send the request to CyberSource and get the reply.
- $reply = $soapClient->runTransaction($request);
- }
- catch (SoapFault $exception) {
- // Log and display errors if Ubercart is unable to connect via SOAP.
- watchdog('uc_cybersource', 'Unable to connect to CyberSource via SOAP.', array(), WATCHDOG_ERROR);
- drupal_set_message(t('We apologize for the delay, but we are unable to process your credit card at this time. Please <a href="@url">contact sales</a> to complete your order.', array('@url' => url('contact'))), 'error');
- }
- // Process a reply from CyberSource.
- if (isset($reply)) {
- $types = uc_credit_transaction_types();
- // Create the order and payment ledger comments.
- $o_comment = t('<b>@type:</b> @amount<br /><b>Decision: @decision</b><br /><b>Reason:</b> !reason', array('@type' => $types[$data['txn_type']], '@amount' => uc_currency_format($amount), '@decision' => $reply->decision, '!reason' => _uc_cybersource_parse_reason_code($reply->reasonCode)));
- $p_comment = t('<b>@type:</b><br />@id<br />@decision, Reason: !reason', array('@type' => $types[$data['txn_type']], '@id' => $reply->requestID, '@decision' => $reply->decision, '!reason' => $reply->reasonCode));
- if (!empty($reply->ccAuthReply->avsCode)) {
- $o_comment .= '<br />' . t('<b>AVS:</b> @avs', array('@avs' => _uc_cybersource_parse_avs_code($reply->ccAuthReply->avsCode)));
- $p_comment .= t(', AVS: @avs', array('@avs' => $reply->ccAuthReply->avsCode));
- }
- if (!empty($reply->ccAuthReply->cvCode)) {
- $o_comment .= '<br />' . t('<b>CVV:</b> @cvv', array('@cvv' => _uc_cybersource_parse_cvv_code($reply->ccAuthReply->cvCode)));
- $p_comment .= t(', CVV: @cvv', array('@cvv' => $reply->ccAuthReply->cvCode));
- }
- uc_order_comment_save($order->order_id, $user->uid, $o_comment, 'admin');
- // Store the subscription ID if one was created.
- if (isset($reply->paySubscriptionCreateReply)) {
- // If the create request was successful...
- if ($reply->paySubscriptionCreateReply->reasonCode == '100') {
- $id = $reply->paySubscriptionCreateReply->subscriptionID;
- // Save the subscription ID to the order's data array.
- $order->data = uc_credit_log_reference($order->order_id, $id, $order->payment_details['cc_number']);
- uc_order_comment_save($order->order_id, 0, t('<b>CyberSource profile created.</b><br /><b>Subscription ID:</b> @id', array('@id' => $id)), 'admin');
- }
- else {
- uc_order_comment_save($order->order_id, 0, t('<b>Attempt to create CyberSource profile failed.</b><br /><b>Reason:</b> @code', array('@code' => $reply->paySubscriptionCreateReply->reasonCode)), 'admin');
- }
- }
- if ($reply->decision == 'ACCEPT') {
- $result = array(
- 'success' => TRUE,
- 'comment' => $p_comment,
- 'message' => $o_comment,
- 'uid' => $user->uid,
- 'data' => array('module' => 'uc_cybersource', 'txn_type' => $data['txn_type'], 'request_id' => $reply->requestID),
- );
- // If this was an authorization only transaction...
- if ($data['txn_type'] == UC_CREDIT_AUTH_ONLY) {
- // Log the authorization to the order.
- $order->data = uc_credit_log_authorization($order->order_id, $reply->requestID, $amount);
- // Add the request token associated with the request ID.
- $order->data['cybersource'][$reply->requestID] = $reply->requestToken;
- // Save the updated data array to the database.
- db_update('uc_orders')
- ->fields(array('data' => serialize($order->data)))
- ->condition('order_id', $order->order_id)
- ->execute();
- }
- elseif ($data['txn_type'] == UC_CREDIT_PRIOR_AUTH_CAPTURE) {
- uc_credit_log_prior_auth_capture($order->order_id, $data['auth_id']);
- }
- }
- else {
- $result = array(
- 'success' => FALSE,
- 'comment' => $p_comment,
- 'message' => $o_comment,
- 'uid' => $user->uid,
- );
- }
- }
- else {
- $result = array(
- 'success' => FALSE,
- 'message' => t('No response returned from CyberSource.'),
- );
- }
- // Don't log this as a payment if money wasn't actually captured.
- if (in_array($data['txn_type'], array(UC_CREDIT_AUTH_ONLY))) {
- $result['log_payment'] = FALSE;
- }
- return $result;
- }
- /**
- * Displays the taxes for an order.
- */
- function uc_cybersource_tax_test($order) {
- // Fetch the taxes for the order.
- $data = uc_cybersource_uc_calculate_tax($order);
- // Build an item list for the taxes.
- $items = array();
- foreach ($data as $tax) {
- $items[] = t('@tax: @amount', array('@tax' => $tax['name'], '@amount' => uc_currency_format($tax['amount'])));
- }
- // Display a message if there are no taxes.
- if (empty($items)) {
- $items[] = t('No taxes returned for this order.');
- }
- return array(
- '#theme' => 'item_list',
- '#items' => $items,
- );
- }
- /**
- * Implements hook_uc_calculate_tax().
- *
- * Calculates taxes for an order using CyberSource's tax service.
- *
- * @param $order
- * An order object with address and product information.
- *
- * @return
- * An array of tax line item objects with the fields 'id', 'name', and
- * 'amount', keyed by id.
- */
- function uc_cybersource_uc_calculate_tax($order) {
- // Kick out if the tax service is not enabled.
- if (!variable_set('uc_cybersource_soap_tax_calculate', FALSE)) {
- return array();
- }
- if (!is_object($order)) {
- return array();
- }
- // Include the SOAP helper file.
- module_load_include('inc', 'uc_cybersource', 'uc_cybersource.soap');
- global $user;
- // Set the URL for the CyberSource SOAP Toolkit API WSDL.
- if (variable_get('uc_cybersource_server', 'test') == 'test') {
- $url = 'https://ics2wstest.ic3.com/commerce/1.x/transactionProcessor/CyberSourceTransaction_1.38.wsdl';
- }
- else {
- $url = 'https://ics2ws.ic3.com/commerce/1.x/transactionProcessor/CyberSourceTransaction_1.38.wsdl';
- }
- // Variable currency... not used at the moment.
- $currency = variable_get('uc_cybersource_currency', 'usd');
- $billing_country = uc_get_country_data(array('country_id' => $order->billing_country));
- $delivery_country = uc_get_country_data(array('country_id' => $order->delivery_country));
- try {
- $soapClient = new CyberSourceSoapClient($url, array());
- $login = _uc_cybersource_soap_login_data();
- // Create the request with some meta data.
- $request = new stdClass();
- $request->merchantID = $login['merchant_id'];
- $request->merchantReferenceCode = $order->order_id;
- $request->clientLibrary = 'PHP';
- $request->clientLibraryVersion = phpversion();
- $request->clientEnvironment = php_uname();
- // Add the billing information.
- $billTo = new stdClass();
- $billTo->firstName = $order->billing_first_name;
- $billTo->lastName = $order->billing_last_name;
- $billTo->street1 = $order->billing_street1;
- if ($order->billing_street2) {
- $billTo->street2 = $order->billing_street2;
- }
- $billTo->city = $order->billing_city;
- $billTo->state = uc_get_zone_code($order->billing_zone);
- $billTo->postalCode = $order->billing_postal_code;
- $billTo->country = $billing_country[0]['country_iso_code_2'];
- if ($order->billing_phone) {
- $billTo->phoneNumber = $order->billing_phone;
- }
- $billTo->email = $order->primary_email;
- $billTo->customerID = $order->uid;
- $request->billTo = $billTo;
- // Add the shipping information.
- $shipTo = new stdClass();
- $shipTo->firstName = $order->delivery_first_name;
- $shipTo->lastName = $order->delivery_last_name;
- $shipTo->street1 = $order->delivery_street1;
- if ($order->billing_street2) {
- $shipTo->street2 = $order->delivery_street2;
- }
- $shipTo->city = $order->delivery_city;
- $shipTo->state = uc_get_zone_code($order->delivery_zone);
- $shipTo->postalCode = $order->delivery_postal_code;
- $shipTo->country = $delivery_country[0]['country_iso_code_2'];
- $shipTo->email = $order->primary_email;
- $request->shipTo = $shipTo;
- // Add the company's ship from information.
- $shipFrom = new stdClass();
- $shipFrom->firstName = variable_get('cs_ship_from_first_name', '');
- $shipFrom->lastName = variable_get('cs_ship_from_last_name', '');
- $shipFrom->street1 = variable_get('cs_ship_from_street1', '');
- $shipFrom->city = variable_get('cs_ship_from_city', '');
- $shipFrom->state = variable_get('cs_ship_from_zone', '');
- $shipFrom->postalCode = variable_get('cs_ship_from_postal_code', '');
- $shipFrom->country = variable_get('cs_ship_from_country', '');
- $shipFrom->email = variable_get('cs_ship_from_email', '');
- $request->shipFrom = $shipFrom;
- // TaxService
- // US product codes:
- // 70.280: Software Training Services
- // 81112201.121: Business Use Services and Upgrades via Elect Dnld
- // TODO: product code, international product code
- // TODO: invoiceHeader->invoiceDate: to get correct refund amounts
- // TODO: VAT
- $taxService = new stdClass();
- $taxService->nexus = 'MA CA';
- $taxService->orderOriginCity = $taxService->orderAcceptanceCity = $shipFrom->city;
- $taxService->orderOriginCountry = $taxService->orderAcceptanceCountry = $shipFrom->country;
- $taxService->orderOriginState = $taxService->orderAcceptanceState = $shipFrom->state;
- $taxService->orderOriginPostalCode = $taxService->orderAcceptancePostalCode = $shipFrom->postalCode;
- $taxService->sellerRegistration = 'XXX TODO';
- $taxService->run = 'true';
- $request->taxService = $taxService;
- // Add the order total information.
- $purchaseTotals = new stdClass();
- $purchaseTotals->currency = $currency;
- // Add the products to the request.
- $request->item = array();
- $counter = 0;
- // Add the products to the item array.
- foreach ($order->products as $product) {
- $obj = $request->item[] = new stdClass();
- $obj->productName = $product->title;
- $obj->unitPrice = $product->price;
- $obj->quantity = $product->qty;
- $obj->productSKU = $product->model;
- $obj->productCode = 'default';
- $obj->id = $counter;
- $counter++;
- }
- // drupal_set_message('<pre>Request: ' . print_r($request, TRUE) . '</pre>');
- // Send the request to CyberSource and get the reply.
- $reply = $soapClient->runTransaction($request);
- // drupal_set_message('<pre>Reply: ' . print_r($reply, TRUE) . '</pre>');
- }
- catch (SoapFault $exception) {
- // Log and display errors if Ubercart is unable to connect via SOAP.
- watchdog('uc_cybersource', 'Unable to connect to CyberSource via SOAP.', array(), WATCHDOG_ERROR);
- drupal_set_message(t('We apologize for the delay, but we are unable to process your credit card at this time. Please <a href="@url">contact sales</a> to complete your order.', array('@url' => url('contact'))), 'error');
- }
- // Process a reply from CyberSource.
- if (isset($reply)) {
- $result = array();
- if ($reply->reasonCode == '100') {
- // Add a city tax if applicable.
- if (floatval($reply->taxReply->totalCityTaxAmount) > 0) {
- $result['city'] = (object) array(
- 'id' => 'city',
- 'name' => t('@city city tax', array('@city' => floatval($reply->taxReply->city))),
- 'amount' => floatval($reply->taxReply->totalCityTaxAmount),
- );
- }
- // Add a county tax if applicable.
- if (floatval($reply->taxReply->totalCountyTaxAmount) > 0) {
- $result['county'] = (object) array(
- 'id' => 'county',
- 'name' => t('County tax'),
- 'amount' => floatval($reply->taxReply->totalCountryTaxAmount),
- );
- }
- // Add a district tax if applicable.
- if (floatval($reply->taxReply->totalDistrictTaxAmount) > 0) {
- $result['district'] = (object) array(
- 'id' => 'district',
- 'name' => t('District tax'),
- 'amount' => floatval($reply->taxReply->totalDistrictTaxAmount),
- );
- }
- // Add a state tax if applicable.
- if (floatval($reply->taxReply->totalStateTaxAmount) > 0) {
- $result['state'] = (object) array(
- 'id' => 'state',
- 'name' => t('@state state tax', array('@state' => $reply->taxReply->state)),
- 'amount' => floatval($reply->taxReply->totalStateTaxAmount),
- );
- }
- // Verify that the component taxes equal the total.
- $total = 0;
- foreach ($result as $tax) {
- $total += $tax['amount'];
- }
- // If it doesn't, log an error message and simply return the total.
- if ($total != floatval($reply->taxReply->totalTaxAmount)) {
- watchdog('uc_cybersource', 'Tax calculation produced uneven results. Expected a total of @total, received the following: @dump', array('@total' => uc_currency_format($reply->taxReply->totalTaxAmount), '@dump' => '<pre>' . print_r($result, TRUE) . '</pre>'), WATCHDOG_ERROR);
- $result = array(
- 'total' => (object) array(
- 'id' => 'total',
- 'name' => t('Tax'),
- 'amount' => floatval($reply->taxReply->totalTaxAmount),
- ),
- );
- }
- }
- else {
- watchdog('uc_cybersource', 'Attempted to calculate taxes failed for order @order_id - reason @code', array('@order_id' => $order->order_id, '@code' => $reply->reasonCode), WATCHDOG_ERROR);
- }
- }
- else {
- watchdog('uc_cybersource', 'Attempted to calculate taxes failed for order @order_id. No response returned from CyberSource.', array('@order_id' => $order->order_id), WATCHDOG_ERROR);
- $result = array();
- }
- /**
- * Code for the Simple Order API that was never completed.
- *
- * else {
- * $config = cybs_load_config('cybs.ini');
- * if (variable_get('uc_cybersource_server', 'test') == 'test') {
- * $config['sendToProduction'] = 'false';
- * }
- *
- * $request['ccAuthService_run'] = 'true';
- * if (variable_get('uc_cybersource_hop_transaction_type', 'sale') == 'sale') {
- * $request['ccCaptureService_run'] = 'true';
- * }
- * $request['merchantReferenceCode'] = $order_id;
- * $request['purchaseTotals_currency'] = 'USD';
- * $request['purchaseTotals_grandTotalAmount'] = $amount;
- *
- * drupal_set_message('<pre>' . print_r($config, TRUE) . '</pre>');
- * drupal_set_message('<pre>' . print_r($request, TRUE) . '</pre>');
- * }
- */
- return $result;
- }
- /**
- * Returns an array with the SOAP Merchant ID and Transaction key.
- */
- function _uc_cybersource_soap_login_data() {
- static $data;
- if (!empty($data)) {
- return $data;
- }
- $merchant_id = variable_get('uc_cybersource_soap_merchant_id', '');
- $transaction_key = variable_get('uc_cybersource_soap_transaction_key', '');
- // If CC encryption has been configured properly.
- if ($key = uc_credit_encryption_key()) {
- // Setup our encryption object.
- $crypt = new UbercartEncryption();
- // Decrypt the Merchant ID and Transaction key.
- if (!empty($merchant_id)) {
- $merchant_id = $crypt->decrypt($key, $merchant_id);
- }
- if (!empty($transaction_key)) {
- $transaction_key = $crypt->decrypt($key, $transaction_key);
- }
- // Store any errors.
- uc_store_encryption_errors($crypt, 'uc_cybersource');
- }
- $data = array(
- 'merchant_id' => $merchant_id,
- 'transaction_key' => $transaction_key,
- );
- return $data;
- }
- /**
- * Returns the code for the credit card type.
- */
- function _uc_cybersource_card_type($cc_number) {
- switch (substr(strval($cc_number), 0, 1)) {
- case '3':
- if (strlen($cc_number) == 14) {
- return '005'; // Diners Club
- }
- elseif (strlen($cc_number) == 15) {
- return '003'; // AmEx
- }
- else {
- return '007'; // JCB
- }
- case '4':
- return '001'; // Visa
- case '5':
- return '002'; // MasterCard
- case '6':
- return '004'; // Discover
- }
- return FALSE;
- }
- /**
- * Returns the meaning of the reason code given by CyberSource.
- */
- function _uc_cybersource_parse_reason_code($code) {
- switch ($code) {
- case '100':
- return t('Successful transaction.');
- case '102':
- return t('One or more fields in the request are missing or invalid.<br /><b>Possible action:</b> Resend the request with the correct information.');
- case '150':
- return t('<b>Error:</b> General system failure.<br /><b>Possible action:</b> Wait a few minutes and resend the request.');
- case '151':
- return t('<b>Error:</b> The request was received, but a server time-out occurred. This error does not include time-outs between the client and the server.<br /><b>Possible action:</b> To avoid duplicating the order, do not resend the request until you have reviewed the order status in the Business Center.');
- case '152':
- return t('<b>Error:</b> The request was received, but a service did not finish running in time.<br /><b>Possible action:</b> To avoid duplicating the order, do not resend the request until you have reviewed the order status in the Business Center.');
- case '200':
- return t('The authorization request was approved by the issuing bank but declined by CyberSource because it did not pass the Address Verification Service (AVS) check.<br /><b>Possible action:</b> You can capture the authorization, but consider reviewing the order for the possibility of fraud.');
- case '202':
- return t('Expired card.<br /><b>Possible action:</b> Request a different card or other form of payment.');
- case '203':
- return t('General decline of the card. No other information provided by the issuing bank.<br /><b>Possible action:</b> Request a different card or other form of payment.');
- case '204':
- return t('Insufficient funds in the account.<br /><b>Possible action:</b> Request a different card or other form of payment.');
- case '205':
- return t("Stolen or lost card.<br /><b>Possible action:</b> Review the customer's information and determine if you want to request a different card from the customer.");
- case '207':
- return t('Issuing bank unavailable.<br /><b>Possible action:</b> Wait a few minutes and resend the request.');
- case '208':
- return t('Inactive card or card not authorized for card-not-present transactions.<br /><b>Possible action:</b> Request a different card or other form of payment.');
- case '210':
- return t('The card has reached the credit limit.<br /><b>Possible action:</b> Request a different card or other form of payment.');
- case '211':
- return t('The card verification number is invalid.<br /><b>Possible action:</b> Request a different card or other form of payment.');
- case '220':
- return t("The processor declined the request based on a general issue with the customer's account.<br /><b>Possible action:</b> Request a different form of payment.");
- case '221':
- return t('The customer matched an entry on the processor’s negative file.<br /><b>Possible action:</b> Review the order and contact the payment processor.');
- case '222':
- return t("The customer's bank account is frozen.<br /><b>Possible action:</b> Review the order or request a different form of payment.");
- case '230':
- return t('The authorization request was approved by the issuing bank but declined by CyberSource because it did not pass the card verification number check.<br /><b>Possible action:</b> You can capture the authorization, but consider reviewing the order for the possibility of fraud.');
- case '231':
- return t('Invalid account number.<br /><b>Possible action:</b> Request a different card or other form of payment.');
- case '232':
- return t('The card type is not accepted by the payment processor.<br /><b>Possible action:</b> Request a different card or other form of payment. Also, check with CyberSource Customer Support to make sure that your account is configured correctly.');
- case '233':
- return t('The processor declined the request based on an issue with the request itself.<br /><b>Possible action:</b> Request a different form of payment.');
- case '234':
- return t('There is a problem with your CyberSource merchant configuration.<br /><b>Possible action:</b> Do not resend the request. Contact Customer Support to correct the configuration problem.');
- case '236':
- return t('Processor failure.<br /><b>Possible action:</b> Possible action: Wait a few minutes and resend the request.');
- case '240':
- return t('The card type sent is invalid or does not correlate with the credit card number.<br /><b>Possible action:</b> Ask your customer to verify that the card is really the type indicated in your Web store, then resend the request.');
- case '250':
- return t('<b>Error:</b> The request was received, but a time-out occurred with the payment processor.<br /><b>Possible action:</b> To avoid duplicating the transaction, do not resend the request until you have reviewed the transaction status in the Business Center.');
- case '475':
- return t('The customer is enrolled in payer authentication.<br /><b>Possible action:</b> Authenticate the cardholder before continuing with the transaction.');
- case '476':
- return t("The customer cannot be authenticated.<br /><b>Possible action:</b> Review the customer's order.");
- case '520':
- return t('The authorization request was approved by the issuing bank but declined by CyberSource based on your Smart Authorization settings.<br /><b>Possible action:</b> Do not capture the authorization without further review. Review the avsCode, cvResult, and factorCode fields to determine why CyberSource rejected the request.');
- }
- }
- /**
- * Returns the meaning of the code for Address Verification.
- */
- function _uc_cybersource_parse_avs_code($code) {
- switch ($code) {
- case 'A':
- return t('Street address matches, but 5- and 9-digit postal codes do not match.');
- case 'B':
- return t('Street address matches, but postal code not verified. Returned only for non U.S.-issued Visa cards.');
- case 'C':
- return t('Street address and postal code do not match. Returned only for non U.S.-issued Visa cards.');
- case 'D':
- return t('Street address and postal code match. Returned only for non U.S.-issued Visa cards.');
- case 'E':
- return t('AVS data is invalid, or AVS is not allowed for this card type.');
- case 'F':
- return t("Card member's name does not match, but postal code matches. Returned only for the American Express card type.");
- case 'G':
- return t('Non-U.S. issuing bank does not support AVS.');
- case 'H':
- return t("Card member's name does not match. Street address and postal code match. Returned only for the American Express card type.");
- case 'I':
- return t('Address not verified. Returned only for non U.S.-issued Visa cards.');
- case 'K':
- return t("Card member's name matches but billing address and billing postal code do not match. Returned only for the American Express card type.");
- case 'L':
- return t("Card member's name and billing postal code match, but billing address does not match. Returned only for the American Express card type");
- case 'N':
- return t("Street address and postal code do not match. - or - Card member's name, street address and postal code do not match. Returned only for the American Express card type.");
- case 'O':
- return t("Card member's name and billing address match, but billing postal code does not match. Returned only for the American Express card type.");
- case 'P':
- return t('Postal code matches, but street address not verified. Returned only for non-U.S.-issued Visa cards.');
- case 'R':
- return t('System unavailable.');
- case 'S':
- return t('U.S.-issuing bank does not support AVS.');
- case 'T':
- return t("Card member's name does not match, but street address matches. Returned only for the American Express card type.");
- case 'U':
- return t('Address information unavailable. Returned if non-U.S. AVS is not available or if the AVS in a U.S. bank is not functioning properly.');
- case 'W':
- return t('Street address does not match, but 9-digit postal code matches.');
- case 'X':
- return t('Exact match. Street address and 9-digit postal code match.');
- case 'Y':
- return t('Exact match. Street address and 5-digit postal code match.');
- case 'Z':
- return t('Street address does not match, but 5-digit postal code matches.');
- case '1':
- return t('AVS is not supported for this processor or card type.');
- case '2':
- return t('The processor returned an unrecognized value for the AVS response.');
- }
- }
- /**
- * Returns the meaning of the code sent back for CVV verification.
- */
- function _uc_cybersource_parse_cvv_code($code) {
- switch ($code) {
- case 'D':
- return t('Transaction determined suspicious by issuing bank.');
- case 'I':
- return t("Card verification number failed processor's data validation check.");
- case 'M':
- return t('Card verification number matched.');
- case 'N':
- return t('Card verification number not matched.');
- case 'P':
- return t('Card verification number not processed by processor for unspecified reason.');
- case 'S':
- return t('Card verification number is on the card but was not included in the request.');
- case 'U':
- return t('Card verification is not supported by the issuing bank.');
- case 'X':
- return t('Card verification is not supported by the card association.');
- case '1':
- return t('Card verification is not supported for this processor or card type.');
- case '2':
- return t('Unrecognized result code returned by processor for card verification response.');
- case '3':
- return t('No result code returned by processor.');
- }
- }
|