'uc_authorizenet_silent_post',
'access callback' => 'uc_authorizenet_silent_post_access',
'type' => MENU_CALLBACK,
'file' => 'uc_authorizenet.pages.inc',
);
// User operations menu items for ARB recurring fees.
$items['user/%user/recurring/%/arb-update'] = array(
'title' => 'Update your payment details',
'description' => 'Update the payment details for a recurring fee.',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_authorizenet_arb_user_update_form', 1, 3),
'access callback' => 'uc_recurring_user_access',
'access arguments' => array(1, 3),
'type' => MENU_CALLBACK,
'file' => 'uc_authorizenet.pages.inc',
);
$items['user/%user/recurring/%/arb-cancel'] = array(
'title' => 'Cancel the recurring fee?',
'description' => 'Cancel a recurring fee.',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_authorizenet_arb_user_cancel_form', 1, 3),
'access callback' => 'uc_recurring_user_access',
'access arguments' => array(1, 3),
'type' => MENU_CALLBACK,
'file' => 'uc_authorizenet.pages.inc',
);
// Admin operations menu items.
$items['admin/store/orders/recurring/%/arb-update'] = array(
'title' => 'Update ARB subscription',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_authorizenet_arb_admin_update_form', 4),
'access arguments' => array('administer recurring fees'),
'type' => MENU_CALLBACK,
'file' => 'uc_authorizenet.admin.inc',
);
$items['admin/store/orders/recurring/%/arb-cancel'] = array(
'title' => 'Cancel ARB subscription',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_authorizenet_arb_admin_cancel_form', 4),
'access arguments' => array('administer recurring fees'),
'type' => MENU_CALLBACK,
'file' => 'uc_authorizenet.admin.inc',
);
return $items;
}
// Make sure Authorize.Net always has access to send Silent POSTs.
function uc_authorizenet_silent_post_access() {
return TRUE;
}
/**
* Implementation of hook_payment_gateway().
*/
function uc_authorizenet_payment_gateway() {
$gateways[] = array(
'id' => 'authorizenet',
'title' => t('Authorize.net'),
'description' => t('Process credit card payments using the AIM service of Authorize.net.'),
'settings' => 'uc_authorizenet_settings_form',
'credit' => 'uc_authorizenet_charge',
'credit_txn_types' => array(UC_CREDIT_AUTH_ONLY, UC_CREDIT_PRIOR_AUTH_CAPTURE, UC_CREDIT_AUTH_CAPTURE, UC_CREDIT_REFERENCE_SET, UC_CREDIT_REFERENCE_TXN),
);
return $gateways;
}
/**
* Callback for payment gateway settings.
*/
function uc_authorizenet_settings_form() {
$login_data = _uc_authorizenet_login_data();
// Allow admin to set duplicate window
$form['uc_authnet_duplicate_window'] = array(
'#type' => 'select',
'#title' => t('Duplicate window'),
'#description' => t('Blocks submission of duplicate transactions within the specified window. Defaults to 120 seconds.'),
'#default_value' => variable_get('uc_authnet_duplicate_window', 120),
'#options' => drupal_map_assoc(array(0, 15, 30, 45, 60, 75, 90, 105, 120)),
);
$form['api_id_key'] = array(
'#type' => 'fieldset',
'#title' => t('API Login ID and Transaction Key'),
'#description' => t('This information is required for Ubercart to interact with your payment gateway account. It is different from your login ID and password and may be found through your account settings page. Do not change the gateway URLs unless you are using this module with an Authorize.net-compatible gateway that requires different URLs.'),
);
$form['api_id_key']['uc_authnet_api_login_id'] = array(
'#type' => 'textfield',
'#title' => t('API Login ID'),
'#default_value' => variable_get('uc_authnet_api_login_id', ''),
);
$form['api_id_key']['uc_authnet_api_transaction_key'] = array(
'#type' => 'textfield',
'#title' => t('Transaction Key'),
'#default_value' => variable_get('uc_authnet_api_transaction_key', ''),
);
$form['api_id_key']['uc_authnet_api_test_gateway_url'] = array(
'#type' => 'textfield',
'#title' => t('Authorize.net Test Gateway URL'),
'#default_value' => variable_get('uc_authnet_api_test_gateway_url',
UC_AUTHORIZENET_TEST_GATEWAY_URL),
);
$form['api_id_key']['uc_authnet_api_live_gateway_url'] = array(
'#type' => 'textfield',
'#title' => t('Authorize.net Live Gateway URL'),
'#default_value' => variable_get('uc_authnet_api_live_gateway_url',
UC_AUTHORIZENET_LIVE_GATEWAY_URL),
);
$form['aim_settings'] = array(
'#type' => 'fieldset',
'#title' => t('AIM settings'),
'#description' => t('These settings pertain to the Authorize.Net AIM payment method for card not present transactions.'),
);
$form['aim_settings']['uc_authnet_aim_txn_mode'] = array(
'#type' => 'radios',
'#title' => t('Transaction mode'),
'#description' => t('Only specify a developer test account if you login to your account through https://test.authorize.net.
Adjust to live transactions when you are ready to start processing real payments.'),
'#options' => array(
'live' => t('Live transactions in a live account'),
'live_test' => t('Test transactions in a live account'),
'developer_test' => t('Developer test account transactions'),
),
'#default_value' => variable_get('uc_authnet_aim_txn_mode', 'live_test'),
);
$form['aim_settings']['uc_authnet_aim_email_customer'] = array(
'#type' => 'checkbox',
'#title' => t('Tell Authorize.net to e-mail the customer a receipt based on your account settings.'),
'#default_value' => variable_get('uc_authnet_aim_email_customer', FALSE),
);
$form['aim_settings']['uc_authnet_response_debug'] = array(
'#type' => 'checkbox',
'#title' => t('Log full API response messages from Authorize.net for debugging.'),
'#default_value' => variable_get('uc_authnet_response_debug', FALSE),
);
$form['arb_settings'] = array(
'#type' => 'fieldset',
'#title' => t('ARB settings'),
'#description' => t('These settings pertain to the Authorize.Net Automated Recurring Billing service.')
);
$form['arb_settings']['uc_authnet_arb_mode'] = array(
'#type' => 'radios',
'#title' => t('Transaction mode'),
'#description' => t('Only specify developer mode if you login to your account through https://test.authorize.net.
Adjust to production mode when you are ready to start processing real recurring fees.'),
'#options' => array(
'production' => t('Production'),
'developer' => t('Developer test'),
'disabled' => t('Disabled'),
),
'#default_value' => variable_get('uc_authnet_arb_mode', 'disabled'),
);
$form['arb_settings']['uc_authnet_md5_hash'] = array(
'#type' => 'textfield',
'#title' => t('MD5 Hash'),
'#description' => t('Note: You must first configure credit card encryption before setting this.
Enter the value here you entered in your Auth.Net account settings.'),
'#default_value' => $login_data['md5_hash'],
'#access' => user_access('administer credit cards'),
);
$form['arb_settings']['uc_authnet_report_arb_post'] = array(
'#type' => 'checkbox',
'#title' => t('Log reported ARB payments in watchdog.'),
'#description' => t('Make sure you have set your Silent POST URL in Authorize.Net to @url.', array('@url' => url('authnet/silent-post', array('absolute' => TRUE)))),
'#default_value' => variable_get('uc_authnet_report_arb_post', FALSE),
);
$form['cim_settings'] = array(
'#type' => 'fieldset',
'#title' => t('CIM settings'),
'#description' => t('These settings pertain to the Authorize.Net Customer Information Management service.')
);
$form['cim_settings']['uc_authnet_cim_profile'] = array(
'#type' => 'checkbox',
'#title' => t('Always create a CIM profile for securely storing CC info for later use.'),
'#default_value' => variable_get('uc_authnet_cim_profile', FALSE),
);
$form['cim_settings']['uc_authnet_cim_mode'] = array(
'#type' => 'radios',
'#title' => t('Transaction mode'),
'#description' => t('Only specify a developer test account if you login to your account through https://test.authorize.net.
Adjust to live transactions when you are ready to start processing real payments.'),
'#options' => array(
'production' => t('Production'),
'developer' => t('Developer test'),
'disabled' => t('Disabled'),
),
'#default_value' => variable_get('uc_authnet_cim_mode', 'disabled'),
);
return $form;
}
/**
* Implementation of hook_form_alter().
*/
function uc_authorizenet_form_alter(&$form, $form_state, $form_id) {
if ($form_id == 'uc_payment_gateways_form') {
$form['#submit'][] = 'uc_authorizenet_payment_gateway_settings_submit';
}
}
// Submit handler for payment gateway settings form to encrypt fields.
function uc_authorizenet_payment_gateway_settings_submit($form, &$form_state) {
// If CC encryption has been configured properly.
if ($key = uc_credit_encryption_key()) {
// Setup our encryption object.
$crypt = new uc_encryption_class;
// Encrypt the Login ID, Transaction key, and MD5 Hash.
if (!empty($form_state['values']['uc_authnet_md5_hash'])) {
variable_set('uc_authnet_md5_hash', $crypt->encrypt($key, $form_state['values']['uc_authnet_md5_hash']));
}
// Store any errors.
uc_store_encryption_errors($crypt, 'uc_authorizenet');
}
}
// Main handler for processing credit card transactions.
function uc_authorizenet_charge($order_id, $amount, $data) {
// Load the order.
$order = uc_order_load($order_id);
// Perform the appropriate action based on the transaction type.
switch ($data['txn_type']) {
// Reference transactions are handled through Authorize.Net's CIM.
case UC_CREDIT_REFERENCE_TXN:
return _uc_authorizenet_cim_profile_charge($order, $amount, $data);
// Set a reference only.
case UC_CREDIT_REFERENCE_SET:
// Return the error message if this failed.
if ($message = _uc_authorizenet_cim_profile_create($order)) {
return array('success' => FALSE, 'message' => $message);
}
else {
return array('success' => TRUE, 'message' => t('New customer profile created successfully at Authorize.Net.'));
}
// Accommodate all other transaction types.
default:
return _uc_authorizenet_charge($order, $amount, $data);
}
}
/**
* Create a CIM profile using an order's data.
*/
function _uc_authorizenet_cim_profile_create($order) {
$server = variable_get('uc_authnet_cim_mode', 'disabled');
// Help build the request.
$request = _uc_authorizenet_cim_profile_create_request($order);
// Request a profile from auth.net.
$xml = _uc_authorizenet_xml_api_wrapper('createCustomerProfileRequest', _uc_authorizenet_array_to_xml($request));
// Parse the response.
$response = _uc_authorizenet_cim_parse_response(uc_authorizenet_xml_api($server, $xml));
if ($response['resultCode'] == 'Error') {
uc_order_comment_save($order->order_id, 0, t('Authorize.Net: Creating CIM profile failed.
@error - @text', array('@error' => $response['code'], '@text' => $response['text'])), 'admin');
return $response['text'];
}
else {
uc_order_comment_save($order->order_id, 0, t('Authorize.Net: CIM profile created - @id', array('@id' => $response['customerProfileId'])));
}
// Save the new credit reference to the db.
$order->data = uc_credit_log_reference($order->order_id, $response['customerProfileId'], $order->payment_details['cc_number']);
return '';
}
/**
* Helper to create the CIM profile creation request.
*/
function _uc_authorizenet_cim_profile_create_request($order) {
return array(
'refId' => substr($order->order_id .'-'. time(), 0, 20),
'profile' => array(
'merchantCustomerId' => substr($order->uid, 0, 20),
'description' => substr(t('Order @order taking place at @date', array('@order' => $order->order_id, '@date' => format_date(time()))), 0, 255),
'email' => substr($order->primary_email, 0, 255),
'paymentProfiles' => array(
'billTo' => _uc_authorize_cim_xml_billto($order),
'payment' => array(
'creditCard' => array(
'cardNumber' => $order->payment_details['cc_number'],
'expirationDate' => $order->payment_details['cc_exp_year'] .'-'. str_pad($order->payment_details['cc_exp_month'], 2, '0', STR_PAD_LEFT),
),
),
),
'shipToList' => _uc_authorize_cim_xml_shipto($order),
),
);
}
/**
* Use a reference to charge to a CIM profile.
*/
function _uc_authorizenet_cim_profile_charge($order, $amount, $data) {
global $user;
$server = variable_get('uc_authnet_cim_mode', 'disabled');
// Help build the request.
$request = _uc_authorizenet_cim_profile_charge_request($order, $amount, $data);
// Check error state.
if (array_key_exists('errorCode', $request)) {
$comment[] = $request['text'];
$result = array(
'success' => FALSE,
);
}
// Request went off smooth.
else {
// Request a profile from auth.net.
$xml = _uc_authorizenet_xml_api_wrapper('createCustomerProfileTransactionRequest', _uc_authorizenet_array_to_xml($request));
// Parse the response.
$response = _uc_authorizenet_cim_parse_response(uc_authorizenet_xml_api($server, $xml));
// Error state.
if ($response['resultCode'] == 'Error') {
$result = array(
'success' => FALSE,
);
$comment[] = '('. $response['resultCode'] .': '. $response['text'] .')';
}
// Transaction succeeded.
else {
$result = array(
'success' => TRUE,
);
// Build info message.
$types = uc_credit_transaction_types();
$context = array(
'revision' => 'formatted-original',
'type' => 'amount',
);
$comment[] = t('@type: @amount', array('@type' => $types[$data['txn_type']], '@amount' => uc_price($amount, $context)));
// Save a comment to the order.
uc_order_comment_save($order->order_id, $user->uid, implode('
', $comment), 'admin');
}
}
// Build the response to the payment gateway API.
return $result + array(
'comment' => implode(', ', $comment),
'message' => implode('
', $comment),
'uid' => $user->uid,
);
}
/**
* Helper for building the request for a CIM profile charge.
*/
function _uc_authorizenet_cim_profile_charge_request($order, $amount, $data) {
$profile = _uc_authorizenet_cim_profile_get($order, $data['ref_id']);
if ($profile['resultCode'] == 'Error') {
return $profile;
}
else {
return array(
'refId' => substr($order->order_id .'-'. time(), 0, 20),
'transaction' => array(
'profileTransAuthCapture' => array(
'amount' => $amount,
'customerProfileId' => $profile['customerProfileId'],
'customerPaymentProfileId' => $profile['customerPaymentProfileId'],
'order' => array(
'invoiceNumber' => $order->order_id,
),
),
),
);
}
}
/**
* Get a CIM profile stored at Authorize.Net.
*/
function _uc_authorizenet_cim_profile_get($order, $profile_id) {
$server = variable_get('uc_authnet_cim_mode', 'disabled');
$request = array(
'customerProfileId' => $profile_id,
);
// Request a profile from auth.net.
$xml = _uc_authorizenet_xml_api_wrapper('getCustomerProfileRequest', _uc_authorizenet_array_to_xml($request));
// Parse the response.
$response = _uc_authorizenet_cim_parse_response(uc_authorizenet_xml_api($server, $xml));
return $response;
}
/**
* Get a CIM payment profile stored at auth.net.
*/
function _uc_authorizenet_cim_payment_profile_get($order, $profile_id, $payment_profile_id) {
$server = variable_get('uc_authnet_cim_mode', 'disabled');
$request = array(
'customerProfileId' => $profile_id,
);
// Request a profile from auth.net.
$xml = _uc_authorizenet_xml_api_wrapper('getCustomerPaymentProfileRequest', _uc_authorizenet_array_to_xml($request));
// Parse the response.
$response = _uc_authorizenet_cim_parse_response(uc_authorizenet_xml_api($server, $xml));
return $response['resultCode'] == 'Error' ? FALSE : $response;
}
/**
* Handles authorizations and captures through AIM at Authorize.Net
*/
function _uc_authorizenet_charge($order, $amount, $data) {
global $user;
// Build a description of the order for logging in Auth.Net.
$description = array();
foreach ((array) $order->products as $product) {
$description[] = $product->qty .'x '. $product->model;
}
$billing_country = uc_get_country_data(array('country_id' => $order->billing_country));
$delivery_country = uc_get_country_data(array('country_id' => $order->delivery_country));
$context = array(
'revision' => 'formatted-original',
'type' => 'amount',
);
$options = array(
'sign' => FALSE,
'thou' => FALSE,
'dec' => '.',
);
// Build the POST data for the transaction.
$submit_data = array(
// Merchant Information
'x_login' => variable_get('uc_authnet_api_login_id', ''),
'x_tran_key' => variable_get('uc_authnet_api_transaction_key', ''),
// Transaction Information
'x_version' => '3.1',
'x_type' => _uc_authorizenet_txn_map($data['txn_type']),
// 'x_method' => $order->payment_method == 'credit' ? 'CC' : 'ECHECK',
'x_method' => 'CC',
// 'x_recurring_billing' => 'FALSE',
'x_amount' => uc_price($amount, $context, $options),
'x_card_num' => $order->payment_details['cc_number'],
'x_exp_date' => $order->payment_details['cc_exp_month'] .'/'. $order->payment_details['cc_exp_year'],
'x_card_code' => $order->payment_details['cc_cvv'],
// 'x_trans_id' => '',
// 'x_auth_code' => '',
'x_test_request' => variable_get('uc_authnet_aim_txn_mode', 'live_test') == 'live_test' ? 'TRUE' : 'FALSE',
'x_duplicate_window' => variable_get('uc_authnet_duplicate_window', 120),
// Order Information
'x_invoice_num' => $order->order_id,
'x_description' => substr(implode(', ', $description), 0, 255),
// Customer Information
'x_first_name' => substr($order->billing_first_name, 0, 50),
'x_last_name' => substr($order->billing_last_name, 0, 50),
'x_company' => substr($order->billing_company, 0, 50),
'x_address' => substr($order->billing_street1, 0, 60),
'x_city' => substr($order->billing_city, 0, 40),
'x_state' => substr(uc_get_zone_code($order->billing_zone), 0, 40),
'x_zip' => substr($order->billing_postal_code, 0, 20),
'x_country' => !$billing_country ? '' : $billing_country[0]['country_iso_code_2'],
'x_phone' => substr($order->billing_phone, 0, 25),
// 'x_fax' => substr('', 0, 25),
'x_email' => substr($order->primary_email, 0, 255),
'x_cust_id' => substr($order->uid, 0, 20),
'x_customer_ip' => substr(ip_address(), 0, 15),
// Shipping Information
'x_ship_to_first_name' => substr($order->delivery_first_name, 0, 50),
'x_ship_to_last_name' => substr($order->delivery_last_name, 0, 50),
'x_ship_to_company' => substr($order->delivery_company, 0, 50),
'x_ship_to_address' => substr($order->delivery_street1, 0, 60),
'x_ship_to_city' => substr($order->delivery_city, 0, 40),
'x_ship_to_state' => substr(uc_get_zone_code($order->delivery_zone), 0, 40),
'x_ship_to_zip' => substr($order->delivery_postal_code, 0, 20),
'x_ship_to_country' => !$delivery_country ? '' : $delivery_country[0]['country_iso_code_2'],
// Extra Information
'x_delim_data' => 'TRUE',
'x_delim_char' => '|',
'x_encap_char' => '"',
'x_relay_response' => 'FALSE',
'x_email_customer' => variable_get('uc_authnet_aim_email_customer', FALSE) ? 'TRUE' : 'FALSE',
);
if ($data['txn_type'] == UC_CREDIT_PRIOR_AUTH_CAPTURE) {
$submit_data['x_trans_id'] = $data['auth_id'];
}
// Determine the correct URL based on the transaction mode.
if (variable_get('uc_authnet_aim_txn_mode', 'live_test') == 'developer_test') {
$post_url = variable_get('uc_authnet_api_test_gateway_url',
UC_AUTHORIZENET_TEST_GATEWAY_URL);
}
else {
$post_url = variable_get('uc_authnet_api_live_gateway_url',
UC_AUTHORIZENET_LIVE_GATEWAY_URL);
}
// Translate the data array into a string we can POST.
$post_fields = array();
foreach ($submit_data as $key => $value) {
$post_fields[] = $key .'='. urlencode($value);
}
// Setup the cURL request.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $post_url);
curl_setopt($ch, CURLOPT_VERBOSE, 0);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, implode('&', $post_fields));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_NOPROGRESS, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
$result = curl_exec($ch);
// Log any errors to the watchdog.
if ($error = curl_error($ch)) {
watchdog('uc_authorizenet', 'cURL error: @error', array('@error' => $error), WATCHDOG_ERROR);
return array('success' => FALSE);
}
curl_close($ch);
$response = explode('|', $result);
if (variable_get('uc_authnet_response_debug', FALSE)) {
watchdog('uc_authorizenet', 'Debug response: !data', array('!data' => '
'. check_plain(print_r($response, TRUE)) .'')); } // Trim off the encapsulating character from the results. for ($i = 0; $i < count($response); $i++) { $response[$i] = substr($response[$i], 1, strlen($response[$i]) - 2); } /** * Response key index: * 0 = Response Code * 2 = Response Reason Code * 3 = Response Reason Text * 4 = Authorization Code * 5 = Address Verification Service (AVS) Response * 6 = Transaction ID; needed for CREDIT, PRIOR_AUTH_CAPTURE, and VOID transactions. * 9 = Amount * 11 = Transaction Type * 32 = Tax Amount Charged * 37 = Transaction Response MD5 Hash * 38 = Card Code (CVV) Response */ // If we didn't get an approval response code... if ($response[0] != '1') { // Fail the charge with the reason text in the decline message. $result = array( 'success' => FALSE, 'message' => t('Credit card payment declined: @message', array('@message' => $response[3])), 'uid' => $user->uid, ); } else { // Build a message for display and comments in the payments table. $message = t('Type: @type