commerce_authnet.module in Commerce Authorize.Net 7
Same filename and directory in other branches
Implements Authorize.Net payment services for use in Drupal Commerce.
File
commerce_authnet.moduleView source
<?php
/**
* @file
* Implements Authorize.Net payment services for use in Drupal Commerce.
*/
// Authorize.Net transaction mode definitions:
define('AUTHNET_TXN_MODE_LIVE', 'live');
define('AUTHNET_TXN_MODE_LIVE_TEST', 'live_test');
define('AUTHNET_TXN_MODE_DEVELOPER', 'developer');
/**
* Implements hook_library().
*/
function commerce_authnet_library() {
$path = drupal_get_path('module', 'commerce_authnet');
$libraries['commerce_authnet.acceptjs.sandbox'] = array(
'title' => 'Accept.JS Sandbox',
'website' => 'https://developer.authorize.net/api/reference/features/acceptjs.html',
'version' => 'v1',
'js' => array(
'https://jstest.authorize.net/v1/Accept.js' => array(
'type' => 'external',
),
),
);
$libraries['commerce_authnet.acceptjs.production'] = array(
'title' => 'Accept.JS Production',
'website' => 'https://developer.authorize.net/api/reference/features/acceptjs.html',
'version' => 'v1',
'js' => array(
'https://js.authorize.net/v1/Accept.js' => array(
'type' => 'external',
),
),
);
$libraries['commerce_authnet.acceptjs.accept'] = array(
'title' => 'Accept.JS Form',
'version' => VERSION,
'js' => array(
$path . '/js/commerce_authnet.form.js' => array(),
$path . '/js/commerce_authnet.accept.form.js' => array(),
),
);
return $libraries;
}
/**
* Implements hook_menu().
*/
function commerce_authnet_menu() {
$items = array();
// Add a menu item for capturing authorizations.
$items['admin/commerce/orders/%commerce_order/payment/%commerce_payment_transaction/authnet-aim-capture'] = array(
'title' => 'Capture',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'commerce_authnet_aim_capture_form',
3,
5,
),
'access callback' => 'commerce_authnet_aim_capture_access',
'access arguments' => array(
3,
5,
),
'type' => MENU_DEFAULT_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
'weight' => 2,
'file' => 'includes/commerce_authnet.admin.inc',
);
// Add a menu item for voiding transactions.
$items['admin/commerce/orders/%commerce_order/payment/%commerce_payment_transaction/authnet-aim-void'] = array(
'title' => 'Void',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'commerce_authnet_aim_void_form',
3,
5,
),
'access callback' => 'commerce_authnet_aim_void_access',
'access arguments' => array(
3,
5,
),
'type' => MENU_DEFAULT_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
'weight' => 2,
'file' => 'includes/commerce_authnet.admin.inc',
);
// Add a menu item for issuing credits.
$items['admin/commerce/orders/%commerce_order/payment/%commerce_payment_transaction/authnet-aim-credit'] = array(
'title' => 'Credit',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'commerce_authnet_aim_credit_form',
3,
5,
),
'access callback' => 'commerce_authnet_aim_credit_access',
'access arguments' => array(
3,
5,
),
'type' => MENU_DEFAULT_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
'weight' => 2,
'file' => 'includes/commerce_authnet.admin.inc',
);
return $items;
}
/**
* Determines access to the prior authorization capture form for Authorize.Net
* AIM 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_authnet_aim_capture_access($order, $transaction) {
// Return FALSE if the transaction isn't for Authorize.Net AIM or isn't
// awaiting capture.
if (!in_array($transaction->payment_method, array(
'authnet_aim',
'authnet_acceptjs',
)) || empty($transaction->remote_id) || strtoupper($transaction->remote_status) != 'AUTH_ONLY') {
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 void form for Authorize.Net AIM credit card
* transactions
*
* @param $order
* The order the transaction is on.
* @param $transaction
* The payment transaction object to be voided.
*
* @return
* TRUE or FALSE indicating void access.
*/
function commerce_authnet_aim_void_access($order, $transaction) {
if (!in_array($transaction->payment_method, array(
'authnet_aim',
'authnet_acceptjs',
)) || empty($transaction->remote_id) || !in_array(strtoupper($transaction->remote_status), array(
'AUTH_ONLY',
'PRIOR_AUTH_CAPTURE',
'AUTH_CAPTURE',
))) {
return FALSE;
}
// Return FALSE if it is more than 24 hours since the last update to the
// transaction, as it will already have been settled.
if (time() - $transaction->changed > 3600 * 24) {
return FALSE;
}
// Allow access if the user can update this transaction.
return commerce_payment_transaction_access('update', $transaction);
}
/**
* Determines access to the credit form for successful Authorize.Net AIM credit
* card transactions.
*
* @param $order
* The order the transaction is on.
* @param $transaction
* The payment transaction object to be credited.
*
* @return
* TRUE or FALSE indicating credit access.
*/
function commerce_authnet_aim_credit_access($order, $transaction) {
// Return FALSE if the transaction isn't for Authorize.Net AIM, doesn't have a
// success status or has an amount of 0 or less.
if (!in_array($transaction->payment_method, array(
'authnet_aim',
'authnet_acceptjs',
)) || $transaction->status != 'success' || $transaction->amount <= 0) {
return FALSE;
}
// Return FALSE if it is more than 120 days past the original capture.
if (time() - $transaction->created > 86400 * 120) {
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_authnet_commerce_payment_method_info() {
$payment_methods = array();
$payment_methods['authnet_aim'] = array(
'base' => 'commerce_authnet_aim',
'title' => t('Authorize.Net AIM - Credit Card'),
'short_title' => t('Authorize.Net CC'),
'display_title' => t('Credit card'),
'description' => t('Integrates Authorize.Net Advanced Integration Method for card not present CC transactions.'),
'cardonfile' => array(
'create callback' => 'commerce_authnet_cim_cardonfile_create',
'charge callback' => 'commerce_authnet_cim_cardonfile_charge',
'update callback' => 'commerce_authnet_cim_cardonfile_update',
'delete callback' => 'commerce_authnet_cim_cardonfile_delete',
),
);
$payment_methods['authnet_acceptjs'] = array(
'base' => 'commerce_authnet_acceptjs',
'title' => t('Authorize.Net Accept.JS - Credit Card'),
'short_title' => t('Authorize.Net Accept.JS'),
'display_title' => t('Credit card'),
'description' => t('Integrates Authorize.Net Accept.JS for card not present CC transactions.'),
'file' => 'includes/commerce_authnet.acceptjs.inc',
'cardonfile' => array(
'create callback' => 'commerce_authnet_acceptjs_cardonfile_create',
'charge callback' => 'commerce_authnet_cim_cardonfile_charge',
'update callback' => 'commerce_authnet_cim_cardonfile_update',
'delete callback' => 'commerce_authnet_cim_cardonfile_delete',
),
);
return $payment_methods;
}
/**
* Returns the default settings for the Authorize.Net AIM payment method.
*/
function commerce_authnet_aim_default_settings() {
return array(
'login' => '',
'tran_key' => '',
'txn_mode' => AUTHNET_TXN_MODE_LIVE_TEST,
'txn_type' => COMMERCE_CREDIT_AUTH_CAPTURE,
'cardonfile' => FALSE,
'continuous' => FALSE,
'email_customer' => FALSE,
'log' => array(
'request' => '0',
'response' => '0',
),
'card_types' => array(),
);
}
/**
* Payment method callback: settings form.
*/
function commerce_authnet_aim_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_authnet_aim_default_settings();
$form = array();
$form['login'] = array(
'#type' => 'textfield',
'#title' => t('API Login ID'),
'#description' => t('Your API Login ID is different from the username you use to login to your Authorize.Net account. Once you login, browse to your Account tab and click the <em>API Login ID and Transaction Key</em> link to find your API Login ID. If you are using a new Authorize.Net account, you may still need to generate an ID.'),
'#default_value' => $settings['login'],
'#required' => TRUE,
);
$form['tran_key'] = array(
'#type' => 'textfield',
'#title' => t('Transaction Key'),
'#description' => t('Your Transaction Key can be found on the same screen as your API Login ID. However, it will not be readily displayed. You must answer your security question and submit a form to see your Transaction Key.'),
'#default_value' => $settings['tran_key'],
'#required' => TRUE,
);
$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.') . '<br />' . t('Only specify a developer test account if you login to your account through https://test.authorize.net.'),
'#options' => array(
AUTHNET_TXN_MODE_LIVE => t('Live transactions in a live account'),
AUTHNET_TXN_MODE_LIVE_TEST => t('Test transactions in a live account'),
AUTHNET_TXN_MODE_DEVELOPER => t('Developer test account transactions'),
),
'#default_value' => $settings['txn_mode'],
);
$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(
COMMERCE_CREDIT_AUTH_CAPTURE => t('Authorization and capture'),
COMMERCE_CREDIT_AUTH_ONLY => t('Authorization only (requires manual or automated capture after checkout)'),
),
'#default_value' => $settings['txn_type'],
);
$form['card_types'] = array(
'#type' => 'checkboxes',
'#title' => t('Limit accepted credit cards to the following types'),
'#description' => t('If you want to limit acceptable card types, you should only select those supported by your merchant account.') . '<br />' . t('If none are checked, any credit card type will be accepted.'),
'#options' => commerce_payment_credit_card_types(),
'#default_value' => $settings['card_types'],
);
// CIM support in conjunction with AIM requires the Card on File module.
if (module_exists('commerce_cardonfile')) {
$form['cardonfile'] = array(
'#type' => 'checkbox',
'#title' => t('Enable Card on File functionality with this payment method using Authorize.Net CIM.'),
'#description' => t('This requires an Authorize.Net account upgraded to include support for CIM (Customer Information Manager).'),
'#default_value' => $settings['cardonfile'],
);
$form['continuous'] = array(
'#type' => 'checkbox',
'#title' => t('Use continuous authority transactions.'),
'#description' => t('A continuous authority merchant account 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.'),
);
}
$form['email_customer'] = array(
'#type' => 'checkbox',
'#title' => t('Tell Authorize.net to e-mail the customer a receipt based on your account settings.'),
'#default_value' => $settings['email_customer'],
);
$form['log'] = array(
'#type' => 'checkboxes',
'#title' => t('Log the following messages for debugging'),
'#options' => array(
'request' => t('API request messages'),
'response' => t('API response messages'),
),
'#default_value' => $settings['log'],
);
return $form;
}
/**
* Payment method callback: checkout form.
*/
function commerce_authnet_aim_submit_form($payment_method, $pane_values, $checkout_pane, $order) {
module_load_include('inc', 'commerce_payment', 'includes/commerce_payment.credit_card');
// Prepare the fields to include on the credit card form.
$fields = array(
'code' => '',
);
// Add the credit card types array if necessary.
if (isset($payment_method['settings']['card_types'])) {
$card_types = array_diff(array_values($payment_method['settings']['card_types']), array(
0,
));
if (!empty($card_types)) {
$fields['type'] = $card_types;
}
}
return commerce_payment_credit_card_form($fields);
}
/**
* Payment method callback: checkout form validation.
*/
function commerce_authnet_aim_submit_form_validate($payment_method, $pane_form, $pane_values, $order, $form_parents = array()) {
// If the customer specified a card on file, skip the normal validation.
if (module_exists('commerce_cardonfile') && !empty($payment_method['settings']['cardonfile']) && !empty($pane_values['cardonfile']) && $pane_values['cardonfile'] !== 'new') {
return;
}
module_load_include('inc', 'commerce_payment', 'includes/commerce_payment.credit_card');
// Validate the credit card fields.
$settings = array(
'form_parents' => array_merge($form_parents, array(
'credit_card',
)),
);
if (!commerce_payment_credit_card_validate($pane_values['credit_card'], $settings)) {
return FALSE;
}
}
/**
* Payment method callback: checkout form submission.
*/
function commerce_authnet_aim_submit_form_submit($payment_method, $pane_form, $pane_values, $order, $charge) {
// If Card on File is enabled and active for this payment method, then we need
// to figure out what to do.
if (module_exists('commerce_cardonfile') && $payment_method['settings']['cardonfile'] && !empty($pane_values['cardonfile'])) {
if (!empty($pane_values['cardonfile']) && $pane_values['cardonfile'] !== 'new') {
// We're using a stored payment profile. Pass it along to CIM.
return commerce_authnet_cim_submit_form_submit($payment_method, $pane_form, $pane_values, $order, $charge);
}
else {
if (!empty($pane_values['credit_card']['cardonfile_store']) && $pane_values['credit_card']['cardonfile_store']) {
// We've got a request to store a new card.
return commerce_authnet_cim_submit_new_card_form_submit($payment_method, $pane_form, $pane_values, $order, $charge);
}
}
}
// Determine the credit card type if possible for use in later code.
if (!empty($pane_values['credit_card']['number'])) {
module_load_include('inc', 'commerce_payment', 'includes/commerce_payment.credit_card');
$card_type = commerce_payment_validate_credit_card_type($pane_values['credit_card']['number'], array_keys(commerce_payment_credit_card_types()));
}
// If the charge amount is 0...
if ($charge['amount'] == 0) {
// Prevent the transaction except under limited circumstances.
$prevent_transaction = TRUE;
// Allow 0 amount authorizations on Visa cards.
if ($payment_method['settings']['txn_type'] == COMMERCE_CREDIT_AUTH_ONLY && $card_type == 'visa') {
$prevent_transaction = FALSE;
}
// If the transaction should still be prevented...
if ($prevent_transaction) {
// Create a transaction to log the skipped transaction and display a
// helpful message to the customer.
$transaction = commerce_payment_transaction_new('authnet_aim', $order->order_id);
$transaction->amount = $charge['amount'];
$transaction->currency_code = $charge['currency_code'];
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
$transaction->message = t('Invalid @amount transaction not attempted.', array(
'@amount' => commerce_currency_format($charge['amount'], $charge['currency_code']),
));
commerce_payment_transaction_save($transaction);
drupal_set_message(t('We encountered an error processing your transaction. Please contact us to resolve the issue.'), 'error');
return FALSE;
}
}
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
// Get the default transaction type from the payment method settings.
$txn_type = $payment_method['settings']['txn_type'];
// If txn_type has been specified in the pane values array, such as through
// the special select element we alter onto the payment terminal form, use
// that instead.
if (!empty($pane_values['txn_type'])) {
$txn_type = $pane_values['txn_type'];
}
// Build a name-value pair array for this transaction.
$nvp = array(
'x_type' => commerce_authnet_txn_type($txn_type),
'x_method' => 'CC',
'x_amount' => number_format(commerce_currency_amount_to_decimal($charge['amount'], $charge['currency_code']), 2, '.', ''),
'x_currency_code' => $charge['currency_code'],
'x_card_num' => $pane_values['credit_card']['number'],
'x_exp_date' => $pane_values['credit_card']['exp_month'] . $pane_values['credit_card']['exp_year'],
);
if (isset($pane_values['credit_card']['code'])) {
$nvp['x_card_code'] = $pane_values['credit_card']['code'];
}
// Build a description for the order.
$description = array();
// Descriptions come from products, though not all environments have them.
// So check first.
if (function_exists('commerce_product_line_item_types')) {
foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
if (in_array($line_item_wrapper->type
->value(), commerce_product_line_item_types())) {
$description[] = round($line_item_wrapper->quantity
->value(), 2) . 'x ' . $line_item_wrapper->line_item_label
->value();
}
}
}
// Add additional transaction invormation to the request array.
$nvp += array(
// Order Information.
'x_invoice_num' => $order->order_number,
'x_description' => substr(implode(', ', $description), 0, 255),
// Customer Information.
'x_email' => substr($order->mail, 0, 255),
'x_cust_id' => substr($order->uid, 0, 20),
'x_customer_ip' => substr(ip_address(), 0, 15),
);
// Prepare the billing address for use in the request.
if (isset($order->commerce_customer_billing) && $order_wrapper->commerce_customer_billing
->value()) {
$billing_address = $order_wrapper->commerce_customer_billing->commerce_customer_address
->value();
if (empty($billing_address['first_name'])) {
$name_parts = explode(' ', $billing_address['name_line']);
$billing_address['first_name'] = array_shift($name_parts);
$billing_address['last_name'] = implode(' ', $name_parts);
}
$nvp += array(
// Customer Billing Address
'x_first_name' => substr($billing_address['first_name'], 0, 50),
'x_last_name' => substr($billing_address['last_name'], 0, 50),
'x_company' => substr($billing_address['organisation_name'], 0, 50),
'x_address' => substr($billing_address['thoroughfare'], 0, 60),
'x_city' => substr($billing_address['locality'], 0, 40),
'x_state' => substr($billing_address['administrative_area'], 0, 40),
'x_zip' => substr($billing_address['postal_code'], 0, 20),
'x_country' => $billing_address['country'],
);
}
// Prepare the shipping address for use in the request.
if (isset($order->commerce_customer_shipping) && $order_wrapper->commerce_customer_shipping
->value()) {
$shipping_address = $order_wrapper->commerce_customer_shipping->commerce_customer_address
->value();
if (empty($shipping_address['first_name'])) {
$name_parts = explode(' ', $shipping_address['name_line']);
$shipping_address['first_name'] = array_shift($name_parts);
$shipping_address['last_name'] = implode(' ', $name_parts);
}
$nvp += array(
// Customer shipping Address
'x_ship_to_first_name' => substr($shipping_address['first_name'], 0, 50),
'x_ship_to_last_name' => substr($shipping_address['last_name'], 0, 50),
'x_ship_to_company' => substr($shipping_address['organisation_name'], 0, 50),
'x_ship_to_address' => substr($shipping_address['thoroughfare'], 0, 60),
'x_ship_to_city' => substr($shipping_address['locality'], 0, 40),
'x_ship_to_state' => substr($shipping_address['administrative_area'], 0, 40),
'x_ship_to_zip' => substr($shipping_address['postal_code'], 0, 20),
'x_ship_to_country' => $shipping_address['country'],
);
}
// Submit the request to Authorize.Net.
$response = commerce_authnet_aim_request($payment_method, $nvp);
// Prepare a transaction object to log the API response.
$transaction = commerce_payment_transaction_new('authnet_aim', $order->order_id);
$transaction->instance_id = $payment_method['instance_id'];
$transaction->remote_id = $response[6];
$transaction->amount = $charge['amount'];
$transaction->currency_code = $charge['currency_code'];
$transaction->payload[REQUEST_TIME] = $response;
// If we didn't get an approval response code...
if ($response[0] != '1') {
// Create a failed transaction with the error message.
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
}
else {
// Set the transaction status based on the type of transaction this was.
switch ($txn_type) {
case COMMERCE_CREDIT_AUTH_ONLY:
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
break;
case COMMERCE_CREDIT_AUTH_CAPTURE:
$transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
break;
case COMMERCE_CREDIT_CAPTURE_ONLY:
$transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
break;
}
}
// Store the type of transaction in the remote status.
$transaction->remote_status = $response[11];
// Build a meaningful response message.
$message = array(
'<b>' . commerce_authnet_reverse_txn_type($response[11]) . '</b>',
'<b>' . ($response[0] != '1' ? t('REJECTED') : t('ACCEPTED')) . ':</b> ' . check_plain($response[3]),
t('AVS response: @avs', array(
'@avs' => commerce_authnet_avs_response($response[5]),
)),
);
// Add the CVV response if enabled.
if (isset($nvp['x_card_code'])) {
$message[] = t('CVV match: @cvv', array(
'@cvv' => commerce_authnet_cvv_response($response[38]),
));
}
$transaction->message = implode('<br />', $message);
// Save the transaction information.
commerce_payment_transaction_save($transaction);
// If the payment failed, display an error and rebuild the form.
if ($response[0] != '1') {
drupal_set_message(t('We received the following error processing your card. Please enter your information again or try a different card.'), 'error');
drupal_set_message(check_plain($response[3]), 'error');
return FALSE;
}
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function commerce_authnet_form_commerce_payment_order_transaction_add_form_alter(&$form, &$form_state) {
// If the payment terminal is displayed for an authnet_aim transaction...
if (!empty($form['payment_terminal']) && in_array($form_state['payment_method']['method_id'], array(
'authnet_aim',
'authnet_acceptjs',
))) {
// Add a select list to let the administrator choose a different transaction
// type than the payment method's default.
$form['payment_terminal']['payment_details']['txn_type'] = array(
'#type' => 'select',
'#title' => t('Transaction type'),
'#options' => array(
COMMERCE_CREDIT_AUTH_ONLY => t('Authorization only'),
COMMERCE_CREDIT_AUTH_CAPTURE => t('Authorization and capture'),
),
'#default_value' => $form_state['payment_method']['settings']['txn_type'],
);
}
}
/**
* Handles advanced logic relating to creating CIM orders with new card data.
*
* @see commerce_authnet_aim_submit_form_submit()
*/
function commerce_authnet_cim_submit_new_card_form_submit($payment_method, $pane_form, $pane_values, $order, $charge) {
// At this point, we have a few choices to make. We know the user is logged in
// and so we know if they have any existing card on file profiles. If they do
// not, then we can assume that they need a new profile. If they do have one,
// then we need take appropriate steps to add the CC info to the existing
// profile and process the payment based on their profile.
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$payment_details = array(
'cardNumber' => $pane_values['credit_card']['number'],
'expirationDate' => $pane_values['credit_card']['exp_year'] . '-' . $pane_values['credit_card']['exp_month'],
);
if (isset($pane_values['credit_card']['code'])) {
$payment_details['cardCode'] = $pane_values['credit_card']['code'];
}
// Prepare the billing address for use in the request.
if (isset($order->commerce_customer_billing) && $order_wrapper->commerce_customer_billing
->value()) {
$billing_address = $order_wrapper->commerce_customer_billing->commerce_customer_address
->value();
if (empty($billing_address['first_name'])) {
$name_parts = explode(' ', $billing_address['name_line']);
$billing_address['first_name'] = array_shift($name_parts);
$billing_address['last_name'] = implode(' ', $name_parts);
}
}
$remote_id = FALSE;
// First look to see if we already have cards on file for the user.
$stored_cards = array();
if (!user_is_anonymous()) {
$stored_cards = commerce_cardonfile_load_multiple_by_uid($order->uid, $payment_method['instance_id']);
}
$add_to_profile = NULL;
if (empty($stored_cards)) {
// We do not, create the profile (which includes the payment details).
if ($response = commerce_authnet_cim_create_customer_profile_request($payment_method, $order, $payment_details)) {
// If the Customer Profile creation was a success, store the new card on
// file data locally.
if ((string) $response->messages->resultCode == 'Ok') {
// Build a remote ID that includes the Customer Profile ID and the
// Payment Profile ID.
$remote_id = (string) $response->customerProfileId . '|' . (string) $response->customerPaymentProfileIdList->numericString;
}
elseif ((string) $response->messages->message->code == 'E00039') {
// But if a Customer Profile already existed for this user, attempt
// instead to add this card as a new Payment Profile to it.
$result = array_filter(explode(' ', (string) $response->messages->message->text), 'is_numeric');
$add_to_profile = reset($result);
}
}
}
else {
// Extract the user's Customer Profile ID from the first card's remote ID.
$card_data = reset($stored_cards);
list($cim_customer_profile_id, ) = explode('|', $card_data->remote_id);
// Attempt to add the card as a new payment profile to this Customer Profile.
$add_to_profile = $cim_customer_profile_id;
}
// Attempt to add the card to an existing Customer Profile if specified.
if (!empty($add_to_profile)) {
$response = commerce_authnet_cim_create_customer_payment_profile_request($payment_method, $add_to_profile, $order, $payment_details);
// If the Payment Profile creation was a success, store the new card on
// file data locally.
if ((string) $response->messages->resultCode == 'Ok' || (string) $response->messages->message->code == 'E00039') {
// If we got a duplicate code, then the payment profile
// at Authorize.Net and needs to be represented locally.
if ((string) $response->messages->message->code == 'E00039') {
$cim_profile_response = commerce_authnet_cim_get_customer_profile_request($payment_method, $add_to_profile);
if ((string) $cim_profile_response->messages->resultCode == 'Ok') {
// Inspect the returned payment profiles to find the one that
// generated the duplicate error code.
$cim_payment_profiles = $cim_profile_response->profile->paymentProfiles;
if (!is_array($cim_payment_profiles)) {
$cim_payment_profiles = array(
$cim_payment_profiles,
);
}
foreach ($cim_payment_profiles as $key => $payment_profile) {
// We match the submitted values against the existing payment
// profiles using the last 4 digits of the card number. This could
// potentially create a conflict if the same customer has two
// different cards that end in the same four digits, but that is
// highly unlikely.
if (substr($pane_values['credit_card']['number'], -4) == substr($payment_profile->payment->creditCard->cardNumber, -4)) {
$payment_profile_id = (string) $payment_profile->customerPaymentProfileId;
break;
}
}
}
}
else {
$payment_profile_id = (string) $response->customerPaymentProfileId;
}
// Build a remote ID that includes the customer profile ID and the new
// or existing payment profile ID. We don't do any check here to ensure
// we found a payment profile ID, as we shouldn't have got a duplicate
// error if it didn't actually exist.
$remote_id = $add_to_profile . '|' . $payment_profile_id;
}
elseif ($response->messages->message->code == 'E00040') {
// But if we could not find a customer profile, create a new one.
if ($response = commerce_authnet_cim_create_customer_profile_request($payment_method, $order, $payment_details)) {
// If the Customer Profile creation was a success, store the new card on
// file data locally.
if ((string) $response->messages->resultCode == 'Ok') {
// Build a remote ID that includes the Customer Profile ID and the
// Payment Profile ID.
$remote_id = (string) $response->customerProfileId . '|' . (string) $response->customerPaymentProfileIdList->numericString;
}
}
}
}
// We couldn't store a profile. Abandon ship!
if (!$remote_id) {
return FALSE;
}
// Build our card for storing with card on file.
$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 = $remote_id;
$card_data->card_type = !empty($card_type) ? $card_type : 'card';
$card_data->card_name = !empty($billing_address['name_line']) ? $billing_address['name_line'] : '';
$card_data->card_number = substr($pane_values['credit_card']['number'], -4);
$card_data->card_exp_month = $pane_values['credit_card']['exp_month'];
$card_data->card_exp_year = $pane_values['credit_card']['exp_year'];
$card_data->status = 1;
if (isset($pane_values['cardonfile_instance_default'])) {
$card_data->instance_default = $pane_values['cardonfile_instance_default'];
}
// Save and log the creation of the new card on file.
commerce_cardonfile_save($card_data);
if ($add_to_profile) {
watchdog('commerce_authnet', 'CIM Payment Profile added to Customer Profile @profile_id for user @uid.', array(
'@profile_id' => $add_to_profile,
'@uid' => $order->uid,
));
}
else {
watchdog('commerce_authnet', 'CIM Customer Profile @profile_id created and saved to user @uid.', array(
'@profile_id' => (string) $response->customerProfileId,
'@uid' => $order->uid,
));
}
// Process the payment
return commerce_authnet_cim_cardonfile_charge($payment_method, $card_data, $order, $charge);
}
/**
* Imitates the checkout form submission callback for the AIM payment method.
*/
function commerce_authnet_cim_submit_form_submit($payment_method, $pane_form, $pane_values, $order, $charge) {
// First attempt to load the card on file.
$card_data = commerce_cardonfile_load($pane_values['cardonfile']);
// Fail now if it is no longer available or the card is inactive.
if (empty($card_data) || $card_data->status == 0) {
drupal_set_message(t('The requested card on file is no longer valid.'), 'error');
return FALSE;
}
return commerce_authnet_cim_cardonfile_charge($payment_method, $card_data, $order, $charge);
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function commerce_authnet_form_commerce_cardonfile_update_form_alter(&$form, &$form_state) {
// Extract the card data from the form and load the payment method instance.
$card_data = $form['card_data']['#value'];
$payment_method = commerce_payment_method_instance_load($card_data->instance_id);
// If this is not an Authorize.Net card then bail.
if ($payment_method['method_id'] != 'authnet_aim') {
return;
}
// Extract the Customer Profile and Payment Profile IDs from the remote_id.
list($cim_customer_profile_id, $cim_payment_profile_id) = explode('|', $card_data->remote_id);
// Load the full payment profile from Authorize.Net.
$payment_profile_xml = commerce_authnet_cim_get_customer_payment_profile_request($payment_method, $cim_customer_profile_id, $cim_payment_profile_id);
if ($payment_profile_xml->messages->message->code == 'I00001') {
$billto = $payment_profile_xml->paymentProfile->billTo;
$address = array(
'<strong>' . t('Billing address:') . '</strong>',
(string) $billto->firstName . ' ' . (string) $billto->lastName,
(string) $billto->company,
(string) $billto->address,
(string) $billto->city . ', ' . (string) $billto->state . ' ' . (string) $billto->zip,
(string) $billto->country,
);
// Add the address info to the form.
$form['billto'] = array(
'#type' => 'markup',
'#markup' => '<div class="commerce-authnet-cim-billto">' . implode('<br />', array_diff($address, array(
'',
))) . '</div>',
'#weight' => -50,
);
$form_state['billto_xml'] = $billto;
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Adds a commerce customer billing address field to the cardonfile form.
*/
function commerce_authnet_form_commerce_cardonfile_card_form_alter(&$form, &$form_state) {
// Make sure this is the right payment method and the cardonfile create form.
if (in_array($form_state['card']->payment_method, array(
'authnet_aim',
'authnet_acceptjs',
)) && $form_state['op'] == 'create') {
// Hide the existing name field.
$form['credit_card']['owner']['#access'] = FALSE;
// Create a billing profile object and add the address form.
$profile = commerce_customer_profile_new('billing', $form_state['card']->uid);
$form_state['commerce_customer_profile'] = $profile;
$form['commerce_customer_profile'] = array();
field_attach_form('commerce_customer_profile', $profile, $form['commerce_customer_profile'], $form_state);
$form['commerce_customer_profile']['#weight'] = -2;
// Add a validation callback so that we can call field_attach functions.
$form['#validate'][] = 'commerce_authnet_cim_cardonfile_create_validate';
}
if ($form_state['card']->payment_method == 'authnet_acceptjs' && $form_state['op'] == 'create') {
unset($form['credit_card']);
$form['#validate'] = array(
'commerce_authnet_acceptjs_cardonfile_create_validate',
'commerce_authnet_cim_cardonfile_create_validate',
);
$payment_method = commerce_payment_method_instance_load($form_state['card']->instance_id);
$form += commerce_authnet_acceptjs_form_elements($payment_method);
$types = array(
'visa',
'mastercard',
'amex',
);
$form['credit_card']['type'] = array(
'#type' => 'select',
'#title' => t('Card type'),
'#options' => array_intersect_key(commerce_payment_credit_card_types(), drupal_map_assoc((array) $types)),
'#default_value' => '',
);
$form['credit_card']['valid_types'] = array(
'#type' => 'value',
'#value' => $types,
);
$form['commerce_customer_profile']['#weight'] = -2;
$form['credit_card']['#weight'] = -1;
$form['#submit'] = array(
'commerce_authnet_acceptjs_cardonfile_create_submit',
);
}
}
/**
* Validation callback for card on file create.
*/
function commerce_authnet_cim_cardonfile_create_validate($form, &$form_state) {
$profile = $form_state['commerce_customer_profile'];
field_attach_form_validate('commerce_customer_profile', $profile, $form['commerce_customer_profile'], $form_state);
}
/**
* Validation callback for AcceptJS card on file create.
*/
function commerce_authnet_acceptjs_cardonfile_create_validate($form, &$form_state) {
if (empty($form_state['values']['data_descriptor']) || empty($form_state['values']['data_value'])) {
form_set_error('errors', t('There was an error.'));
}
}
function commerce_authnet_acceptjs_cardonfile_create_submit($form, &$form_state) {
$op = $form_state['op'];
$card = $form_state['card'];
// Invoke the payment method's card create/update callback.
$payment_method = commerce_payment_method_instance_load($card->instance_id);
$callback = $payment_method['cardonfile'][$op . ' callback'];
$success = FALSE;
if (function_exists($callback)) {
$callback_return = $callback($form, $form_state, $payment_method, $card);
if ($callback_return) {
if ($op == 'create') {
$card_save = $callback_return;
$confirm_message = t('A new card has been added.');
}
else {
$card_save = $card;
$confirm_message = t('The card has been updated.');
}
commerce_cardonfile_save($card_save);
drupal_set_message($confirm_message);
$success = TRUE;
}
}
if (!$success) {
if ($op == 'create') {
drupal_set_message(t('We encountered an error attempting to save your card data. Please try again and contact us if this error persists.'), 'error');
}
else {
drupal_set_message(t('We encountered an error attempting to update your card data. Please try again and contact us if this error persists.'), 'error');
}
}
$form_state['redirect'] = 'user/' . $card->uid . '/cards';
}
/**
* Commerce Card on File create callback.
*
* @param array $form
* The card on file create form.
* @param array $form_state
* The card on file create form state.
* @param array $payment_method
* The payment method for the card on file request.
* @param object $card_data
* The commerce_cardonfile entity.
*
* @return object|bool
* A new commerce_cardonfile entity or FALSE if there was an error.
*/
function commerce_authnet_cim_cardonfile_create($form, &$form_state, $payment_method, $card_data) {
$account = user_load($card_data->uid);
// Submit the profile to the field attach handlers.
$profile = $form_state['commerce_customer_profile'];
field_attach_submit('commerce_customer_profile', $profile, $form['commerce_customer_profile'], $form_state);
commerce_customer_profile_save($profile);
// Format the address into the Auth.net schema.
$profile_wrapper = entity_metadata_wrapper('commerce_customer_profile', $profile);
$billto = commerce_authnet_cim_billto_array(NULL, $profile_wrapper->commerce_customer_address
->value());
// Build the card data to submit to Auth.net.
$number = $form_state['values']['credit_card']['number'];
$card_data->card_exp_month = $form_state['values']['credit_card']['exp_month'];
$card_data->card_exp_year = $form_state['values']['credit_card']['exp_year'];
$card_expire = $card_data->card_exp_year . '-' . $card_data->card_exp_month;
$card_code = $form_state['values']['credit_card']['code'];
$card_type = $form_state['values']['credit_card']['type'];
// Attempt to load and existing profile id from the customers card data.
$existing_cards = commerce_cardonfile_load_multiple_by_uid($account->uid, $payment_method['instance_id']);
// Check for a remote ID.
$remote_id = NULL;
if (!empty($existing_cards)) {
$existing_card = reset($existing_cards);
$remote_id = $existing_card->remote_id;
}
if ($remote_id) {
// Extract the profile id from the remote id.
list($cim_customer_profile_id, $cim_customer_payment_id) = explode('|', $remote_id);
// Build a request to add a new payment method to an existing profile.
$api_request_data = array(
'customerProfileId' => $cim_customer_profile_id,
'paymentProfile' => array(
'billTo' => $billto,
'payment' => array(
'creditCard' => array(
'cardNumber' => $number,
'expirationDate' => $card_expire,
'cardCode' => $card_code,
),
),
),
);
$xml_response = commerce_authnet_cim_request($payment_method, 'createCustomerPaymentProfileRequest', $api_request_data);
}
else {
// There isn't a profile ID to extract.
$cim_customer_profile_id = NULL;
// Build a request to create a profile and add payment method to it.
$api_request_data = array(
'profile' => array(
'merchantCustomerId' => $account->uid,
'description' => $billto['firstName'] . ' ' . $billto['lastName'],
'email' => $account->mail,
'paymentProfiles' => array(
'billTo' => $billto,
'payment' => array(
'creditCard' => array(
'cardNumber' => $number,
'expirationDate' => $card_expire,
'cardCode' => $card_code,
),
),
),
),
);
$xml_response = commerce_authnet_cim_request($payment_method, 'createCustomerProfileRequest', $api_request_data);
}
if ((string) $xml_response->messages->resultCode == 'Ok') {
// Build a remote ID that includes the Customer Profile ID and the
// Payment Profile ID.
if ($cim_customer_profile_id) {
$remote_id = $cim_customer_profile_id . '|' . (string) $xml_response->customerPaymentProfileId;
}
else {
$remote_id = (string) $xml_response->customerProfileId . '|' . (string) $xml_response->customerPaymentProfileIdList->numericString;
}
$card_data_wrapper = entity_metadata_wrapper('commerce_cardonfile', $card_data);
$card_data->uid = $account->uid;
$card_data->remote_id = $remote_id;
$card_data->card_type = $card_type;
$card_data->card_name = $billto['firstName'] . ' ' . $billto['lastName'];
$card_data->card_number = substr($number, -4);
$card_data->status = 1;
$card_data_wrapper->commerce_cardonfile_profile = $profile;
return $card_data;
}
elseif ((string) $xml_response->messages->message->code == 'E00039') {
// But if a Customer Profile already existed for this user, attempt
// instead to add this card as a new Payment Profile to it.
$result = array_filter(explode(' ', (string) $xml_response->messages->message->text), 'is_numeric');
$add_to_profile = reset($result);
// Build a request to add a new payment method to an existing profile.
$api_request_data = array(
'customerProfileId' => $add_to_profile,
'paymentProfile' => array(
'billTo' => $billto,
'payment' => array(
'creditCard' => array(
'cardNumber' => $number,
'expirationDate' => $card_expire,
'cardCode' => $card_code,
),
),
),
);
$xml_response = commerce_authnet_cim_request($payment_method, 'createCustomerPaymentProfileRequest', $api_request_data);
if ((string) $xml_response->messages->resultCode == 'Ok' || (string) $xml_response->messages->message->code == 'E00039') {
// If we got a duplicate code, then the payment profile already exists
// at Authorize.Net and needs to be represented locally.
if ((string) $xml_response->messages->message->code == 'E00039') {
$cim_profile_response = commerce_authnet_cim_get_customer_profile_request($payment_method, $add_to_profile);
if ((string) $cim_profile_response->messages->resultCode == 'Ok') {
// Inspect the returned payment profiles to find the one that
// generated the duplicate error code.
$cim_payment_profiles = $cim_profile_response->profile->paymentProfiles;
if (!is_array($cim_payment_profiles)) {
$cim_payment_profiles = array(
$cim_payment_profiles,
);
}
foreach ($cim_payment_profiles as $key => $payment_profile) {
// We match the submitted values against the existing payment
// profiles using the last 4 digits of the card number. This could
// potentially create a conflict if the same customer has two
// different cards that end in the same four digits, but that is
// highly unlikely.
if (substr($number, -4) == substr($payment_profile->payment->creditCard->cardNumber, -4)) {
$payment_profile_id = (string) $payment_profile->customerPaymentProfileId;
break;
}
}
}
}
else {
$payment_profile_id = (string) $xml_response->customerPaymentProfileId;
}
// Build a remote ID that includes the customer profile ID and the new
// or existing payment profile ID. We don't do any check here to ensure
// we found a payment profile ID, as we shouldn't have got a duplicate
// error if it didn't actually exist.
$remote_id = $add_to_profile . '|' . $payment_profile_id;
$card_data_wrapper = entity_metadata_wrapper('commerce_cardonfile', $card_data);
$card_data->uid = $account->uid;
$card_data->remote_id = $remote_id;
$card_data->card_type = $card_type;
$card_data->card_name = $billto['firstName'] . ' ' . $billto['lastName'];
$card_data->card_number = substr($number, -4);
$card_data->status = 1;
$card_data_wrapper->commerce_cardonfile_profile = $profile;
return $card_data;
}
else {
// Provide the user with information on the failure if it exists.
if (!empty($xml_response->messages->message->text)) {
drupal_set_message(t('Error: @error', array(
'@error' => (string) $xml_response->messages->message->text,
)), 'error');
}
}
}
return FALSE;
}
/**
* Commerce Card on File create callback.
*
* @param array $form
* The card on file create form.
* @param array $form_state
* The card on file create form state.
* @param array $payment_method
* The payment method for the card on file request.
* @param object $card_data
* The commerce_cardonfile entity.
*
* @return object|bool
* A new commerce_cardonfile entity or FALSE if there was an error.
*/
function commerce_authnet_acceptjs_cardonfile_create($form, &$form_state, $payment_method, $card_data) {
$account = user_load($card_data->uid);
// Submit the profile to the field attach handlers.
$profile = $form_state['commerce_customer_profile'];
field_attach_submit('commerce_customer_profile', $profile, $form['commerce_customer_profile'], $form_state);
commerce_customer_profile_save($profile);
// Format the address into the Auth.net schema.
$profile_wrapper = entity_metadata_wrapper('commerce_customer_profile', $profile);
$billto = commerce_authnet_cim_billto_array(NULL, $profile_wrapper->commerce_customer_address
->value());
// Build the card data to submit to Auth.net.
$data_descriptor = $form_state['values']['data_descriptor'];
$data_value = $form_state['values']['data_value'];
$last4 = $form_state['values']['last4'];
$card_data->card_exp_month = $form_state['values']['expiration_month'];
$card_data->card_exp_year = substr(date('Y'), 0, 2) . $form_state['values']['expiration_year'];
$card_type = $form_state['values']['type'];
$card_data->card_number = $last4;
// Attempt to load and existing profile id from the customers card data.
$existing_cards = commerce_cardonfile_load_multiple_by_uid($account->uid, $payment_method['instance_id']);
// Check for a remote ID.
$remote_id = NULL;
if (!empty($existing_cards)) {
$existing_card = reset($existing_cards);
$remote_id = $existing_card->remote_id;
}
if ($remote_id) {
// Extract the profile id from the remote id.
list($customer_profile_id, $cim_customer_payment_id) = explode('|', $remote_id);
// Build a request to add a new payment method to an existing profile.
$api_request_data = array(
'customerProfileId' => $customer_profile_id,
'paymentProfile' => array(
'billTo' => $billto,
'payment' => array(
'opaqueData' => array(
'dataDescriptor' => $data_descriptor,
'dataValue' => $data_value,
),
),
),
);
$xml_response = commerce_authnet_cim_request($payment_method, 'createCustomerPaymentProfileRequest', $api_request_data);
if ((string) $xml_response->messages->resultCode == 'Ok') {
$customer_payment_id = (string) $xml_response->customerPaymentProfileId;
}
elseif ((string) $xml_response->messages->message->code == 'E00039') {
$customer_payment_id = (string) $xml_response->customerPaymentProfileId;
}
else {
return FALSE;
}
}
else {
// Build a request to create a profile and add payment method to it.
$api_request_data = array(
'profile' => array(
'merchantCustomerId' => $account->uid,
'description' => $billto['firstName'] . ' ' . $billto['lastName'],
'email' => $account->mail,
),
);
$xml_response = commerce_authnet_cim_request($payment_method, 'createCustomerProfileRequest', $api_request_data);
if ((string) $xml_response->messages->resultCode == 'Ok') {
$customer_profile_id = (string) $xml_response->customerProfileId;
}
elseif ((string) $xml_response->messages->message->code == 'E00039') {
$result = array_filter(explode(' ', (string) $xml_response->messages->message->text), 'is_numeric');
$customer_profile_id = reset($result);
}
else {
return FALSE;
}
// Build a request to add a new payment method to an existing profile.
$api_request_data = array(
'customerProfileId' => $customer_profile_id,
'paymentProfile' => array(
'billTo' => $billto,
'payment' => array(
'opaqueData' => array(
'dataDescriptor' => $data_descriptor,
'dataValue' => $data_value,
),
),
),
);
$xml_response = commerce_authnet_cim_request($payment_method, 'createCustomerPaymentProfileRequest', $api_request_data);
if ((string) $xml_response->messages->resultCode == 'Ok') {
$customer_payment_id = (string) $xml_response->customerPaymentProfileId;
}
elseif ((string) $xml_response->messages->message->code == 'E00039') {
$customer_payment_id = (string) $xml_response->customerPaymentProfileId;
}
else {
return FALSE;
}
}
$remote_id = $customer_profile_id . '|' . $customer_payment_id;
$card_data_wrapper = entity_metadata_wrapper('commerce_cardonfile', $card_data);
$card_data->uid = $account->uid;
$card_data->remote_id = $remote_id;
$card_data->card_type = $card_type;
$card_data->card_name = $billto['firstName'] . ' ' . $billto['lastName'];
$card_data->card_number = $last4;
$card_data->status = 1;
$card_data_wrapper->commerce_cardonfile_profile = $profile;
return $card_data;
}
/**
* 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_authnet_cim_cardonfile_charge($payment_method, $card_data, $order, $charge = NULL) {
// Format order total for transaction.
if (!isset($charge)) {
$wrapper = entity_metadata_wrapper('commerce_order', $order);
$charge = commerce_line_items_total($wrapper->commerce_line_items);
}
// Extract the Customer Profile and Payment Profile IDs from the remote_id.
list($cim_customer_profile_id, $cim_payment_profile_id) = explode('|', $card_data->remote_id);
// Determine the proper transaction element to use inside the XML.
$element_name = commerce_authnet_cim_transaction_element_name($payment_method['settings']['txn_type']);
// Build a data array for the transaction API request.
$api_request_data = array(
'transaction' => array(
$element_name => array(
'amount' => number_format(commerce_currency_amount_to_decimal($charge['amount'], $charge['currency_code']), 2, '.', ''),
'customerProfileId' => $cim_customer_profile_id,
'customerPaymentProfileId' => $cim_payment_profile_id,
'order' => array(
'invoiceNumber' => $order->order_number,
),
),
),
'extraOptions' => '<![CDATA[x_delim_data=TRUE&x_delim_char=|&x_encap_char="&x_solution_id=A1000009&x_currency_code=' . $charge['currency_code'] . '&x_customer_ip=' . substr(ip_address(), 0, 15) . '&x_email_customer=' . $payment_method['settings']['email_customer'] . ']]>',
);
// If we get a response from the API server...
$xml_response = commerce_authnet_cim_request($payment_method, 'createCustomerProfileTransactionRequest', $api_request_data);
if (!empty($xml_response->directResponse)) {
// Extract the response data from the XML.
$response = explode('|', (string) $xml_response->directResponse);
for ($i = 0; $i < count($response); $i++) {
$response[$i] = substr($response[$i], 1, strlen($response[$i]) - 2);
}
// Prepare a transaction object to represent the transaction attempt.
$transaction = commerce_payment_transaction_new('authnet_aim', $order->order_id);
$transaction->instance_id = $payment_method['instance_id'];
$transaction->remote_id = $response[6];
$transaction->amount = $charge['amount'];
$transaction->currency_code = $charge['currency_code'];
$transaction->payload[REQUEST_TIME] = $response;
// If we didn't get an approval response code...
if ($response[0] != '1') {
// Create a failed transaction with the error message.
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
}
else {
// Set the transaction status based on the type of transaction this was.
switch ($payment_method['settings']['txn_type']) {
case COMMERCE_CREDIT_AUTH_ONLY:
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
break;
case COMMERCE_CREDIT_AUTH_CAPTURE:
$transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
break;
}
}
// Store the type of transaction in the remote status.
$transaction->remote_status = $response[11];
// Build a meaningful response message.
$message = array(
'<b>' . commerce_authnet_reverse_txn_type(commerce_authnet_txn_type($payment_method['settings']['txn_type'])) . '</b>',
'<b>' . ($response[0] != '1' ? t('REJECTED') : t('ACCEPTED')) . ':</b> ' . check_plain($response[3]),
t('AVS response: @avs', array(
'@avs' => commerce_authnet_avs_response($response[5]),
)),
);
$transaction->message = implode('<br />', $message);
// Save the transaction information.
commerce_payment_transaction_save($transaction);
// If the payment failed, display an error and rebuild the form.
if ($response[0] != '1') {
drupal_set_message(t('We received the following error processing your card. Please enter your information again or try a different card.'), 'error');
drupal_set_message(check_plain($response[3]), 'error');
return FALSE;
}
return isset($transaction->status) ? $transaction->status : TRUE;
}
elseif ((string) $xml_response->messages->message->code == 'E00040') {
// If the response indicated a non-existent profile, deactive it now.
$card_data->status = 0;
commerce_cardonfile_save($card_data);
drupal_set_message(t('The card you selected is no longer valid. Please use a different card to complete payment.'), 'error');
return FALSE;
}
drupal_set_message(t('We could not process your card on file at this time. Please try again or use a different card.'), 'error');
return FALSE;
}
/**
* Card on file callback: updates the associated customer payment profile.
*/
function commerce_authnet_cim_cardonfile_update($form, &$form_state, $payment_method, $card_data) {
// Extract the Customer Profile and Payment Profile IDs from the remote_id.
list($cim_customer_profile_id, $cim_payment_profile_id) = explode('|', $card_data->remote_id);
if (!empty($form_state['values']['credit_card']['number']) && $form_state['values']['credit_card']['number'] != $form['credit_card']['number']['#default_value']) {
$number = $form_state['values']['credit_card']['number'];
}
else {
$number = 'XXXX' . $card_data->card_number;
}
// Load the payment profile so that billTo can be updated.
$payment_profile_xml = commerce_authnet_cim_get_customer_payment_profile_request($payment_method, $cim_customer_profile_id, $cim_payment_profile_id);
$billto = $payment_profile_xml->paymentProfile->billTo;
$first_name = (string) $billto->firstName;
$last_name = (string) $billto->lastName;
// Extract the first and last name from form values.
if (!empty($form_state['values']['credit_card']['owner'])) {
$name_parts = explode(' ', $form_state['values']['credit_card']['owner']);
$first_name = array_shift($name_parts);
$last_name = implode(' ', $name_parts);
}
// Build the base profile update data.
$api_request_data = array(
'customerProfileId' => $cim_customer_profile_id,
'paymentProfile' => array(
'billTo' => array(
'firstName' => substr($first_name, 0, 50),
'lastName' => substr($last_name, 0, 50),
'company' => (string) $billto->company,
'address' => (string) $billto->address,
'city' => (string) $billto->city,
'state' => (string) $billto->state,
'zip' => (string) $billto->zip,
'country' => (string) $billto->country,
),
'payment' => array(
'creditCard' => array(
'cardNumber' => $number,
'expirationDate' => $card_data->card_exp_year . '-' . $card_data->card_exp_month,
),
),
'customerPaymentProfileId' => $cim_payment_profile_id,
),
);
// Fetch the response from the API server and let Card on File know if the
// update was successful.
$xml_response = commerce_authnet_cim_request($payment_method, 'updateCustomerPaymentProfileRequest', $api_request_data);
return (string) $xml_response->messages->message->code == 'I00001';
}
/**
* Card on file callback: deletes the associated customer payment profile.
*/
function commerce_authnet_cim_cardonfile_delete($form, &$form_state, $payment_method, $card_data) {
// Extract the Customer Profile and Payment Profile IDs from the remote_id.
list($cim_customer_profile_id, $cim_payment_profile_id) = explode('|', $card_data->remote_id);
// Fetch the response from the API server and let Card on File know that the
// delete was either successful or not necessary.
$xml_response = commerce_authnet_cim_delete_customer_payment_profile_request($payment_method, $cim_customer_profile_id, $cim_payment_profile_id);
$code = (string) $xml_response->messages->message->code;
return in_array($code, array(
'I00001',
'E00040',
));
}
/**
* Generates a billTo array for CIM API requests.
*
* @param $order
* The order object containing the billing information used for the billTo.
* @param $billing_address
* Optional. A commerce_customer_address field value array to use when the
* order object is empty; useful for generating an API request when you do not
* have an order object.
*
* @return
* An array used to generate the billTo XML in CIM API requests.
*/
function commerce_authnet_cim_billto_array($order, $billing_address = NULL) {
// If an order was given, prepare the billing address for use in this request.
if (!empty($order)) {
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$billing_address = $order_wrapper->commerce_customer_billing->commerce_customer_address
->value();
}
// Ensure we have a first and last name in the address.
if (empty($billing_address['first_name'])) {
$name_parts = explode(' ', $billing_address['name_line']);
$billing_address['first_name'] = array_shift($name_parts);
$billing_address['last_name'] = implode(' ', $name_parts);
}
// Ensure we have a state the address.
if (empty($billing_address['administrative_area'])) {
$billing_address['administrative_area'] = $billing_address['locality'];
}
// Ensure organisation name is keyed.
if (!isset($billing_address['organisation_name'])) {
$billing_address['organisation_name'] = '';
}
// Return the billTo array.
return array(
'firstName' => substr($billing_address['first_name'], 0, 50),
'lastName' => substr($billing_address['last_name'], 0, 50),
'company' => substr($billing_address['organisation_name'], 0, 50),
'address' => substr($billing_address['thoroughfare'], 0, 60),
'city' => substr($billing_address['locality'], 0, 40),
'state' => substr($billing_address['administrative_area'], 0, 40),
'zip' => substr($billing_address['postal_code'], 0, 20),
'country' => $billing_address['country'],
);
}
/**
* Generates a shipTo array for CIM API requests.
*
* @param $order
* The order object containing the shipping information used for the billTo.
*
* @return
* An array used to generate the shipTo XML in CIM API requests.
*/
function commerce_authnet_cim_shipto_array($order) {
// Prepare the shipping address for use in the request.
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$shipping_address = $order_wrapper->commerce_customer_shipping->commerce_customer_address
->value();
// Ensure we have a first and last name in the address.
if (empty($shipping_address['first_name'])) {
$name_parts = explode(' ', $shipping_address['name_line']);
$shipping_address['first_name'] = array_shift($name_parts);
$shipping_address['last_name'] = implode(' ', $name_parts);
}
// Return the shipTo array.
return array(
'firstName' => substr($shipping_address['first_name'], 0, 50),
'lastName' => substr($shipping_address['last_name'], 0, 50),
'company' => substr($shipping_address['organisation_name'], 0, 50),
'address' => substr($shipping_address['thoroughfare'], 0, 60),
'city' => substr($shipping_address['locality'], 0, 40),
'state' => substr($shipping_address['administrative_area'], 0, 40),
'zip' => substr($shipping_address['postal_code'], 0, 20),
'country' => $shipping_address['country'],
);
}
/**
* Generates a creditCard array for CIM API requests.
*
* @param $payment_details
* An array of payment details used in a CIM API request that doesn't have to
* include credit card data. If it does, the following keys are expected:
* - cardNumber: the full credit card number
* - expirationDate: the expiration date in YYYY-MM format
* - cardCode: the three or four digit card security code
*/
function commerce_authnet_cim_credit_card_array($payment_details) {
$credit_card = array();
foreach (array(
'cardNumber',
'expirationDate',
'cardCode',
) as $key) {
if (!empty($payment_details[$key])) {
$credit_card[$key] = $payment_details[$key];
}
}
return $credit_card;
}
/**
* Submits a createCustomerProfileRequest XML CIM API request to Authorize.Net.
*
* This function will attempt to create a CIM Customer Profile and a default
* Payment Profile for it using the given payment details.
*
* @param $payment_method
* The payment method instance array containing the API credentials for a CIM
* enabled Authorize.Net account.
* @param $order
* The order object containing the billing address and e-mail to use for the
* customer profile.
* @param $payment_details
* An array of payment details to use in the default payment profile. See the
* respective helper array functions for possible keys.
*
* @return
* A SimpleXMLElement containing the API response.
*
* @see commerce_authnet_cim_credit_card_array()
*/
function commerce_authnet_cim_create_customer_profile_request($payment_method, $order, $payment_details) {
$billto = commerce_authnet_cim_billto_array($order);
// Build the base profile request data.
$api_request_data = array(
'profile' => array(
'merchantCustomerId' => $order->uid,
'description' => $billto['firstName'] . ' ' . $billto['lastName'],
'email' => $order->mail,
'paymentProfiles' => array(
'billTo' => $billto,
'payment' => array(),
),
),
);
// Add the shipping address if available.
if (isset($order->commerce_customer_shipping)) {
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
if ($order_wrapper->commerce_customer_shipping
->value()) {
$api_request_data['profile']['shipToList'] = commerce_authnet_cim_shipto_array($order);
}
}
// If the order is anonymous, unset the merchantCustomerId from the request.
if (empty($api_request_data['profile']['merchantCustomerId'])) {
unset($api_request_data['profile']['merchantCustomerId']);
}
// Add credit card payment details to the default payment profile if given.
$credit_card = commerce_authnet_cim_credit_card_array($payment_details);
if (!empty($credit_card)) {
$api_request_data['profile']['paymentProfiles']['payment']['creditCard'] = $credit_card;
}
return commerce_authnet_cim_request($payment_method, 'createCustomerProfileRequest', $api_request_data);
}
/**
* Submits a createCustomerPaymentProfileRequest XML CIM API request to Authorize.Net.
*
* @param $payment_method
* The payment method instance array containing the API credentials for a CIM
* enabled Authorize.Net account.
* @param $cim_customer_profile_id
* A numerical CIM Customer Profile ID.
* @param $order
* The order object containing the billing address and e-mail to use for the
* payment profile.
* @param $payment_details
* An array of payment details to use in the default payment profile. See the
* respective helper array functions for possible keys.
*
* @return
* A SimpleXMLElement containing the API response.
*
* @see commerce_authnet_cim_credit_card_array()
*/
function commerce_authnet_cim_create_customer_payment_profile_request($payment_method, $cim_customer_profile_id, $order, $payment_details) {
$billto = commerce_authnet_cim_billto_array($order);
// Build the base profile request data.
$api_request_data = array(
'customerProfileId' => $cim_customer_profile_id,
'paymentProfile' => array(
'billTo' => $billto,
'payment' => array(),
),
);
// Add credit card payment details to the default payment profile if given.
$credit_card = commerce_authnet_cim_credit_card_array($payment_details);
if (!empty($credit_card)) {
$api_request_data['paymentProfile']['payment']['creditCard'] = $credit_card;
}
return commerce_authnet_cim_request($payment_method, 'createCustomerPaymentProfileRequest', $api_request_data);
}
/**
* Submits a request to retrieve a Customer profile's payment profiles
*
* This is useful when Authorize.net returns an error code of E00039, duplicate.
*
* @param $payment_method
* @param $cim_customer_profile_id
* @param $number
*
* @return bool|string
* Returns FALSE if not payment profile found, or the matching profile ID.
*/
function commerce_authnet_cim_get_customer_profile_request($payment_method, $cim_customer_profile_id) {
// Query for the profile's payment profiles.
$api_request_data = array(
'customerProfileId' => $cim_customer_profile_id,
);
return commerce_authnet_cim_request($payment_method, 'getCustomerProfileRequest', $api_request_data);
}
/**
* Submits a getCustomerPaymentProfileRequest XML CIM API request to Authorize.Net.
*
* @param $payment_method
* The payment method instance array containing the API credentials for a CIM
* enabled Authorize.Net account.
* @param $cim_customer_profile_id
* A numerical CIM Customer Profile ID.
* @param $cim_payment_profile_id
* A numerical CIM Customer Payment Profile ID.
*
* @return
* A SimpleXMLElement containing the API response.
*/
function commerce_authnet_cim_get_customer_payment_profile_request($payment_method, $cim_customer_profile_id, $cim_payment_profile_id) {
// Build the get payment profile request data.
$api_request_data = array(
'customerProfileId' => $cim_customer_profile_id,
'customerPaymentProfileId' => $cim_payment_profile_id,
);
return commerce_authnet_cim_request($payment_method, 'getCustomerPaymentProfileRequest', $api_request_data);
}
/**
* Submits a deleteCustomerPaymentProfileRequest XML CIM API request to Authorize.Net.
*
* @param $payment_method
* The payment method instance array containing the API credentials for a CIM
* enabled Authorize.Net account.
* @param $cim_customer_profile_id
* A numerical CIM Customer Profile ID.
* @param $cim_payment_profile_id
* A numerical CIM Customer Payment Profile ID.
*
* @return
* A SimpleXMLElement containing the API response.
*/
function commerce_authnet_cim_delete_customer_payment_profile_request($payment_method, $cim_customer_profile_id, $cim_payment_profile_id) {
// Build the payment profile delete request data.
$api_request_data = array(
'customerProfileId' => $cim_customer_profile_id,
'customerPaymentProfileId' => $cim_payment_profile_id,
);
return commerce_authnet_cim_request($payment_method, 'deleteCustomerPaymentProfileRequest', $api_request_data);
}
/**
* Submits an AIM API request to Authorize.Net.
*
* @param $payment_method
* The payment method instance array associated with this API request.
*/
function commerce_authnet_aim_request($payment_method, $nvp = array()) {
// Get the API endpoint URL for the method's transaction mode.
$url = commerce_authnet_aim_server_url($payment_method['settings']['txn_mode']);
// Add the default name-value pairs to the array.
$nvp += array(
// API credentials
'x_login' => $payment_method['settings']['login'],
'x_tran_key' => $payment_method['settings']['tran_key'],
'x_version' => '3.1',
// Extra administrative values
'x_test_request' => $payment_method['settings']['txn_mode'] == AUTHNET_TXN_MODE_LIVE_TEST ? 'TRUE' : 'FALSE',
'x_delim_data' => 'TRUE',
'x_delim_char' => '|',
'x_encap_char' => '"',
'x_relay_response' => 'FALSE',
'x_email_customer' => $payment_method['settings']['email_customer'],
// Drupal Commerce Solution ID
'x_solution_id' => 'A1000009',
);
// Allow modules to alter parameters of the API request.
drupal_alter('commerce_authnet_aim_request', $nvp, $payment_method);
// Log the request if specified.
if ($payment_method['settings']['log']['request'] == 'request') {
// Mask the credit card number and CVV.
$log_nvp = $nvp;
$log_nvp['x_login'] = str_repeat('X', strlen($log_nvp['x_login']));
$log_nvp['x_tran_key'] = str_repeat('X', strlen($log_nvp['x_tran_key']));
if (!empty($log_nvp['x_card_num'])) {
$log_nvp['x_card_num'] = str_repeat('X', strlen($log_nvp['x_card_num']) - 4) . substr($log_nvp['x_card_num'], -4);
}
if (!empty($log_nvp['x_card_code'])) {
$log_nvp['x_card_code'] = str_repeat('X', strlen($log_nvp['x_card_code']));
}
watchdog('commerce_authnet', 'Authorize.Net AIM request to @url: !param', array(
'@url' => $url,
'!param' => '<pre>' . check_plain(print_r($log_nvp, TRUE)) . '</pre>',
), WATCHDOG_DEBUG);
}
// Prepare the name-value pair array to be sent as a string.
$pairs = array();
foreach ($nvp as $key => $value) {
$pairs[] = $key . '=' . urlencode($value);
}
// Setup the cURL request.
$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, implode('&', $pairs));
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);
$result = curl_exec($ch);
// Commerce Authnet requires SSL peer verification, which may prevent out of
// date servers from successfully processing API requests. If you get an error
// related to peer verification, you may need to:
// * Update libraries like openssl and libcurl.
// * Use https://github.com/paragonie/certainty to get updated certificates.
// * Update your php.ini to point to your CA certificate bundle with the
// curl.cainfo setting.
// * Download the CA certificate bundle file from
// http://curl.haxx.se/docs/caextract.html, place it in a safe location on
// your web server, and update your settings.php to set the
// commerce_authnet_cacert variable to contain the absolute path of the
// file.
if (variable_get('commerce_authnet_cacert', FALSE)) {
curl_setopt($ch, CURLOPT_CAINFO, variable_get('commerce_authnet_cacert', ''));
}
// Log any errors to the watchdog.
if ($error = curl_error($ch)) {
watchdog('commerce_authnet', 'cURL error: @error', array(
'@error' => $error,
), WATCHDOG_ERROR);
return FALSE;
}
curl_close($ch);
// Make the response an array and trim off the encapsulating characters.
$response = explode('|', $result);
for ($i = 0; $i < count($response); $i++) {
$response[$i] = substr($response[$i], 1, strlen($response[$i]) - 2);
}
// Log the response if specified.
if ($payment_method['settings']['log']['response'] == 'response') {
watchdog('commerce_authnet', 'Authorize.Net AIM response: !param', array(
'!param' => '<pre>' . check_plain(print_r($response, TRUE)) . '</pre>',
WATCHDOG_DEBUG,
));
}
return $response;
}
/**
* Returns the URL to the Authorize.Net AIM server determined by transaction mode.
*
* @param $txn_mode
* The transaction mode that relates to the live or test server.
*
* @return
* The URL to use to submit requests to the Authorize.Net AIM server.
*/
function commerce_authnet_aim_server_url($txn_mode) {
switch ($txn_mode) {
case AUTHNET_TXN_MODE_LIVE:
case AUTHNET_TXN_MODE_LIVE_TEST:
return variable_get('commerce_authnet_aim_server_url_live', 'https://secure2.authorize.net/gateway/transact.dll');
case AUTHNET_TXN_MODE_DEVELOPER:
return variable_get('commerce_authnet_aim_server_url_dev', 'https://test.authorize.net/gateway/transact.dll');
}
}
/**
* Submits a CIM XML API request to Authorize.Net.
*
* @param $payment_method
* The payment method instance array associated with this API request.
* @param $request_type
* The name of the request type to submit.
* @param $api_request_data
* An associative array of data to be turned into a CIM XML API request.
*/
function commerce_authnet_cim_request($payment_method, $request_type, $api_request_data) {
// Get the API endpoint URL for the method's transaction mode.
$url = commerce_authnet_cim_server_url($payment_method['settings']['txn_mode']);
// Add default data to the API request data array.
if (!isset($api_request_data['merchantAuthentication'])) {
$api_request_data = array(
'merchantAuthentication' => array(
'name' => $payment_method['settings']['login'],
'transactionKey' => $payment_method['settings']['tran_key'],
),
) + $api_request_data;
}
// Determine if it is necessary to add a validation mode to the API request.
$validation_mode = '';
switch ($request_type) {
case 'createCustomerProfileRequest':
if (empty($api_request_data['profile']['paymentProfiles'])) {
$validation_mode = 'none';
}
else {
$validation_mode = $payment_method['settings']['txn_mode'] == AUTHNET_TXN_MODE_LIVE ? 'liveMode' : 'testMode';
}
break;
case 'createCustomerPaymentProfileRequest':
case 'updateCustomerPaymentProfileRequest':
case 'validateCustomerPaymentProfileRequest':
$validation_mode = $payment_method['settings']['txn_mode'] == AUTHNET_TXN_MODE_LIVE ? 'liveMode' : 'testMode';
break;
default:
break;
}
// Add the validation mode now if one was found.
if (!empty($validation_mode)) {
$api_request_data['validationMode'] = $validation_mode;
}
// Build and populate the API request SimpleXML element.
$api_request_element = new SimpleXMLElement('<' . $request_type . '/>');
$api_request_element
->addAttribute('xmlns', 'AnetApi/xml/v1/schema/AnetApiSchema.xsd');
commerce_simplexml_add_children($api_request_element, $api_request_data);
// Allow modules an opportunity to alter the request before it is sent.
drupal_alter('commerce_authnet_cim_request', $api_request_element, $payment_method, $request_type);
// Generate an XML string.
$xml = $api_request_element
->asXML();
// Log the request if specified.
if ($payment_method['settings']['log']['request'] == 'request') {
// Mask the credit card number and CVV.
$log_element = clone $api_request_element;
$log_element->merchantAuthentication->name = str_repeat('X', strlen((string) $log_element->merchantAuthentication->name));
$log_element->merchantAuthentication->transactionKey = str_repeat('X', strlen((string) $log_element->merchantAuthentication->transactionKey));
if (!empty($log_element->profile->paymentProfiles->payment->creditCard->cardNumber)) {
$card_number = (string) $log_element->profile->paymentProfiles->payment->creditCard->cardNumber;
$log_element->profile->paymentProfiles->payment->creditCard->cardNumber = str_repeat('X', strlen($card_number) - 4) . substr($card_number, -4);
}
if (!empty($log_element->paymentProfile->payment->creditCard->cardNumber)) {
$card_number = (string) $log_element->paymentProfile->payment->creditCard->cardNumber;
$log_element->paymentProfile->payment->creditCard->cardNumber = str_repeat('X', strlen($card_number) - 4) . substr($card_number, -4);
}
if (!empty($log_element->profile->paymentProfiles->payment->creditCard->cardCode)) {
$log_element->profile->paymentProfiles->payment->creditCard->cardCode = str_repeat('X', strlen((string) $log_element->profile->paymentProfiles->payment->creditCard->cardCode));
}
if (!empty($log_element->paymentProfile->payment->creditCard->cardCode)) {
$log_element->paymentProfile->payment->creditCard->cardCode = str_repeat('X', strlen((string) $log_element->paymentProfile->payment->creditCard->cardCode));
}
watchdog('commerce_authnet', 'Authorize.Net CIM @type to @url: @xml', array(
'@type' => $request_type,
'@url' => $url,
'@xml' => $log_element
->asXML(),
), WATCHDOG_DEBUG);
}
// Build the array of header information for the request.
$header = array();
$header[] = 'Content-type: text/xml; charset=utf-8';
// Setup the cURL request.
$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, $xml);
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);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
$result = curl_exec($ch);
// Commerce Authnet requires SSL peer verification, which may prevent out of
// date servers from successfully processing API requests. If you get an error
// related to peer verification, you may need to:
// * Update libraries like openssl and libcurl.
// * Use https://github.com/paragonie/certainty to get updated certificates.
// * Update your php.ini to point to your CA certificate bundle with the
// curl.cainfo setting.
// * Download the CA certificate bundle file from
// http://curl.haxx.se/docs/caextract.html, place it in a safe location on
// your web server, and update your settings.php to set the
// commerce_authnet_cacert variable to contain the absolute path of the
// file.
if (variable_get('commerce_authnet_cacert', FALSE)) {
curl_setopt($ch, CURLOPT_CAINFO, variable_get('commerce_authnet_cacert', ''));
}
// Log any errors to the watchdog.
if ($error = curl_error($ch)) {
watchdog('commerce_authnet', 'cURL error: @error', array(
'@error' => $error,
), WATCHDOG_ERROR);
return FALSE;
}
curl_close($ch);
// If we received data back from the server...
if (!empty($result)) {
// Remove non-absolute XML namespaces to prevent SimpleXML warnings.
$result = str_replace(' xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd"', '', $result);
// Extract the result into an XML response object.
$response = new SimpleXMLElement($result);
// Log the API response if specified.
if ($payment_method['settings']['log']['response'] == 'response') {
watchdog('commerce_authnet', 'API response received:<pre>@xml</pre>', array(
'@xml' => $response
->asXML(),
));
}
return $response;
}
else {
return FALSE;
}
}
/**
* Returns the URL to the Authorize.Net CIM server determined by transaction mode.
*
* @param $txn_mode
* The transaction mode that relates to the live or test server.
*
* @return
* The URL to use to submit requests to the Authorize.Net CIM server.
*/
function commerce_authnet_cim_server_url($txn_mode) {
switch ($txn_mode) {
case AUTHNET_TXN_MODE_LIVE:
case AUTHNET_TXN_MODE_LIVE_TEST:
return variable_get('commerce_authnet_cim_server_url_live', 'https://api2.authorize.net/xml/v1/request.api');
case AUTHNET_TXN_MODE_DEVELOPER:
return variable_get('commerce_authnet_cim_server_url_dev', 'https://apitest.authorize.net/xml/v1/request.api');
}
}
/**
* Returns the transaction type string for Authorize.Net that corresponds to the
* Drupal Commerce constant.
*
* @param $txn_type
* A Drupal Commerce transaction type constant.
*/
function commerce_authnet_txn_type($txn_type) {
switch ($txn_type) {
case COMMERCE_CREDIT_AUTH_ONLY:
return 'AUTH_ONLY';
case COMMERCE_CREDIT_PRIOR_AUTH_CAPTURE:
return 'PRIOR_AUTH_CAPTURE';
case COMMERCE_CREDIT_AUTH_CAPTURE:
return 'AUTH_CAPTURE';
case COMMERCE_CREDIT_CAPTURE_ONLY:
return 'CAPTURE_ONLY';
case COMMERCE_CREDIT_REFERENCE_SET:
case COMMERCE_CREDIT_REFERENCE_TXN:
case COMMERCE_CREDIT_REFERENCE_REMOVE:
case COMMERCE_CREDIT_REFERENCE_CREDIT:
return NULL;
case COMMERCE_CREDIT_CREDIT:
return 'CREDIT';
case COMMERCE_CREDIT_VOID:
return 'VOID';
}
}
/**
* Returns the CIM transaction request type that correponds to a the Drupal
* Commerce constant.
*
* @param $txn_type
* A Drupal Commerce transaction type constant.
*/
function commerce_authnet_cim_transaction_element_name($txn_type) {
switch ($txn_type) {
case COMMERCE_CREDIT_AUTH_ONLY:
return 'profileTransAuthOnly';
case COMMERCE_CREDIT_AUTH_CAPTURE:
return 'profileTransAuthCapture';
case COMMERCE_CREDIT_CAPTURE_ONLY:
return 'profileTransCaptureOnly';
case COMMERCE_CREDIT_PRIOR_AUTH_CAPTURE:
return 'profileTransPriorAuthCapture';
case COMMERCE_CREDIT_CREDIT:
return 'profileTransRefund';
case COMMERCE_CREDIT_VOID:
return 'profileTransVoid';
default:
return '';
}
}
/**
* Returns the description of an Authorize.Net transaction type.
*
* @param $txn_type
* An Authorize.Net transaction type string.
*/
function commerce_authnet_reverse_txn_type($txn_type) {
switch (strtoupper($txn_type)) {
case 'AUTH_ONLY':
return t('Authorization only');
case 'PRIOR_AUTH_CAPTURE':
return t('Prior authorization capture');
case 'AUTH_CAPTURE':
return t('Authorization and capture');
case 'CAPTURE_ONLY':
return t('Capture only');
case 'CREDIT':
return t('Credit');
case 'VOID':
return t('Void');
}
}
/**
* Returns the message text for an AVS response code.
*/
function commerce_authnet_avs_response($code) {
switch ($code) {
case 'A':
return t('Address (Street) matches, ZIP does not');
case 'B':
return t('Address information not provided for AVS check');
case 'E':
return t('AVS error');
case 'G':
return t('Non-U.S. Card Issuing Bank');
case 'N':
return t('No Match on Address (Street) or ZIP');
case 'P':
return t('AVS not applicable for this transaction');
case 'R':
return t('Retry – System unavailable or timed out');
case 'S':
return t('Service not supported by issuer');
case 'U':
return t('Address information is unavailable');
case 'W':
return t('Nine digit ZIP matches, Address (Street) does not');
case 'X':
return t('Address (Street) and nine digit ZIP match');
case 'Y':
return t('Address (Street) and five digit ZIP match');
case 'Z':
return t('Five digit ZIP matches, Address (Street) does not');
}
return '-';
}
/**
* Returns the message text for a CVV match.
*/
function commerce_authnet_cvv_response($code) {
switch ($code) {
case 'M':
return t('Match');
case 'N':
return t('No Match');
case 'P':
return t('Not Processed');
case 'S':
return t('Should have been present');
case 'U':
return t('Issuer unable to process request');
}
return '-';
}
Functions
Name | Description |
---|---|
commerce_authnet_acceptjs_cardonfile_create | Commerce Card on File create callback. |
commerce_authnet_acceptjs_cardonfile_create_submit | |
commerce_authnet_acceptjs_cardonfile_create_validate | Validation callback for AcceptJS card on file create. |
commerce_authnet_aim_capture_access | Determines access to the prior authorization capture form for Authorize.Net AIM credit card transactions. |
commerce_authnet_aim_credit_access | Determines access to the credit form for successful Authorize.Net AIM credit card transactions. |
commerce_authnet_aim_default_settings | Returns the default settings for the Authorize.Net AIM payment method. |
commerce_authnet_aim_request | Submits an AIM API request to Authorize.Net. |
commerce_authnet_aim_server_url | Returns the URL to the Authorize.Net AIM server determined by transaction mode. |
commerce_authnet_aim_settings_form | Payment method callback: settings form. |
commerce_authnet_aim_submit_form | Payment method callback: checkout form. |
commerce_authnet_aim_submit_form_submit | Payment method callback: checkout form submission. |
commerce_authnet_aim_submit_form_validate | Payment method callback: checkout form validation. |
commerce_authnet_aim_void_access | Determines access to the void form for Authorize.Net AIM credit card transactions |
commerce_authnet_avs_response | Returns the message text for an AVS response code. |
commerce_authnet_cim_billto_array | Generates a billTo array for CIM API requests. |
commerce_authnet_cim_cardonfile_charge | Card on file callback: background charge payment |
commerce_authnet_cim_cardonfile_create | Commerce Card on File create callback. |
commerce_authnet_cim_cardonfile_create_validate | Validation callback for card on file create. |
commerce_authnet_cim_cardonfile_delete | Card on file callback: deletes the associated customer payment profile. |
commerce_authnet_cim_cardonfile_update | Card on file callback: updates the associated customer payment profile. |
commerce_authnet_cim_create_customer_payment_profile_request | Submits a createCustomerPaymentProfileRequest XML CIM API request to Authorize.Net. |
commerce_authnet_cim_create_customer_profile_request | Submits a createCustomerProfileRequest XML CIM API request to Authorize.Net. |
commerce_authnet_cim_credit_card_array | Generates a creditCard array for CIM API requests. |
commerce_authnet_cim_delete_customer_payment_profile_request | Submits a deleteCustomerPaymentProfileRequest XML CIM API request to Authorize.Net. |
commerce_authnet_cim_get_customer_payment_profile_request | Submits a getCustomerPaymentProfileRequest XML CIM API request to Authorize.Net. |
commerce_authnet_cim_get_customer_profile_request | Submits a request to retrieve a Customer profile's payment profiles |
commerce_authnet_cim_request | Submits a CIM XML API request to Authorize.Net. |
commerce_authnet_cim_server_url | Returns the URL to the Authorize.Net CIM server determined by transaction mode. |
commerce_authnet_cim_shipto_array | Generates a shipTo array for CIM API requests. |
commerce_authnet_cim_submit_form_submit | Imitates the checkout form submission callback for the AIM payment method. |
commerce_authnet_cim_submit_new_card_form_submit | Handles advanced logic relating to creating CIM orders with new card data. |
commerce_authnet_cim_transaction_element_name | Returns the CIM transaction request type that correponds to a the Drupal Commerce constant. |
commerce_authnet_commerce_payment_method_info | Implements hook_commerce_payment_method_info(). |
commerce_authnet_cvv_response | Returns the message text for a CVV match. |
commerce_authnet_form_commerce_cardonfile_card_form_alter | Implements hook_form_FORM_ID_alter(). |
commerce_authnet_form_commerce_cardonfile_update_form_alter | Implements hook_form_FORM_ID_alter(). |
commerce_authnet_form_commerce_payment_order_transaction_add_form_alter | Implements hook_form_FORM_ID_alter(). |
commerce_authnet_library | Implements hook_library(). |
commerce_authnet_menu | Implements hook_menu(). |
commerce_authnet_reverse_txn_type | Returns the description of an Authorize.Net transaction type. |
commerce_authnet_txn_type | Returns the transaction type string for Authorize.Net that corresponds to the Drupal Commerce constant. |