commerce_amex.module in Commerce American Express Payment Gateway (Amex) 7
Implements American Express payment gateway for use in Drupal Commerce.
File
commerce_amex.moduleView source
<?php
/**
* @file
* Implements American Express payment gateway for use in Drupal Commerce.
*/
// Amex transaction mode definitions:
define('AMEX_TXN_MODE_LIVE', '');
define('AMEX_TXN_MODE_TEST', 'TEST');
// Amex transaction types
define('AMEX_OP_AUTH', 'AUTHORIZE');
define('AMEX_OP_CAPTURE', 'CAPTURE');
define('AMEX_OP_PAY', 'PAY');
define('AMEX_OP_VERIFY', 'VERIFY');
define('AMEX_TXN_PATH', '/api/rest/version/12/merchant/');
define('COMMERCE_PAYMENT_STATUS_AMEX_REVIEW', 'amex_review');
define('COMMERCE_PAYMENT_STATUS_AMEX_CANCELED', 'amex_canceled');
/**
* Implements hook_menu
*/
function commerce_amex_menu() {
$items = array();
// Define a path for the Amex gatewayReturnURL.
$items['checkout/%commerce_order/amex/return/%'] = array(
'page callback' => 'commerce_amex_hosted_return_handle',
'page arguments' => array(
1,
4,
),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
// Define a path to receive 3D Secure callback.
$items['checkout/%commerce_order/amex/3d/%commerce_payment_transaction'] = array(
'page callback' => 'commerce_amex_3d_secure_callback',
'page arguments' => array(
1,
4,
),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
// Add a menu item for capturing authorizations.
$items['admin/commerce/orders/%commerce_order/payment/%commerce_payment_transaction/amex-capture'] = array(
'title' => 'Capture',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'commerce_amex_capture_form',
3,
5,
),
'access callback' => 'commerce_amex_capture_access',
'access arguments' => array(
3,
5,
),
'type' => MENU_DEFAULT_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
'weight' => 2,
'file' => 'includes/commerce_amex.admin.inc',
);
// Add a menu item for voiding authorizations.
$items['admin/commerce/orders/%commerce_order/payment/%commerce_payment_transaction/amex-void'] = array(
'title' => 'Void',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'commerce_amex_void_form',
3,
5,
),
'access callback' => 'commerce_amex_void_access',
'access arguments' => array(
3,
5,
),
'type' => MENU_DEFAULT_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
'weight' => 2,
'file' => 'includes/commerce_amex.admin.inc',
);
// Add a menu item for refunding authorizations.
$items['admin/commerce/orders/%commerce_order/payment/%commerce_payment_transaction/amex-refund'] = array(
'title' => 'Refund',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'commerce_amex_refund_form',
3,
5,
),
'access callback' => 'commerce_amex_refund_access',
'access arguments' => array(
3,
5,
),
'type' => MENU_DEFAULT_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
'weight' => 2,
'file' => 'includes/commerce_amex.admin.inc',
);
// Add a menu item for refunding authorizations.
$items['admin/commerce/orders/%commerce_order/payment/%commerce_payment_transaction/amex-update'] = array(
'title' => 'Update Status',
'page callback' => 'commerce_amex_update_transaction',
'page arguments' => array(
3,
5,
),
'access callback' => 'commerce_amex_update_access',
'access arguments' => array(
3,
5,
),
'type' => MENU_DEFAULT_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
'weight' => 2,
'file' => 'includes/commerce_amex.admin.inc',
);
return $items;
}
/**
* Determines access to the prior authorization capture form for Amex
* hosted credit card transactions.
*
* @param $order
* The order the transaction is on.
* @param $transaction
* The payment transaction object to be captured.
*
* @return
* TRUE or FALSE indicating capture access.
*/
function commerce_amex_capture_access($order, $transaction) {
// Return FALSE if the transaction isn't for Amex or isn't
// awaiting capture.
if ($transaction->payment_method != 'amex_hosted' || strtoupper($transaction->remote_status) != 'APPROVED' || $transaction->status != COMMERCE_PAYMENT_STATUS_PENDING) {
return FALSE;
}
// Return FALSE if it is more than 30 days past the original authorization.
if (time() - $transaction->created > 86400 * 30) {
return FALSE;
}
// Allow access if the user can update this transaction.
return commerce_payment_transaction_access('update', $transaction);
}
/**
* Determines access to the prior authorization capture form for Amex
* hosted credit card transactions.
*
* @param $order
* The order the transaction is on.
* @param $transaction
* The payment transaction object to be captured.
*
* @return
* TRUE or FALSE indicating capture access.
*/
function commerce_amex_void_access($order, $transaction) {
// Return FALSE if the transaction isn't for Amex or isn't
// awaiting capture.
if ($transaction->payment_method != 'amex_hosted' || empty($transaction->remote_id) || strtoupper($transaction->remote_status) != 'APPROVED' || $transaction->status != COMMERCE_PAYMENT_STATUS_PENDING) {
return FALSE;
}
// Return FALSE if it is more than 30 days past the original authorization.
if (time() - $transaction->created > 86400 * 30) {
return FALSE;
}
// Allow access if the user can update this transaction.
return commerce_payment_transaction_access('update', $transaction);
}
/**
* Determines access to the prior authorization capture form for Amex
* hosted credit card transactions.
*
* @param $order
* The order the transaction is on.
* @param $transaction
* The payment transaction object to be captured.
*
* @return
* TRUE or FALSE indicating capture access.
*/
function commerce_amex_refund_access($order, $transaction) {
// Return FALSE if the transaction isn't for Amex or isn't
// awaiting capture.
if ($transaction->payment_method != 'amex_hosted' || empty($transaction->remote_id) || strtoupper($transaction->remote_status) != 'APPROVED' || $transaction->status != COMMERCE_PAYMENT_STATUS_SUCCESS) {
return FALSE;
}
if ($transaction->amount <= 0) {
return FALSE;
}
// Return FALSE if it is more than 30 days past the original authorization.
if (time() - $transaction->created > 86400 * 30) {
return FALSE;
}
// Allow access if the user can update this transaction.
return commerce_payment_transaction_access('update', $transaction);
}
/**
* Determines access to the prior authorization capture form for Amex
* hosted credit card transactions.
*
* @param $order
* The order the transaction is on.
* @param $transaction
* The payment transaction object to be captured.
*
* @return
* TRUE or FALSE indicating capture access.
*/
function commerce_amex_update_access($order, $transaction) {
// Return FALSE if the transaction isn't for Amex or isn't
// awaiting capture.
if ($transaction->payment_method != 'amex_hosted' || empty($transaction->remote_id) || $transaction->status != COMMERCE_PAYMENT_STATUS_AMEX_REVIEW) {
return FALSE;
}
// Allow access if the user can update this transaction.
return commerce_payment_transaction_access('update', $transaction);
}
/**
* Implements hook_commerce_payment_method_info().
*/
function commerce_amex_commerce_payment_method_info() {
$payment_methods = array();
$payment_methods['amex_hosted'] = array(
'base' => 'commerce_amex_hosted',
'title' => t('American Express'),
'short_title' => t('AMEX Host'),
'display_title' => t('Pay with Credit or Debit Card'),
'description' => t('Integrates American Express Hosted Payment form.'),
'offsite' => TRUE,
'cardonfile' => array(
// create and update are not used because offsite card form.
'update callback' => 'commerce_amex_cardonfile_update',
//'update form callback' => 'commerce_amex_cardonfile_update_form',
'delete callback' => 'commerce_amex_cardonfile_delete',
'charge callback' => 'commerce_amex_cardonfile_charge',
),
);
return $payment_methods;
}
/**
* Implements hool_commerce_payment_transaction_status_info().
*/
function commerce_amex_commerce_payment_transaction_status_info() {
$statuses = array();
$statuses[COMMERCE_PAYMENT_STATUS_AMEX_REVIEW] = array(
'status' => COMMERCE_PAYMENT_STATUS_AMEX_REVIEW,
'title' => t('Review'),
'icon' => drupal_get_path('module', 'commerce_payment') . '/theme/icon-pending.png',
'total' => FALSE,
);
$statuses[COMMERCE_PAYMENT_STATUS_AMEX_CANCELED] = array(
'status' => COMMERCE_PAYMENT_STATUS_AMEX_CANCELED,
'title' => t('Canceled'),
'icon' => drupal_get_path('module', 'commerce_payment') . '/theme/icon-failure.png',
'total' => FALSE,
);
return $statuses;
}
/**
* Implements hook_commerce_checkout_page_info_alter().
*/
function commerce_amex_commerce_checkout_page_info_alter(&$checkout_pages) {
$checkout_pages['payment']['help'] = NULL;
}
/**
* Implements hook_commerce_cardonfile_checkout_pane_form_alter()
*/
// function commerce_amex_commerce_cardonfile_checkout_pane_form_alter(&$pane_form, $form) {
// // Make alterations for our method
// if (!empty($form['commerce_payment']['payment_method']['#default_value'])) {
// $instance_id = $form['commerce_payment']['payment_method']['#default_value'];
// $payment_method = commerce_payment_method_instance_load($instance_id);
// if ($payment_method['method_id'] == 'amex_hosted') {
// // Always remove the save card option here.
// unset($pane_form['credit_card']['cardonfile_store']);
// unset($pane_form['cardonfile_instance_default']);
// }
// }
// }
/**
* Returns the default settings for the Amex hosted payment method.
*/
function commerce_amex_hosted_default_settings() {
return array(
'merchant_id' => '',
'password' => '',
'txn_mode' => AMEX_TXN_MODE_TEST,
'txn_3d_secure' => 1,
'txn_url' => '',
'txn_type' => AMEX_OP_PAY,
'cardonfile' => FALSE,
'continuous' => FALSE,
);
}
/**
* Payment method callback: settings form.
*/
function commerce_amex_hosted_settings_form($settings = NULL) {
module_load_include('inc', 'commerce_payment', 'includes/commerce_payment.credit_card');
// Merge default settings into the stored settings array.
$settings = (array) $settings + commerce_amex_hosted_default_settings();
$form = array();
$form['merchant_id'] = array(
'#type' => 'textfield',
'#title' => t('Merchant ID'),
'#description' => t('Your Merchant ID.'),
'#default_value' => $settings['merchant_id'],
'#required' => TRUE,
);
// @todo Secure password data
$form['password'] = array(
'#type' => 'textfield',
'#title' => t('Password'),
'#description' => t('Your Transaction Password.'),
'#default_value' => $settings['password'],
'#required' => TRUE,
);
$form['txn_type'] = array(
'#type' => 'radios',
'#title' => t('Default credit card transaction type'),
'#description' => t('The default will be used to process transactions during checkout.'),
'#options' => array(
AMEX_OP_PAY => t('Payment in Full'),
AMEX_OP_AUTH => t('Authorisation only (requires manual or automated capture after checkout)'),
AMEX_OP_VERIFY => t('Verify (US Only) no funds are collected any amount is only used for risk assesment.'),
),
'#default_value' => $settings['txn_type'],
);
$form['txn_3d_secure'] = array(
'#type' => 'radios',
'#title' => t('Carry out 3D Secure security check'),
'#default_value' => $settings['txn_3d_secure'],
'#options' => array(
'0' => t('Do not perform 3D-Secure checks for this transaction only and always authorise.'),
'1' => t('If card is enroled attempt authentication (allow if remote server errors)'),
'2' => t('If card is enroled require authentication'),
'3' => t('Require card to be enroled and attempt authentication (allow if remote server errors)'),
'4' => t('Require card to be enroled and require authentication'),
),
);
$form['txn_mode'] = array(
'#type' => 'radios',
'#title' => t('Transaction mode'),
'#description' => t('Adjust to live transactions when you are ready to start processing real payments.'),
'#options' => array(
AMEX_TXN_MODE_LIVE => t('Live transactions'),
AMEX_TXN_MODE_TEST => t('Test transactions'),
),
'#default_value' => $settings['txn_mode'],
);
$form['txn_url'] = array(
'#type' => 'textfield',
'#title' => t('Transaction URL'),
'#description' => t('Enter the transaction URL e.g. https://gateway-emea.americanexpress.com'),
'#default_value' => $settings['txn_url'],
'#required' => TRUE,
);
// Card on File module support.
if (module_exists('commerce_cardonfile')) {
$form['cardonfile'] = array(
'#type' => 'checkbox',
'#title' => t('Enable Card on File functionality with this payment method.'),
'#description' => t('Stores tokenised value for card data.'),
'#default_value' => $settings['cardonfile'],
);
$form['continuous'] = array(
'#type' => 'checkbox',
'#title' => t('Use continuous authority transactions.'),
'#description' => t('Card on file strage will be required.'),
'#default_value' => $settings['continuous'],
);
}
else {
$form['cardonfile'] = array(
'#type' => 'markup',
'#markup' => t('To enable Card on File funcitionality download and install the Card on File module.'),
);
}
return $form;
}
/**
* Payment method callback: submit form submission.
*/
function commerce_amex_hosted_submit_form_submit($payment_method, $pane_form, $pane_values, $order, $charge) {
// Return an error if the enabling action's settings haven't been configured.
if (empty($payment_method['settings']['password'])) {
drupal_set_message(t('Payment gateway is not configured for use.'), 'error');
return array();
}
// Create a form session with Amex.
$session = _commerce_amex_request_session($payment_method);
if (isset($session->result) && $session->result == "SUCCESS") {
$session_id = $session->session;
// Create a new payment transaction and setup the amount.
$transaction = commerce_payment_transaction_new('amex_hosted', $order->order_id);
$transaction->amount = $charge['amount'];
$transaction->currency_code = $charge['currency_code'];
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
$transaction->instance_id = $payment_method['instance_id'];
$transaction->remote_id = $session_id;
$transaction->data['cardonfile_selected'] = !empty($pane_values['cardonfile']) && $pane_values['cardonfile'] != 'new' ? $pane_values['cardonfile'] : NULL;
commerce_payment_transaction_save($transaction);
return TRUE;
}
elseif (isset($session->result) && $session->result == "ERROR") {
$explanation = isset($session->error->explanation) ? $session->error->explanation : 'Unknown Error';
watchdog('commerce_amex', 'Failed to connect to Amex for order %order_id: %explanation', array(
'%order_id' => $order->order_id,
'%explanation' => $explanation,
), WATCHDOG_ERROR);
drupal_set_message(t('There was an error connecting to the payment server.'), 'error');
commerce_payment_redirect_pane_previous_page($order);
}
else {
watchdog('commerce_amex', 'Failed to connect to Amex for order %order_id', array(
'%order_id' => $order->order_id,
), WATCHDOG_ERROR);
drupal_set_message(t('There was an error connecting to the payment server.'), 'error');
commerce_payment_redirect_pane_previous_page($order);
}
}
/**
* Payment method callback: redirect form.
**/
function commerce_amex_hosted_redirect_form($form, &$form_state, $order, $payment_method) {
$cardonfile_capable = module_exists('commerce_cardonfile') && !empty($payment_method['settings']['cardonfile']);
// Validate a transaction for the order.
$transactions = commerce_payment_transaction_load_multiple(array(), array(
'order_id' => $order->order_id,
));
if (count($transactions) > 0) {
$transaction = end($transactions);
$default = array(
'type' => '',
'owner' => '',
'number' => '',
'start_month' => '',
'start_year' => date('Y') - 5,
'exp_month' => date('m'),
'exp_year' => date('Y'),
'issue' => '',
'code' => '',
'bank' => '',
);
$current_year_2 = date('y');
$current_year_4 = date('Y');
$form = array();
$form['#action'] = $payment_method['settings']['txn_url'] . '/form/' . $transaction->remote_id;
$form['#method'] = "post";
// Prepare a display settings array.
$display = array(
'label' => 'hidden',
'type' => 'commerce_price_formatted_components',
'settings' => array(
'calculation' => FALSE,
),
);
// Render the order's order total field with the current display.
$order_total = field_view_field('commerce_order', $order, 'commerce_order_total', $display);
$form['order_total'] = array(
'#type' => 'markup',
'#markup' => render($order_total),
'#attributes' => array(
'class' => array(
'commerce-order-handler-area-order-total',
),
),
'#attached' => array(
'css' => array(
drupal_get_path('module', 'commerce_cart') . '/theme/commerce_cart.theme.css',
),
),
);
// Just show the CCV number field for a Card on File Transaction.
if ($cardonfile_capable && !empty($transaction->data['cardonfile_selected'])) {
$card_data = commerce_cardonfile_load($transaction->data['cardonfile_selected']);
module_load_include('inc', 'commerce_payment', 'includes/commerce_payment.credit_card');
$card_types = commerce_payment_credit_card_types();
$form['card_holder'] = array(
'#type' => 'item',
'#title' => t('Card holder'),
'#markup' => $card_data->card_name,
);
$form['card_type'] = array(
'#type' => 'item',
'#title' => t('Card Type'),
'#markup' => !empty($card_types[$card_data->card_type]) ? $card_types[$card_data->card_type] : $card_data->card_type,
);
$form['card_number'] = array(
'#type' => 'item',
'#title' => t('Card Number'),
'#markup' => 'XXXX - XXXX - XXXX - ' . $card_data->card_number,
);
$form['card_expire'] = array(
'#type' => 'item',
'#title' => t('Card Expires'),
'#markup' => $card_data->card_exp_month . ' / ' . $card_data->card_exp_year,
);
// Add a field for the security code.
$form['gatewayCardSecurityCode'] = array(
'#type' => 'textfield',
'#title' => !empty($fields['code']) ? $fields['code'] : t('Security code'),
'#default_value' => $default['code'],
'#attributes' => array(
'autocomplete' => 'off',
),
'#required' => TRUE,
'#maxlength' => 4,
'#size' => 4,
'#field_suffix' => '<img src = "' . file_create_url(drupal_get_path('module', 'commerce_amex') . '/theme/logo_cvv.jpg') . '">',
'#suffix' => t("Last three digits on the back of your card. For Amex, last four digits on the front of the card."),
);
}
else {
// Always add a field for the credit card owner.
$form['owner'] = array(
'#type' => 'textfield',
'#title' => t('Card holder'),
'#default_value' => $default['owner'],
'#attributes' => array(
'autocomplete' => 'off',
),
'#required' => TRUE,
'#maxlength' => 30,
'#size' => 30,
);
// Always add a field for the credit card number.
$form['gatewayCardNumber'] = array(
'#type' => 'textfield',
'#title' => t('Card number'),
'#default_value' => $default['number'],
'#attributes' => array(
'autocomplete' => 'off',
),
'#required' => TRUE,
'#maxlength' => 19,
'#size' => 20,
);
// Always add fields for the credit card expiration date.
$form['gatewayCardExpiryDateMonth'] = array(
'#type' => 'select',
'#title' => t('Expiration'),
'#options' => drupal_map_assoc(array_keys(commerce_months())),
'#default_value' => strlen($default['exp_month']) == 1 ? '0' . $default['exp_month'] : $default['exp_month'],
'#required' => TRUE,
'#prefix' => '<div class="commerce-credit-card-expiration">',
'#suffix' => '<span class="commerce-month-year-divider">/</span>',
// Attache the CSS from commerce_payment module for nice formatting.
'#attached' => array(
'css' => array(
drupal_get_path('module', 'commerce_payment') . '/theme/commerce_payment.theme.css',
),
),
);
// Build a year select list that uses a 4 digit key with a 2 digit value.
$options = array();
for ($i = 0; $i < 20; $i++) {
$options[$current_year_4 + $i] = str_pad($current_year_2 + $i, 2, '0', STR_PAD_LEFT);
}
$form['gatewayCardExpiryDateYear'] = array(
'#type' => 'select',
'#options' => $options,
'#default_value' => $default['exp_year'],
'#suffix' => '</div>',
);
// Add a field for the security code.
$form['gatewayCardSecurityCode'] = array(
'#type' => 'textfield',
'#title' => !empty($fields['code']) ? $fields['code'] : t('Security code'),
'#default_value' => $default['code'],
'#attributes' => array(
'autocomplete' => 'off',
),
'#required' => TRUE,
'#maxlength' => 4,
'#size' => 4,
'#field_suffix' => '<img src = "' . file_create_url(drupal_get_path('module', 'commerce_amex') . '/theme/logo_cvv.jpg') . '">',
'#suffix' => t("Last three digits on the back of your card. For Amex, last four digits on the front of the card."),
);
if ($cardonfile_capable) {
$storage = variable_get('commerce_cardonfile_storage', 'opt-in');
if (in_array($storage, array(
'opt-in',
'opt-out',
)) && !$payment_method['settings']['continuous']) {
$form['cardonfile_store'] = array(
'#type' => 'checkbox',
'#title' => t('Store this credit card on file for future use.'),
'#default_value' => $storage == 'opt-out',
);
}
else {
$form['cardonfile_store'] = array(
'#type' => 'hidden',
'#value' => TRUE,
);
}
$form['cardonfile_instance_default'] = array(
'#type' => 'checkbox',
'#title' => t('Set as your default card'),
//'#default_value' => $instance_default_default_value || $force_instance_default,
//'#access' => !$instance_default_default_value,
//'#disabled' => $force_instance_default,
'#states' => array(
'invisible' => array(
':input[name$="[cardonfile]"]' => array(
'value' => 'new',
),
),
'visible' => array(
':input[name$="[cardonfile_store]"]' => array(
'checked' => TRUE,
),
),
),
);
}
}
// Add the return URL for Amex return form to post to.
$form['gatewayReturnURL'] = array(
'#type' => 'hidden',
'#value' => url('checkout/' . $order->order_id . '/payment/return/' . $order->data['payment_redirect_key'], array(
'absolute' => TRUE,
)),
);
// Set the backgound colour for the redirect page.
$form['gatewayRedirectDisplayBackgroundColor'] = array(
'#type' => 'hidden',
'#value' => "#ffffff",
);
$form['gatewayRedirectDisplayTitle'] = array(
'#type' => 'hidden',
'#value' => t('Check Your Submitted Payment Details'),
);
$form['gatewayRedirectDisplayContinueButtonText'] = array(
'#type' => 'hidden',
'#value' => t('Click to Continue'),
);
$form['transaction_id'] = array(
'#type' => 'hidden',
'#value' => $transaction->transaction_id,
);
$form['actions'] = array(
'#type' => 'actions',
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Continue'),
);
$form['actions']['cancel'] = array(
'#markup' => l(t('Cancel'), url('checkout/' . $order->order_id . '/payment/back/' . $order->data['payment_redirect_key'], array(
'absolute' => TRUE,
))),
);
if ($payment_method['settings']['txn_3d_secure'] > 0) {
$form['3dsecure'] = array(
'#type' => 'markup',
'#weight' => 100,
'#markup' => '<img src = "' . file_create_url(drupal_get_path('module', 'commerce_amex') . '/theme/logo-3dsecure.jpg') . '">
<div>' . t("You may be taken to your card issuer's website for credit card validation.") . '<br />' . t("You will be returned here once the process is complete.") . '</div>',
'#prefix' => '<div class="commerce-amex-3d">',
'#suffix' => '</div>',
'#attached' => array(
'css' => array(
drupal_get_path('module', 'commerce_amex') . '/theme/commerce_amex.theme.css',
),
),
);
}
return $form;
}
else {
//watchdog('commerce_amex', 'Transaction could not be found', WATCHDOG_ERROR);
drupal_set_message(t('Transaction could not be found.'), 'error');
drupal_goto('checkout/' . $order->order_id . '/payment/back/' . $order->data['payment_redirect_key']);
}
}
/**
* Payment method callback: redirect form validate.
*/
function commerce_amex_hosted_redirect_form_validate($order, $payment_method) {
$return = FALSE;
switch (substr($_POST['gatewayFormResponse'], 0, 1)) {
case '0':
// Card details have been added to the session.
$return = TRUE;
break;
case '1':
case '2':
case '3':
drupal_set_message(t('There was a problem processing your transaction. Your credit/debit card was not charged. Please try again later.'), 'error');
break;
}
return $return;
}
/**
* Payment method callback: redirect form submit.
*/
function commerce_amex_hosted_redirect_form_submit($order, $payment_method) {
$return = FALSE;
$process_transaction = FALSE;
$cardonfile_capable = module_exists('commerce_cardonfile') && !empty($payment_method['settings']['cardonfile']);
if ($transaction = commerce_payment_transaction_load($_POST['transaction_id'])) {
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
if ($cardonfile_capable) {
$transaction->data['cardonfile_store'] = !empty($_POST['cardonfile_store']) ? $_POST['cardonfile_store'] : FALSE;
if (!isset($transaction->data['cardonfile_selected']) && $payment_method['settings']['continuous']) {
$transaction->data['cardonfile_store'] = TRUE;
}
}
$transaction->data['owner'] = !empty($_POST['owner']) ? $_POST['owner'] : FALSE;
// Use the validate stage to check if the card is enroled for 3DSecure.
if ($payment_method['settings']['txn_3d_secure'] > 0) {
commerce_payment_transaction_save($transaction);
$process_transaction = commerce_amex_3d_secure_check($order, $payment_method, $transaction);
}
else {
$process_transaction = TRUE;
}
if ($process_transaction) {
$transaction = _commerce_amex_process_transaction($order, $transaction);
if ($transaction->payload->result == 'SUCCESS') {
$return = TRUE;
}
}
else {
drupal_set_message(t('Transaction could not be found.'), 'error');
}
}
return $return;
}
/**
* Check if the card is registered for 3D Secure and redirect if required
* @param object $order
* @param object $payment_method
* @param object $transaction
* @return boolean
*/
function commerce_amex_3d_secure_check($order, $payment_method, $transaction) {
$amount = commerce_currency_amount_to_decimal($transaction->amount, $transaction->currency_code);
$order_amount = number_format($amount, 2, '.', '');
$return = FALSE;
// Build the data array.
$data = new stdClass();
$data->apiOperation = 'CHECK_3DS_ENROLLMENT';
$data->transaction['amount'] = $order_amount;
$data->transaction['currency'] = $transaction->currency_code;
$data->sourceOfFunds['session'] = $transaction->remote_id;
if (isset($transaction->data['cardonfile_selected']) && ($cardonfile = commerce_cardonfile_load($transaction->data['cardonfile_selected']))) {
$data->sourceOfFunds['token'] = $cardonfile->remote_id;
}
$data->{'3DSecure'}['authenticationRedirect']['responseUrl'] = url('checkout/' . $order->order_id . '/amex/3d/' . $transaction->transaction_id, array(
'absolute' => TRUE,
));
// Build the URL and send the request to Amex.
$url = $payment_method['settings']['txn_url'] . AMEX_TXN_PATH . $payment_method['settings']['merchant_id'] . '/3DSecureId/' . $transaction->transaction_id;
$result = _commerce_amex_put_request($url, $payment_method['settings']['merchant_id'], $payment_method['settings']['password'], $data);
// Process error and return false.
if (isset($result->error) && $result->result == 'ERROR') {
$transaction = _commerce_amex_error_process($result, $transaction);
commerce_payment_redirect_pane_previous_page($order);
}
// If the card is enrolled print the auto redirect form and exit.
if (isset($result->response)) {
switch ($result->response->{'3DSecure'}->gatewayCode) {
case 'CARD_ENROLLED':
print $result->{'3DSecure'}->authenticationRedirect->simple->htmlBodyContent;
drupal_exit();
break;
case 'NOT_ENROLLED_NO_ERROR_DETAILS':
case 'NOT_ENROLLED_ERROR_DETAILS_PROVIDED':
case 'CARD_DOES_NOT_SUPPORT_3DS':
case 'ENROLLMENT_STATUS_UNDETERMINED_NO_ERROR_DETAILS':
case 'ENROLLMENT_STATUS_UNDETERMINED_ERROR_DETAILS_PROVIDED':
case 'INVALID_DIRECTORY_SERVER_CREDENTIALS':
case 'ERROR_PARSING_CHECK_ENROLLMENT_RESPONSE':
case 'ERROR_COMMUNICATING_WITH_DIRECTORY_SERVER':
case 'MPI_PROCESSING_ERROR':
if ($payment_method['settings']['txn_3d_secure'] < 3) {
$return = TRUE;
}
else {
drupal_set_message(t('A 3DSecure card is required'), 'error');
}
break;
}
}
return $return;
}
/**
* Process callback response from merchant server
*/
function commerce_amex_3d_secure_callback($order, $transaction) {
$process_transaction = FALSE;
// If there's no data in the POST, return a page not found.
if (empty($_POST)) {
return drupal_not_found();
}
// check for 3d secure response field
if (!isset($_POST['PaRes'])) {
watchdog('commerce_amex', 'Invalid data received in 3D Secure response', array(), WATCHDOG_ERROR);
return drupal_not_found();
}
$payment_method = commerce_payment_method_instance_load($transaction->instance_id);
$data = new stdClass();
$data->apiOperation = 'PROCESS_ACS_RESULT';
$data->{'3DSecure'}['paRes'] = $_POST['PaRes'];
$url = $payment_method['settings']['txn_url'] . AMEX_TXN_PATH . $payment_method['settings']['merchant_id'] . '/3DSecureId/' . $transaction->transaction_id;
$result = _commerce_amex_post_request($url, $payment_method['settings']['merchant_id'], $payment_method['settings']['password'], $data);
// Process error and return to payment form.
if (isset($result->result) && $result->result == 'ERROR') {
$transaction = _commerce_amex_error_process($result, $transaction);
drupal_set_message(t('There was an error verifying your card with 3D Secure'), 'error');
commerce_payment_redirect_pane_previous_page($order);
drupal_goto('checkout/' . $order->order_id);
}
$transaction->remote_status = $result->response->{'3DSecure'}->gatewayCode;
switch ($result->response->{'3DSecure'}->gatewayCode) {
case 'AUTHENTICATION_SUCCESSFUL':
case 'AUTHENTICATION_ATTEMPTED':
$process_transaction = TRUE;
break;
case 'AUTHENTICATION_FAILED':
case 'INVALID_SIGNATURE_ON_AUTHENTICATION_RESPONSE':
drupal_set_message(t('3D Secure Authentication Failed.'), 'error');
break;
case 'AUTHENTICATION_NOT_AVAILABLE_NO_ERROR_DETAILS':
case 'AUTHENTICATION_NOT_AVAILABLE_ERROR_DETAILS_PROVIDED':
if ($payment_method['settings']['txn_3d_secure'] == 1 || $payment_method['settings']['txn_3d_secure'] == 3) {
$process_transaction = TRUE;
}
else {
drupal_set_message(t('Authentication not availiable.'), 'error');
}
break;
case 'ERROR_PARSING_AUTHENTICATION_RESPONSE':
drupal_set_message(t('There was an error proccessing your security details.'), 'error');
break;
case 'ACS_SESSION_TIMEOUT':
drupal_set_message(t('The 3DSecure session timed out.'), 'error');
break;
}
if ($process_transaction) {
$transaction = _commerce_amex_process_transaction($order, $transaction, TRUE);
if (isset($transaction->payload->result) && $transaction->payload->result == 'SUCCESS') {
commerce_payment_redirect_pane_next_page($order);
drupal_goto('checkout/' . $order->order_id);
}
else {
commerce_payment_redirect_pane_previous_page($order);
drupal_goto('checkout/' . $order->order_id);
}
}
else {
commerce_payment_redirect_pane_previous_page($order);
drupal_goto('checkout/' . $order->order_id);
}
}
/**
* Capture an autorised payment.
* @param unknown $order
* @param unknown $auth_transaction
* @param unknown $amount
* @return boolean
*/
function commerce_amex_capture_transaction($auth_transaction, $amount) {
$return = FALSE;
$instance_id = $auth_transaction->instance_id;
$payment_method = commerce_payment_method_instance_load($instance_id);
$transaction = commerce_payment_transaction_new('amex_hosted', $auth_transaction->order_id);
$transaction->amount = commerce_currency_decimal_to_amount($amount, $auth_transaction->currency_code);
$transaction->currency_code = $auth_transaction->currency_code;
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
$transaction->instance_id = $payment_method['instance_id'];
$transaction->remote_id = $auth_transaction->remote_id;
commerce_payment_transaction_save($transaction);
$data = new stdClass();
$data->apiOperation = AMEX_OP_CAPTURE;
$data->order['reference'] = $auth_transaction->transaction_id;
$data->transaction['amount'] = $amount;
$data->transaction['currency'] = $transaction->currency_code;
$data->transaction['reference'] = $transaction->transaction_id;
$url = $payment_method['settings']['txn_url'] . AMEX_TXN_PATH . $payment_method['settings']['merchant_id'] . '/order/' . (10000000000 + $auth_transaction->transaction_id) . '/transaction/' . $transaction->transaction_id;
$result = _commerce_amex_put_request($url, $payment_method['settings']['password'], $data);
$transaction->payload = $result;
switch ($result->result) {
case 'SUCCESS':
$transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = 'Captured';
$auth_transaction->amount = $auth_transaction->amount - $transaction->amount;
drupal_set_message(t('Transaction was processed succesfully'));
$return = TRUE;
break;
case 'PENDING':
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = $result->response->acquirerMessage;
drupal_set_message(t('Transaction pending'));
$return = TRUE;
break;
case 'FAILURE':
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = $result->response->acquirerMessage;
break;
case 'UNKNOWN':
drupal_set_message(t('There was a problem processing your transaction. Your credit/debit card was not charged. Please try again later.'), 'error');
break;
case 'ERROR':
$transaction = _commerce_amex_error_process($result, $transaction);
break;
}
if ($auth_transaction->amount == 0) {
$auth_transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
}
commerce_payment_transaction_save($transaction);
commerce_payment_transaction_save($auth_transaction);
return $return;
}
/**
* Void an auth transaction
* @param object $auth_transaction
* Transaction to be voided.
* @return boolean
*/
function commerce_amex_void_transaction($auth_transaction) {
$return = FALSE;
$instance_id = $auth_transaction->instance_id;
$payment_method = commerce_payment_method_instance_load($instance_id);
$amount = $transaction->amount;
$transaction = commerce_payment_transaction_new('amex_hosted', $auth_transaction->order_id);
$transaction->amount = 0 - commerce_currency_decimal_to_amount($amount, $auth_transaction->currency_code);
$transaction->currency_code = $auth_transaction->currency_code;
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
$transaction->instance_id = $payment_method['instance_id'];
$transaction->remote_id = $auth_transaction->remote_id;
commerce_payment_transaction_save($transaction);
$data = new stdClass();
$data->apiOperation = 'VOID';
$data->transaction['targetTransactionId'] = $auth_transaction->transaction_id;
$url = $payment_method['settings']['txn_url'] . AMEX_TXN_PATH . $payment_method['settings']['merchant_id'] . '/order/' . (10000000000 + $auth_transaction->transaction_id) . '/transaction/' . $transaction->transaction_id;
$result = _commerce_amex_put_request($url, $payment_method['settings']['password'], $data);
$transaction->payload = $result;
switch ($result->result) {
case 'SUCCESS':
$transaction->status = COMMERCE_PAYMENT_STATUS_AMEX_CANCELED;
$auth_transaction->status = COMMERCE_PAYMENT_STATUS_AMEX_CANCELED;
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = 'Void';
$auth_transaction->message = 'Void';
drupal_set_message(t('Transaction was processed succesfully'));
$return = TRUE;
break;
case 'PENDING':
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = $result->response->acquirerMessage;
drupal_set_message(t('Transaction pending'));
$return = TRUE;
break;
case 'FAILURE':
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = $result->response->acquirerMessage;
break;
case 'UNKNOWN':
drupal_set_message(t('There was a problem processing your transaction. Your credit/debit card was not charged. Please try again later.'), 'error');
break;
case 'ERROR':
$transaction = _commerce_amex_error_process($result, $transaction);
break;
}
commerce_payment_transaction_save($transaction);
commerce_payment_transaction_save($auth_transaction);
return $return;
}
/**
* Void an auth transaction
* @param object $auth_transaction
* Transaction to be voided.
* @return boolean
*/
function commerce_amex_referral_transaction($refer_transaction, $auth_code) {
$return = FALSE;
$instance_id = $refer_transaction->instance_id;
$payment_method = commerce_payment_method_instance_load($instance_id);
$amount = $transaction->amount;
$transaction = commerce_payment_transaction_new('amex_hosted', $refer_transaction->order_id);
$transaction->amount = 0;
$transaction->currency_code = $refer_transaction->currency_code;
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
$transaction->instance_id = $payment_method['instance_id'];
$transaction->remote_id = $refer_transaction->remote_id;
commerce_payment_transaction_save($transaction);
$data = new stdClass();
$data->apiOperation = 'REFERRAL';
$data->transaction['reference'] = $refer_transaction->transaction_id;
$data->transaction['authorizationCode'] = $auth_code;
$url = $payment_method['settings']['txn_url'] . AMEX_TXN_PATH . $payment_method['settings']['merchant_id'] . '/order/' . (10000000000 + $auth_transaction->transaction_id) . '/transaction/' . $transaction->transaction_id;
$result = _commerce_amex_put_request($url, $payment_method['settings']['password'], $data);
$transaction->payload = $result;
switch ($result->result) {
case 'SUCCESS':
$transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
$refer_transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = 'Referral';
$refer_transaction->message = 'Referral Authorised';
drupal_set_message(t('Transaction was processed succesfully'));
$return = TRUE;
break;
case 'PENDING':
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = $result->response->acquirerMessage;
drupal_set_message(t('Transaction pending'));
$return = TRUE;
break;
case 'FAILURE':
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = $result->response->acquirerMessage;
break;
case 'UNKNOWN':
drupal_set_message(t('There was a problem processing your transaction. Your credit/debit card was not charged. Please try again later.'), 'error');
break;
case 'ERROR':
$transaction = _commerce_amex_error_process($result, $transaction);
break;
}
commerce_payment_transaction_save($transaction);
commerce_payment_transaction_save($auth_transaction);
return $return;
}
/**
* Refund a transaction
* @param object $capture_transaction
* Transaction to be refunded
* @param decimal $amount
* Ammount to be refunded as a decimal
* @return boolean
*/
function commerce_amex_refund_transaction($capture_transaction, $amount) {
$return = FALSE;
$instance_id = $capture_transaction->instance_id;
$payment_method = commerce_payment_method_instance_load($instance_id);
$auth_transaction_id = $capture_transaction->data->order->reference;
$transaction = commerce_payment_transaction_new('amex_hosted', $capture_transaction->order_id);
$transaction->amount = -commerce_currency_decimal_to_amount($amount, $capture_transaction->currency_code);
$transaction->currency_code = $capture_transaction->currency_code;
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
$transaction->instance_id = $payment_method['instance_id'];
$transaction->remote_id = $capture_transaction->remote_id;
commerce_payment_transaction_save($transaction);
$data = new stdClass();
$data->apiOperation = 'REFUND';
$data->order['reference'] = $auth_transaction_id;
$data->transaction['amount'] = $amount;
$data->transaction['currency'] = $transaction->currency_code;
$data->transaction['reference'] = $transaction->transaction_id;
$data->sourceOfFunds['session'] = $transaction->remote_id;
$data->sourceOfFunds['type'] = 'CARD';
$url = $payment_method['settings']['txn_url'] . AMEX_TXN_PATH . $payment_method['settings']['merchant_id'] . '/order/' . (10000000000 + $auth_transaction_id) . '/transaction/' . $transaction->transaction_id;
$result = _commerce_amex_put_request($url, $payment_method['settings']['password'], $data);
$transaction->payload = $result;
switch ($result->result) {
case 'SUCCESS':
$transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = 'Refunded';
drupal_set_message(t('Transaction was processed succesfully'));
$return = TRUE;
break;
case 'PENDING':
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = $result->response->acquirerMessage;
drupal_set_message(t('Transaction pending'));
$return = TRUE;
break;
case 'FAILURE':
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = $result->response->acquirerMessage;
break;
case 'UNKNOWN':
drupal_set_message(t('There was a problem processing your transaction. Your credit/debit card was not charged. Please try again later.'), 'error');
break;
case 'ERROR':
$transaction = _commerce_amex_error_process($result, $transaction);
break;
}
commerce_payment_transaction_save($transaction);
return $return;
}
/**
* Request a token from Amex for a session and save the Card of File entity.
*
* @param object $order
* The order - used for the user id and billing address.
* @param object $payment_method
* The payment method being used.
* @param object $transaction
* The transaction the card being saved was used in for the remote session id.
* @return boolean
*/
function commerce_amex_cardonfile_save($order, $payment_method, $transaction) {
$data = new stdClass();
$data->sourceOfFunds['session'] = $transaction->remote_id;
$data->sourceOfFunds['type'] = 'CARD';
$url = $payment_method['settings']['txn_url'] . AMEX_TXN_PATH . $payment_method['settings']['merchant_id'] . '/token';
$result = _commerce_amex_post_request($url, $payment_method['settings']['merchant_id'], $payment_method['settings']['password'], $data);
// Process error and return false.
if (isset($result->result) && $result->result == 'ERROR') {
$transaction = _commerce_amex_error_process($result, $transaction);
drupal_set_message(t('Unable to save your card data due to a remote server error'), 'error');
return FALSE;
}
if (isset($result->token)) {
$card_data = commerce_cardonfile_new();
$card_data->uid = $order->uid;
$card_data->payment_method = $payment_method['method_id'];
$card_data->instance_id = $payment_method['instance_id'];
$card_data->remote_id = $result->token;
$card_data->card_type = !empty($result->sourceOfFunds->provided->card->scheme) ? strtolower($result->sourceOfFunds->provided->card->scheme) : 'card';
$card_data->card_name = $transaction->data['owner'];
$card_data->card_number = drupal_substr($result->sourceOfFunds->provided->card->number, -4);
$card_data->card_exp_month = $result->sourceOfFunds->provided->card->expiry->month;
$card_data->card_exp_year = 2000 + $result->sourceOfFunds->provided->card->expiry->year;
$card_data->status = 1;
$billing_profile = commerce_customer_profile_load($order->commerce_customer_billing[LANGUAGE_NONE][0]['profile_id']);
// Save the new card on file.
if (commerce_cardonfile_save($card_data, $billing_profile)) {
return TRUE;
}
else {
drupal_set_message(t('Unable to save your card data due to a server error'), 'error');
return FALSE;
}
}
}
function commerce_amex_cardonfile_update($card) {
return TRUE;
}
/**
* Builds the form for updating cardonfile data.
*
* @param $card_data
* The card on file entity.
*/
function commerce_amex_cardonfile_update_form($form, &$form_state, $card_data) {
// Load the credit card helper functions from the Payment module.
module_load_include('inc', 'commerce_payment', 'includes/commerce_payment.credit_card');
$payment_method = commerce_payment_method_instance_load($card_data->instance_id);
$session = _commerce_amex_request_session($payment_method);
if ($session->result == "SUCCESS") {
$session_id = $session->session;
$form['#action'] = $payment_method['settings']['txn_url'] . '/form/' . $session_id;
$form['#method'] = "post";
$form['card_data'] = array(
'#type' => 'value',
'#value' => $card_data,
);
$default = array(
'exp_month' => $card_data->card_exp_month,
'exp_year' => $card_data->card_exp_year,
);
$current_year_2 = date('y');
$current_year_4 = date('Y');
$form['card_holder'] = array(
'#type' => 'item',
'#title' => t('Card holder'),
'#markup' => $card_data->card_name,
);
$form['card_type'] = array(
'#type' => 'item',
'#title' => t('Card Type'),
'#markup' => !empty($card_types[$card_data->card_type]) ? $card_types[$card_data->card_type] : $card_data->card_type,
);
$form['card_number'] = array(
'#type' => 'item',
'#title' => t('Card Number'),
'#markup' => 'XXXX - XXXX - XXXX - ' . $card_data->card_number,
);
// Always add fields for the credit card expiration date.
$form['gatewayCardExpiryDateMonth'] = array(
'#type' => 'select',
'#title' => t('Expiration'),
'#options' => drupal_map_assoc(array_keys(commerce_months())),
'#default_value' => strlen($default['exp_month']) == 1 ? '0' . $default['exp_month'] : $default['exp_month'],
'#required' => TRUE,
'#prefix' => '<div class="commerce-credit-card-expiration">',
'#suffix' => '<span class="commerce-month-year-divider">/</span>',
// Attache the CSS from commerce_payment module for nice formatting.
'#attached' => array(
'css' => array(
drupal_get_path('module', 'commerce_payment') . '/theme/commerce_payment.theme.css',
),
),
);
// Build a year select list that uses a 4 digit key with a 2 digit value.
$options = array();
for ($i = 0; $i < 20; $i++) {
$options[$current_year_4 + $i] = str_pad($current_year_2 + $i, 2, '0', STR_PAD_LEFT);
}
$form['gatewayCardExpiryDateYear'] = array(
'#type' => 'select',
'#options' => $options,
'#default_value' => $default['exp_year'],
'#suffix' => '</div>',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Update card data'),
'#suffix' => l(t('Cancel'), 'user/' . $card_data->uid . '/cards'),
);
}
else {
$form['error'] = array(
'#type' => 'markup',
'#markup' => t('Unable to contact payment gateway'),
);
}
return $form;
}
/**
* Card on file callback: deletes the associated customer payment profile.
*
* @param array $form
* The card on file delete form array
* @param array $form_state
* The card on file delete form's form state array
* @param object $payment_method
* The payment method instance definition array.
* @param object $card_data
* The stored credit card data array to be processed
*
* @return
* TRUE if the operation was successful
*/
function commerce_amex_cardonfile_delete($form, &$form_state, $payment_method, $card_data) {
$return = FALSE;
$url = $payment_method['settings']['txn_url'] . AMEX_TXN_PATH . $payment_method['settings']['merchant_id'] . '/token/' . $card_data->remote_id;
$result = _commerce_amex_delete_request($url, $payment_method['settings']['password']);
if (isset($result->result) && $result->result == 'SUCCESS') {
$return = TRUE;
}
elseif (isset($result->result) && $result->result == 'ERROR') {
drupal_set_message(t('Unable to delete card due to a remote server error') . 'error');
}
else {
drupal_set_message(t('Unknown error.') . 'error');
}
return $return;
}
/**
* Card on file callback: background charge payment
*
* @param object $payment_method
* The payment method instance definition array.
* @param object $card_data
* The stored credit card data array to be processed
* @param object $order
* The order object that is being processed
* @param array $charge
* The price array for the charge amount with keys of 'amount' and 'currency'
* If null the total value of the order is used.
*
* @return
* TRUE if the transaction was successful
*/
function commerce_amex_cardonfile_charge($payment_method, $card_data, $order, $charge = NULL) {
// Format order total for transaction.
if (isset($charge)) {
$amount = commerce_currency_amount_to_decimal($charge['amount'], $charge['currency_code']);
}
else {
$wrapper = entity_metadata_wrapper('commerce_order', $order);
$charge = commerce_line_items_total($wrapper->commerce_line_items);
$amount = commerce_currency_amount_to_decimal($charge->amount, $charge->currency_code);
}
$order_amount = number_format($amount, 2, '.', '');
// Create a new payment transaction and setup the amount.
$transaction = commerce_payment_transaction_new('amex_hosted', $order->order_id);
$transaction->amount = $charge['amount'];
$transaction->currency_code = $charge['currency_code'];
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
$transaction->instance_id = $payment_method['instance_id'];
commerce_payment_transaction_save($transaction);
$data = new stdClass();
$data->apiOperation = $payment_method['settings']['txn_type'];
// Drupal Commerce Solution ID.
$data->partnerSolutionId = '3304010';
if (isset($card_data->name)) {
$data->sourceOfFunds['provided']['card']['holder']['lastName'] = $card_data->name;
}
$data->sourceOfFunds['token'] = $card_data->remote_id;
$data->sourceOfFunds['type'] = 'CARD';
$data->transaction['amount'] = $order_amount;
$data->transaction['currency'] = $transaction->currency_code;
$data->transaction['reference'] = $transaction->transaction_id;
if ($payment_method['settings']['continuous']) {
$data->transaction['frequency'] = 'RECURRING';
}
$data->order['reference'] = $order->order_id;
// Add the addresses to the data object.
$data = _commerce_amax_add_addresses($data, $order);
$url = $payment_method['settings']['txn_url'] . AMEX_TXN_PATH . $payment_method['settings']['merchant_id'] . '/order/' . (10000000000 + $transaction->transaction_id) . '/transaction/' . $transaction->transaction_id;
$result = _commerce_amex_put_request($url, $payment_method['settings']['password'], $data);
$transaction->payload = $result;
switch ($result->result) {
case 'SUCCESS':
switch ($payment_method['settings']['txn_type']) {
case AMEX_OP_AUTH:
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
break;
case AMEX_OP_PAY:
$transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
}
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = $result->response->acquirerMessage;
break;
case 'PENDING':
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = $result->response->acquirerMessage;
break;
case 'FAILURE':
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = $result->response->gatewayCode;
break;
case 'UNKNOWN':
break;
case 'ERROR':
$transaction = _commerce_amex_error_process($result, $transaction);
break;
}
commerce_payment_transaction_save($transaction);
}
/**
* A wrapper for curl to be reused for Amex requests.
*
* @param string $url
* @param string $password
* @param array $data
* @return multitype:string
*/
function _commerce_amex_request_session($payment_method) {
// Set a one-minute timeout for this script
set_time_limit(60);
$url = $payment_method['settings']['txn_url'] . AMEX_TXN_PATH . $payment_method['settings']['merchant_id'] . '/session';
// Open the cURL session
$curlSession = curl_init();
curl_setopt($curlSession, CURLOPT_URL, $url);
curl_setopt($curlSession, CURLOPT_HEADER, 0);
curl_setopt($curlSession, CURLOPT_POST, 1);
curl_setopt($curlSession, CURLOPT_POSTFIELDS, '');
curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curlSession, CURLOPT_TIMEOUT, 30);
curl_setopt($curlSession, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curlSession, CURLOPT_SSL_VERIFYHOST, 1);
curl_setopt($curlSession, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($curlSession, CURLOPT_USERPWD, ":" . $payment_method['settings']['password']);
//Send the request and store the result in an array
$rawresponse = curl_exec($curlSession);
$response = json_decode($rawresponse);
watchdog('commerce_amex', print_r(curl_getinfo($curlSession), TRUE));
// Close the cURL session
curl_close($curlSession);
// Return the output
return $response;
}
/**
*
* @param unknown $order
* @param unknown $transaction
* @param string $use3Dsecure
* @return unknown
*/
function _commerce_amex_process_transaction($order, $transaction, $use3Dsecure = FALSE) {
// Get the payment method.
$instance_id = $transaction->instance_id;
$payment_method = commerce_payment_method_instance_load($instance_id);
// Format order total for transaction.
$amount = commerce_currency_amount_to_decimal($transaction->amount, $transaction->currency_code);
$order_amount = number_format($amount, 2, '.', '');
$data = new stdClass();
$data->apiOperation = $payment_method['settings']['txn_type'];
// Drupal Commerce Solution ID.
$data->partnerSolutionId = '3304010';
$data->sourceOfFunds['session'] = $transaction->remote_id;
if ($transaction->data['owner']) {
$data->sourceOfFunds['provided']['card']['holder']['lastName'] = $transaction->data['owner'];
}
if (isset($transaction->data['cardonfile_selected']) && ($cardonfile = commerce_cardonfile_load($transaction->data['cardonfile_selected']))) {
$data->sourceOfFunds['token'] = $cardonfile->remote_id;
$data->sourceOfFunds['provided']['card']['holder']['lastName'] = $cardonfile->card_name;
}
$data->sourceOfFunds['type'] = 'CARD';
$data->transaction['amount'] = $order_amount;
$data->transaction['currency'] = $transaction->currency_code;
$data->transaction['reference'] = $transaction->transaction_id;
if (isset($payment_method['settings']['continuous']) && $payment_method['settings']['continuous']) {
$data->transaction['frequency'] = 'RECURRING';
}
if ($use3Dsecure) {
$data->{'3DSecureId'} = $transaction->transaction_id;
}
$data->order['reference'] = $order->order_id;
// Add the addresses to the data object.
$data = _commerce_amax_add_addresses($data, $order);
$url = $payment_method['settings']['txn_url'] . AMEX_TXN_PATH . $payment_method['settings']['merchant_id'] . '/order/' . (10000000000 + $transaction->transaction_id) . '/transaction/' . $transaction->transaction_id;
$result = _commerce_amex_put_request($url, $payment_method['settings']['merchant_id'], $payment_method['settings']['password'], $data);
$transaction->payload = $result;
switch ($result->result) {
case 'SUCCESS':
if (isset($result->response->risk->gatewayCode) && $result->response->risk->gatewayCode == 'REVIEW') {
$transaction->status = COMMERCE_PAYMENT_STATUS_AMEX_REVIEW;
$transaction->remote_status = $result->response->risk->gatewayCode;
$transaction->message = l(t('Visit Amex to Review'), $payment_method['settings']['txn_url'] . '/ma');
}
else {
switch ($payment_method['settings']['txn_type']) {
case AMEX_OP_AUTH:
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
break;
case AMEX_OP_PAY:
$transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
}
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = $result->response->acquirerMessage;
}
drupal_set_message(t('Transaction was processed succesfully'));
if (isset($transaction->data['cardonfile_store']) && $transaction->data['cardonfile_store']) {
commerce_amex_cardonfile_save($order, $payment_method, $transaction);
}
break;
case 'PENDING':
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = $result->response->acquirerMessage;
drupal_set_message(t('Transaction pending'));
break;
case 'FAILURE':
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
$transaction->remote_status = $result->response->gatewayCode;
$transaction->message = $result->response->gatewayCode;
drupal_set_message(t('%message Your credit/debit card was not charged. Please try again later.', array(
'%message' => $transaction->message,
)), 'warning');
break;
case 'UNKNOWN':
drupal_set_message(t('There was a problem processing your transaction. Your credit/debit card was not charged. Please try again later.'), 'warning');
break;
case 'ERROR':
$transaction = _commerce_amex_error_process($result, $transaction);
drupal_set_message(t('There was a problem processing your transaction. Your credit/debit card was not charged. Please try again later.'), 'warning');
commerce_payment_redirect_pane_previous_page($order);
break;
}
commerce_payment_transaction_save($transaction);
return $transaction;
}
/**
* A wrapper for curl to be reused for Amex requests.
*
* @param string $url
* @param string $password
* @param array $data
* @return multitype:string
*/
function _commerce_amex_post_request($url, $merchant_id, $password, $data = array()) {
$output = array();
$data_string = json_encode($data);
$curlSession = curl_init();
curl_setopt($curlSession, CURLOPT_URL, $url);
curl_setopt($curlSession, CURLOPT_HEADER, 0);
curl_setopt($curlSession, CURLOPT_POST, 1);
curl_setopt($curlSession, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($curlSession, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string),
));
curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curlSession, CURLOPT_TIMEOUT, 30);
curl_setopt($curlSession, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curlSession, CURLOPT_SSL_VERIFYHOST, 1);
curl_setopt($curlSession, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($curlSession, CURLOPT_USERPWD, ":" . $password);
$rawresponse = curl_exec($curlSession);
$response = json_decode($rawresponse);
curl_close($curlSession);
return $response;
}
/**
* A wrapper for curl to be reused for Amex requests.
*
* @param string $url
* @param string $password
* @param array $data
* @return multitype:string
*/
function _commerce_amex_put_request($url, $merchant_id, $password, $data = array()) {
// Set a one-minute timeout for this script
set_time_limit(60);
// Initialise output variable
$output = array();
// Encode the data array.
$data_string = json_encode($data);
// Open the cURL session
$curlSession = curl_init();
// Set the URL
curl_setopt($curlSession, CURLOPT_URL, $url);
// No headers, please
curl_setopt($curlSession, CURLOPT_HEADER, 0);
// It's a PUT request
curl_setopt($curlSession, CURLOPT_CUSTOMREQUEST, "PUT");
// Set the fields for the POST
curl_setopt($curlSession, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($curlSession, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string),
));
// Return it direct, don't print it out
curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, 1);
// This connection will timeout in 30 seconds
curl_setopt($curlSession, CURLOPT_TIMEOUT, 30);
//The next two lines must be present for the kit to work with newer version of cURL
//You should remove them if you have any problems in earlier versions of cURL
curl_setopt($curlSession, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curlSession, CURLOPT_SSL_VERIFYHOST, 1);
curl_setopt($curlSession, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($curlSession, CURLOPT_USERPWD, ":" . $password);
//Send the request and store the result in an array
$rawresponse = curl_exec($curlSession);
$response = json_decode($rawresponse);
// Close the cURL session
curl_close($curlSession);
// Return the output
return $response;
}
/**
* A wrapper for curl to be reused for Amex requests.
*
* @param string $url
* @param string $password
* @param array $data
* @return multitype:string
*/
function _commerce_amex_get_request($url, $merchant_id, $password, $data = array()) {
// Set a one-minute timeout for this script
set_time_limit(60);
$output = array();
$data_string = json_encode($data);
$curlSession = curl_init();
curl_setopt($curlSession, CURLOPT_URL, $url);
curl_setopt($curlSession, CURLOPT_HEADER, 0);
curl_setopt($curlSession, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($curlSession, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($curlSession, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string),
));
// Return it direct, don't print it out
curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, 1);
// This connection will timeout in 30 seconds
curl_setopt($curlSession, CURLOPT_TIMEOUT, 30);
//The next two lines must be present for the kit to work with newer version of cURL
//You should remove them if you have any problems in earlier versions of cURL
curl_setopt($curlSession, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curlSession, CURLOPT_SSL_VERIFYHOST, 1);
curl_setopt($curlSession, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($curlSession, CURLOPT_USERPWD, ":" . $password);
//Send the request and store the result in an array
$rawresponse = curl_exec($curlSession);
$response = json_decode($rawresponse);
// Close the cURL session
curl_close($curlSession);
// Return the output
return $response;
}
/**
* A wrapper for curl to be reused for Amex requests.
*
* @param string $url
* @param string $password
* @param array $data
* @return multitype:string
*/
function _commerce_amex_delete_request($url, $password, $data = array()) {
// Set a one-minute timeout for this script
set_time_limit(60);
// Initialise output variable
$output = array();
// Encode the data array.
$data_string = json_encode($data);
// Open the cURL session
$curlSession = curl_init();
// Set the URL
curl_setopt($curlSession, CURLOPT_URL, $url);
// No headers, please
curl_setopt($curlSession, CURLOPT_HEADER, 0);
// It's a PUT request
curl_setopt($curlSession, CURLOPT_CUSTOMREQUEST, "DELETE");
// Set the fields for the POST
curl_setopt($curlSession, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($curlSession, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string),
));
// Return it direct, don't print it out
curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, 1);
// This connection will timeout in 30 seconds
curl_setopt($curlSession, CURLOPT_TIMEOUT, 30);
//The next two lines must be present for the kit to work with newer version of cURL
//You should remove them if you have any problems in earlier versions of cURL
curl_setopt($curlSession, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curlSession, CURLOPT_SSL_VERIFYHOST, 1);
curl_setopt($curlSession, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($curlSession, CURLOPT_USERPWD, ":" . $password);
//Send the request and store the result in an array
$rawresponse = curl_exec($curlSession);
$response = json_decode($rawresponse);
// Close the cURL session
curl_close($curlSession);
// Return the output
return $response;
}
/**
* Add the addresses to the amex post data object
* @param object $data
* The existing Amex data object.
* @param object $order
* The order with the addresses.
* @return object $data
* The Amex data object with added addresses.
*/
function _commerce_amax_add_addresses($data, $order) {
// Load customer profile.
$profile = commerce_customer_profile_load($order->commerce_customer_billing[LANGUAGE_NONE][0]['profile_id']);
// Get user billing address.
$billing_address = $profile->commerce_customer_address[LANGUAGE_NONE][0];
if (isset($order->data['profiles']['customer_profile_shipping'])) {
// Load customer delivery profile.
$profile = commerce_customer_profile_load($order->data['profiles']['customer_profile_shipping']);
// Get user delivery address.
$delivery_address = $profile->commerce_customer_address[LANGUAGE_NONE][0];
}
elseif (isset($order->field_customer_shipping[LANGUAGE_NONE][0]['profile_id'])) {
$profile = commerce_customer_profile_load($order->field_customer_shipping[LANGUAGE_NONE][0]['profile_id']);
// Get user delivery address.
$delivery_address = $profile->commerce_customer_address[LANGUAGE_NONE][0];
}
else {
$delivery_address = $billing_address;
}
$billing_address['country'] = countries_country_lookup($billing_address['country'], 'iso2');
$data->billing['address']['street'] = $billing_address['premise'] . ' ' . $billing_address['thoroughfare'];
$data->billing['address']['postcodeZip'] = $billing_address['postal_code'];
if ($billing_address['locality'] != '') {
$data->billing['address']['city'] = $billing_address['locality'];
}
if ($billing_address['administrative_area'] != '') {
$data->billing['address']['stateProvince'] = $billing_address['administrative_area'];
}
$data->billing['address']['country'] = $billing_address['country']->iso3;
$delivery_address['country'] = countries_country_lookup($delivery_address['country'], 'iso2');
$data->shipping['firstName'] = $delivery_address['first_name'];
$data->shipping['lastName'] = $delivery_address['last_name'];
$data->shipping['address']['street'] = $delivery_address['premise'] . ' ' . $delivery_address['thoroughfare'];
$data->shipping['address']['postcodeZip'] = $delivery_address['postal_code'];
if ($delivery_address['locality'] != '') {
$data->shipping['address']['city'] = $delivery_address['locality'];
}
if ($delivery_address['administrative_area'] != '') {
$data->shipping['address']['stateProvince'] = $delivery_address['administrative_area'];
}
$data->shipping['address']['country'] = $delivery_address['country']->iso3;
return $data;
}
function _commerce_amex_error_process($result, $transaction) {
if (isset($result->error)) {
$transaction->remote_status = $result->error->cause;
switch ($transaction->remote_status) {
case 'INVALID_REQUEST':
case 'SERVER_BUSY':
$transaction->message = $result->error->explanation;
break;
case 'REQUEST_REJECTED':
$transaction->message = 'Request rejected: Code ' . $result->error->supportCode;
break;
case 'SERVER_FAILED':
$transaction->message = 'Remote server error.';
break;
}
commerce_payment_transaction_save($transaction);
}
return $transaction;
}
Functions
Name![]() |
Description |
---|---|
commerce_amex_3d_secure_callback | Process callback response from merchant server |
commerce_amex_3d_secure_check | Check if the card is registered for 3D Secure and redirect if required |
commerce_amex_capture_access | Determines access to the prior authorization capture form for Amex hosted credit card transactions. |
commerce_amex_capture_transaction | Capture an autorised payment. |
commerce_amex_cardonfile_charge | Card on file callback: background charge payment |
commerce_amex_cardonfile_delete | Card on file callback: deletes the associated customer payment profile. |
commerce_amex_cardonfile_save | Request a token from Amex for a session and save the Card of File entity. |
commerce_amex_cardonfile_update | |
commerce_amex_cardonfile_update_form | Builds the form for updating cardonfile data. |
commerce_amex_commerce_checkout_page_info_alter | Implements hook_commerce_checkout_page_info_alter(). |
commerce_amex_commerce_payment_method_info | Implements hook_commerce_payment_method_info(). |
commerce_amex_commerce_payment_transaction_status_info | Implements hool_commerce_payment_transaction_status_info(). |
commerce_amex_hosted_default_settings | Returns the default settings for the Amex hosted payment method. |
commerce_amex_hosted_redirect_form | Payment method callback: redirect form. |
commerce_amex_hosted_redirect_form_submit | Payment method callback: redirect form submit. |
commerce_amex_hosted_redirect_form_validate | Payment method callback: redirect form validate. |
commerce_amex_hosted_settings_form | Payment method callback: settings form. |
commerce_amex_hosted_submit_form_submit | Payment method callback: submit form submission. |
commerce_amex_menu | Implements hook_menu |
commerce_amex_referral_transaction | Void an auth transaction |
commerce_amex_refund_access | Determines access to the prior authorization capture form for Amex hosted credit card transactions. |
commerce_amex_refund_transaction | Refund a transaction |
commerce_amex_update_access | Determines access to the prior authorization capture form for Amex hosted credit card transactions. |
commerce_amex_void_access | Determines access to the prior authorization capture form for Amex hosted credit card transactions. |
commerce_amex_void_transaction | Void an auth transaction |
_commerce_amax_add_addresses | Add the addresses to the amex post data object |
_commerce_amex_delete_request | A wrapper for curl to be reused for Amex requests. |
_commerce_amex_error_process | |
_commerce_amex_get_request | A wrapper for curl to be reused for Amex requests. |
_commerce_amex_post_request | A wrapper for curl to be reused for Amex requests. |
_commerce_amex_process_transaction | |
_commerce_amex_put_request | A wrapper for curl to be reused for Amex requests. |
_commerce_amex_request_session | A wrapper for curl to be reused for Amex requests. |