uc_recurring.uc_cybersource.inc in UC Recurring Payments and Subscriptions 7.2
Same filename and directory in other branches
Uc recurring implementation for the CyberSource module.
File
includes/uc_recurring.uc_cybersource.incView source
<?php
/**
* @file
* Uc recurring implementation for the CyberSource module.
*/
/**
* Implements hook_recurring_info().
*/
function uc_recurring_uc_cybersource_recurring_info() {
$items['cybersource'] = array(
'name' => t('CyberSource'),
'payment method' => 'credit',
'module' => 'uc_recurring',
'fee handler' => 'cybersource',
'renew callback' => 'uc_recurring_cybersource_renew',
'process callback' => 'uc_recurring_cybersource_process',
'saved profile' => TRUE,
'menu' => array(
'charge' => UC_RECURRING_MENU_DEFAULT,
'edit' => UC_RECURRING_MENU_DEFAULT,
'update' => array(
'title' => 'Update Account Details',
'page arguments' => array(
'uc_recurring_cybersource_update_form',
),
'file' => 'includes/uc_recurring.uc_cybersource.inc',
),
'profile' => array(
'title' => 'Customer Profile',
'page arguments' => array(
'uc_recurring_cybersource_profile_form',
),
'access callback' => 'user_access',
'access arguments' => array(
'administer recurring fees',
),
'file' => 'includes/uc_recurring.uc_cybersource.inc',
),
'cancel' => UC_RECURRING_MENU_DEFAULT,
),
);
return $items;
}
/**
* Set up the recurring fee by creating a CIM profile for future payments
*
* @param $order
* The order object.
* @param $fee
* The fee object.
* @return
* TRUE if recurring fee setup
*/
function uc_recurring_cybersource_process($order, &$fee) {
$fee->fee_handler = 'cybersource';
if (variable_get('uc_cybersource_soap_create_profile', FALSE) == FALSE) {
// The uc_cybersource module doesn't yet support UC_CREDIT_REFERENCE_SET
return _uc_recurring_cybersource_create_profile($order);
}
return TRUE;
}
/**
* Process a renewal using the CIM profile.
*
* @param $order
* The order object.
* @param $fee
* The fee object.
* @return
* TRUE if renewal succeeded
*/
function uc_recurring_cybersource_renew($order, &$fee) {
if (!empty($order->data['cc_txns']['references'])) {
$refs = array_keys($order->data['cc_txns']['references']);
$data = array(
'txn_type' => UC_CREDIT_REFERENCE_TXN,
'ref_id' => end($refs),
);
$result = uc_cybersource_charge($order->order_id, $order->order_total, $data);
if ($result['success'] == TRUE) {
uc_payment_enter($order->order_id, $order->payment_method, $order->order_total, $fee->uid, $result['data'], $result['comment']);
return TRUE;
}
}
return FALSE;
}
/**
* Create form for updating credit card details for recurring fee.
*/
function uc_recurring_cybersource_update_form($form, $form_state, $rfid) {
// Load fee.
$fee = uc_recurring_fee_user_load($rfid);
// Load corresponding order.
$order = uc_order_load($fee->order_id);
$form['rfid'] = array(
'#type' => 'value',
'#value' => $rfid,
);
$form['cc_data'] = array(
'#type' => 'fieldset',
'#title' => t('Credit card details'),
'#theme' => 'uc_payment_method_credit_form',
'#tree' => TRUE,
);
$form['cc_data'] = array_merge($form['cc_data'], uc_payment_method_credit_form($form, $form_state, $order));
unset($form['cc_data']['cc_policy']);
// Make credit card info form items required
$form['cc_data']['cc_owner']['#required'] = TRUE;
$form['cc_data']['cc_number']['#required'] = TRUE;
$form['cc_data']['cc_exp_month']['#required'] = TRUE;
$form['cc_data']['cc_exp_year']['#required'] = TRUE;
$form['cc_data']['cc_cvv']['#required'] = TRUE;
// Add billing address form
if ($billing_items = uc_order_pane_bill_to('edit-form', $order)) {
$form = array_merge($form, $billing_items);
$form['bill_to']['#title'] = t('Billing address');
$form['bill_to']['#description'] = t('Credit card information must be provided to update billing address.');
$form['bill_to']['#collapsible'] = FALSE;
$form['bill_to']['#theme'] = 'uc_recurring_cybersource_billto_form';
// Make billing info form items required
$form['bill_to']['billing_first_name']['#required'] = TRUE;
$form['bill_to']['billing_last_name']['#required'] = TRUE;
$form['bill_to']['billing_street1']['#required'] = TRUE;
$form['bill_to']['billing_city']['#required'] = TRUE;
$form['bill_to']['billing_country']['#required'] = TRUE;
$form['bill_to']['billing_postal_code']['#required'] = TRUE;
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Update'),
'#suffix' => l(t('Cancel'), 'admin/store/orders/recurring/view/fee/' . $rfid),
);
return $form;
}
/**
* Implements update form validation for the cybersource CIM gateway.
*/
function uc_recurring_cybersource_update_form_validate(&$form, &$form_state) {
$has_errors = FALSE;
$values = $form_state['values'];
// Make sure an owner value was entered.
if (variable_get('uc_credit_owner_enabled', FALSE) && empty($values['cc_data']['cc_owner'])) {
form_set_error('cc_data][cc_owner', t('Enter the owner name as it appears on the card.'));
$has_errors = TRUE;
}
// Validate the CC number if that's turned on/check for non-digits.
if (variable_get('uc_credit_validate_numbers', TRUE)) {
// Remove the non-numeric characters
$cc_number = preg_replace('/[^0-9]/', '', $values['cc_data']['cc_number']);
form_set_value($form['cc_data']['cc_number'], $cc_number, $form_state);
if (!_uc_credit_valid_card_number($cc_number) || !ctype_digit($cc_number)) {
form_set_error('cc_data][cc_number', t('You have entered an invalid credit card number.'));
$has_errors = TRUE;
}
}
// Validate the start date (if entered).
if (variable_get('uc_credit_start_enabled', FALSE) && !_uc_credit_valid_card_start($values['cc_data']['cc_start_month'], $values['cc_data']['cc_start_year'])) {
form_set_error('cc_data][cc_start_month', t('The start date you entered is invalid.'));
form_set_error('cc_data][cc_start_year', t('The start date you entered is invalid.'));
$has_errors = TRUE;
}
// Validate the card expiration date.
if (!_uc_credit_valid_card_expiration($values['cc_data']['cc_exp_month'], $values['cc_data']['cc_exp_year'])) {
form_set_error('cc_data][cc_exp_month', t('The credit card you entered has expired.'));
form_set_error('cc_data][cc_exp_year', t('The credit card you entered has expired.'));
$has_errors = TRUE;
}
// Validate the issue number (if entered). With issue numbers, '01' is
// different from '1', but is_numeric() is still appropriate.
if (variable_get('uc_credit_issue_enabled', FALSE) && !_uc_credit_valid_card_issue($values['cc_data']['cc_issue'])) {
form_set_error('cc_data][cc_issue', t('The issue number you entered is invalid.'));
$has_errors = TRUE;
}
// Validate the CVV number if enabled.
if (variable_get('uc_credit_cvv_enabled', TRUE) && !_uc_credit_valid_cvv($values['cc_data']['cc_cvv'])) {
form_set_error('cc_data][cc_cvv', t('You have entered an invalid CVV number.'));
$has_errors = TRUE;
}
// Validate the bank name if enabled.
if (variable_get('uc_credit_bank_enabled', FALSE) && empty($values['cc_data']['cc_bank'])) {
form_set_error('cc_data][cc_bank', t('You must enter the issuing bank for that card.'));
$has_errors = TRUE;
}
//Form requires rebuilding form so codes and expiration blocks will display correctly
if ($has_errors) {
// remove duplicate error messages when two fields share a message
$_SESSION['messages']['error'] = array_unique($_SESSION['messages']['error']);
$form_state["rebuild"] = TRUE;
}
}
/**
* Implements update form submit for the cybersource CIM gateway.
*/
function uc_recurring_cybersource_update_form_submit(&$form, &$form_state) {
$fee = uc_recurring_fee_user_load($form_state['values']['rfid']);
$order = uc_order_load($fee->order_id);
$last4old = substr($order->payment_details['cc_number'], -4);
$order->payment_details = $form_state['values']['cc_data'];
$log = array();
// Process billing address form changes
if (($bill_changes = uc_order_pane_bill_to('edit-process', $order, $form, $form_state)) != NULL) {
foreach ($bill_changes as $key => $value) {
if ($order->{$key} != $value) {
if (!is_array($value)) {
$log[$key] = array(
'old' => $order->{$key},
'new' => $value,
);
}
$order->{$key} = $value;
}
}
}
$refs = array_keys($order->data['cc_txns']['references']);
$subscription_id = end($refs);
if ($message = _uc_recurring_uc_cybersource_update_paymentprofile($order, $subscription_id)) {
drupal_set_message(t('Account update failed: @error', array(
'@error' => $message,
)), 'error');
$form_state['redirect'] = FALSE;
}
else {
drupal_set_message(t('Account updated'));
uc_order_save($order);
$last4new = substr($order->payment_details['cc_number'], -4);
if ($last4new != $last4old) {
$log[t('Credit card')] = array(
'old' => $last4old,
'new' => $last4new,
);
}
if ($log) {
uc_order_log_changes($order->order_id, $log);
}
}
}
/**
* Theme billing address form items
*/
function theme_uc_recurring_cybersource_billto_form($form) {
$output = '<table class="order-edit-table">';
foreach (element_children($form) as $field) {
$title = $form[$field]['#title'];
$form[$field]['#title'] = NULL;
$output .= '<tr><td class="oet-label">' . $title . ':</td><td>' . drupal_render($form[$field]) . '</td></tr>';
}
$output .= '</table>';
return $output;
}
/**
* Update the payment profile
*/
function _uc_recurring_uc_cybersource_update_paymentprofile($order, $subscription_id) {
module_load_include('inc', 'uc_cybersource', 'uc_cybersource.soap');
try {
$soapClient = new CyberSourceSoapClient(_uc_recurring_cybersource_soap_wsdl_url(), array());
$login = _uc_cybersource_soap_login_data();
// Create the request with some meta data.
$request = new stdClass();
$request->merchantID = $login['merchant_id'];
$request->merchantReferenceCode = $order->order_id;
$paySubscriptionUpdateService = new stdClass();
$paySubscriptionUpdateService->run = 'true';
$request->paySubscriptionUpdateService = $paySubscriptionUpdateService;
$recurringSubscriptionInfo = new stdClass();
$recurringSubscriptionInfo->subscriptionID = $subscription_id;
$request->recurringSubscriptionInfo = $recurringSubscriptionInfo;
$request->billTo = _uc_recurring_cybersource_billto_obj($order);
$request->card = _uc_recurring_cybersource_card_obj($order);
$reply = $soapClient
->runTransaction($request);
} catch (SoapFault $exception) {
// Log and display errors if Ubercart is unable to connect via SOAP.
watchdog('uc_cybersource', 'Unable to connect to CyberSource via SOAP.', array(), WATCHDOG_ERROR);
drupal_set_message(t('We apologize for the delay, but we are unable to process your credit card at this time. Please <a href="!url">contact sales</a> to complete your order.', array(
'!url' => url('contact'),
)), 'error');
}
// Process a reply from CyberSource.
if (isset($reply->paySubscriptionUpdateReply)) {
if ($reply->paySubscriptionUpdateReply->reasonCode == 100) {
uc_order_comment_save($order->order_id, 0, t('CyberSource: Subscription @subscription_id updated.', array(
'@subscription_id' => $subscription_id,
)), 'admin');
return;
}
else {
uc_order_comment_save($order->order_id, 0, t('<b>Attempt to update CyberSource subscription profile failed.</b><br /><b>Reason:</b> @code', array(
'@code' => $reply->paySubscriptionCreateReply->reasonCode,
)), 'admin');
return t('Subscription update failed with code @code', array(
'@code' => $reply->paySubscriptionCreateReply->reasonCode,
));
}
}
uc_order_comment_save($order->order_id, 0, t('CyberSource: Subscription @subscription_id update failed: invalid reply.', array(
'@subscription_id' => $subscription_id,
)), 'admin');
return t('Subscription update failed due to an invalid reply.');
}
/**
* List the profile information.
*/
function uc_recurring_cybersource_profile_form($form, $form_state, $rfid) {
$fee = uc_recurring_fee_user_load($rfid);
$order = uc_order_load($fee->order_id);
$refs = array_keys($order->data['cc_txns']['references']);
$subscription_id = end($refs);
module_load_include('inc', 'uc_cybersource', 'uc_cybersource.soap');
try {
$soapClient = new CyberSourceSoapClient(_uc_recurring_cybersource_soap_wsdl_url(), array());
$login = _uc_cybersource_soap_login_data();
// Create the request with some meta data.
$request = new stdClass();
$request->merchantID = $login['merchant_id'];
$request->merchantReferenceCode = $order->order_id;
$paySubscriptionRetrieveService = new stdClass();
$paySubscriptionRetrieveService->run = 'true';
$request->paySubscriptionRetrieveService = $paySubscriptionRetrieveService;
$recurringSubscriptionInfo = new stdClass();
$recurringSubscriptionInfo->subscriptionID = $subscription_id;
$request->recurringSubscriptionInfo = $recurringSubscriptionInfo;
$reply = $soapClient
->runTransaction($request);
} catch (SoapFault $exception) {
// Log and display errors if Ubercart is unable to connect via SOAP.
watchdog('uc_cybersource', 'Unable to connect to CyberSource via SOAP.', array(), WATCHDOG_ERROR);
drupal_set_message(t('We apologize for the delay, but we are unable to process your credit card at this time. Please <a href="!url">contact sales</a> to complete your order.', array(
'!url' => url('contact'),
)), 'error');
}
if (isset($reply->paySubscriptionRetrieveReply)) {
$header = $rows = $row = array();
foreach ($reply->paySubscriptionRetrieveReply as $key => $val) {
$header[] = $key;
$row[] = $val;
}
$rows[] = $row;
$form['info'] = array(
'#markup' => t('The profile details below were returned from CyberSource'),
);
$form['profile'] = array(
'#markup' => theme('table', array(
'header' => $header,
'rows' => $rows,
)),
);
$form['return'] = array(
'#markup' => l(t('Return'), 'admin/store/orders/recurring/view/fee/' . $rfid),
);
}
else {
drupal_set_message(t('Invalid response from SOAP server while retrieving subscription @subscription_id', array(
'@subscription_id' => $subscription_id,
)), 'error');
}
return $form;
}
/**
* Get the WDSL URL used for SOAP requests
*/
function _uc_recurring_cybersource_soap_wsdl_url() {
if (variable_get('uc_cybersource_server', 'test') == 'test') {
return 'https://ics2wstest.ic3.com/commerce/1.x/transactionProcessor/CyberSourceTransaction_1.38.wsdl';
}
else {
return 'https://ics2ws.ic3.com/commerce/1.x/transactionProcessor/CyberSourceTransaction_1.38.wsdl';
}
}
/**
* Set up a CyberSource customer profile
* @param $order Order object
*/
function _uc_recurring_cybersource_create_profile($order) {
module_load_include('inc', 'uc_cybersource', 'uc_cybersource.soap');
try {
$soapClient = new CyberSourceSoapClient(_uc_recurring_cybersource_soap_wsdl_url(), array());
$login = _uc_cybersource_soap_login_data();
// Create the request with some meta data.
$request = new stdClass();
$request->merchantID = $login['merchant_id'];
$request->merchantReferenceCode = $order->order_id;
$recurringSubscriptionInfo = new stdClass();
$recurringSubscriptionInfo->amount = 0;
$recurringSubscriptionInfo->frequency = 'on-demand';
$request->recurringSubscriptionInfo = $recurringSubscriptionInfo;
$paySubscriptionCreateService = new stdClass();
$paySubscriptionCreateService->run = 'true';
$request->paySubscriptionCreateService = $paySubscriptionCreateService;
$purchaseTotals = new stdClass();
$purchaseTotals->currency = variable_get('uc_cybersource_currency', 'usd');
$request->purchaseTotals = $purchaseTotals;
$request->billTo = _uc_recurring_cybersource_billto_obj($order);
$request->card = _uc_recurring_cybersource_card_obj($order);
$reply = $soapClient
->runTransaction($request);
} catch (SoapFault $exception) {
// Log and display errors if Ubercart is unable to connect via SOAP.
watchdog('uc_cybersource', 'Unable to connect to CyberSource via SOAP.', array(), WATCHDOG_ERROR);
drupal_set_message(t('We apologize for the delay, but we are unable to process your credit card at this time. Please <a href="!url">contact sales</a> to complete your order.', array(
'!url' => url('contact'),
)), 'error');
}
if (isset($reply->paySubscriptionCreateReply)) {
// If the create request was successful...
if ($reply->paySubscriptionCreateReply->reasonCode == '100') {
$id = $reply->paySubscriptionCreateReply->subscriptionID;
// Save the subscription ID to the order's data array.
$order->data = uc_credit_log_reference($order->order_id, $id, $order->payment_details['cc_number']);
uc_order_comment_save($order->order_id, 0, t('<b>CyberSource profile created.</b><br /><b>Subscription ID:</b> @id', array(
'@id' => $id,
)), 'admin');
return TRUE;
}
else {
uc_order_comment_save($order->order_id, 0, t('<b>Attempt to create CyberSource profile failed.</b><br /><b>Reason:</b> @code', array(
'@code' => $reply->paySubscriptionCreateReply->reasonCode,
)), 'admin');
}
}
return FALSE;
}
/**
* Create a billTo SOAP object from an order
* @param $order Order object
*/
function _uc_recurring_cybersource_billto_obj($order) {
$billing_country = uc_get_country_data(array(
'country_id' => $order->billing_country,
));
$billTo = new stdClass();
$billTo->firstName = $order->billing_first_name;
$billTo->lastName = $order->billing_last_name;
$billTo->street1 = $order->billing_street1;
if ($order->billing_street2) {
$billTo->street2 = $order->billing_street2;
}
$billTo->city = $order->billing_city;
$billTo->state = uc_get_zone_code($order->billing_zone);
$billTo->postalCode = $order->billing_postal_code;
$billTo->country = $billing_country[0]['country_iso_code_2'];
if ($order->billing_phone) {
$billTo->phoneNumber = $order->billing_phone;
}
$billTo->email = $order->primary_email;
$billTo->customerID = $order->uid;
return $billTo;
}
/**
* Create a card SOAP object from an order
* @param $order Order object
*/
function _uc_recurring_cybersource_card_obj($order) {
$card = new stdClass();
$card->accountNumber = $order->payment_details['cc_number'];
$card->expirationMonth = $order->payment_details['cc_exp_month'];
$card->expirationYear = $order->payment_details['cc_exp_year'];
$card->cardType = _uc_cybersource_card_type($order->payment_details['cc_number']);
if (variable_get('uc_credit_cvv_enabled', TRUE)) {
$card->cvNumber = $order->payment_details['cc_cvv'];
}
return $card;
}
Functions
Name | Description |
---|---|
theme_uc_recurring_cybersource_billto_form | Theme billing address form items |
uc_recurring_cybersource_process | Set up the recurring fee by creating a CIM profile for future payments |
uc_recurring_cybersource_profile_form | List the profile information. |
uc_recurring_cybersource_renew | Process a renewal using the CIM profile. |
uc_recurring_cybersource_update_form | Create form for updating credit card details for recurring fee. |
uc_recurring_cybersource_update_form_submit | Implements update form submit for the cybersource CIM gateway. |
uc_recurring_cybersource_update_form_validate | Implements update form validation for the cybersource CIM gateway. |
uc_recurring_uc_cybersource_recurring_info | Implements hook_recurring_info(). |
_uc_recurring_cybersource_billto_obj | Create a billTo SOAP object from an order |
_uc_recurring_cybersource_card_obj | Create a card SOAP object from an order |
_uc_recurring_cybersource_create_profile | Set up a CyberSource customer profile |
_uc_recurring_cybersource_soap_wsdl_url | Get the WDSL URL used for SOAP requests |
_uc_recurring_uc_cybersource_update_paymentprofile | Update the payment profile |