View source
<?php
module_load_include('inc', 'commerce_sagepay', 'includes/commerce_sagepay_common');
module_load_include('inc', 'commerce_sagepay', 'includes/commerce_sagepay_utils');
function commerce_sagepay_server_redirect_form($form, &$form_state, $order, $payment_method) {
if (!variable_get(SAGEPAY_SETTING_VENDOR_NAME)) {
drupal_set_message(t("SagePay Server Integration is not configured for use.\n <a href=\"/admin/commerce/config/sagepay\">Set a vendor name on the settings page.</a>"), 'error');
return array();
}
drupal_add_js(drupal_get_path('module', 'commerce_sagepay') . '/js/commerce_sagepay_server.js');
$wrapper = entity_metadata_wrapper('commerce_order', $order);
$total = commerce_line_items_total($wrapper->commerce_line_items);
$total['amount'] = $wrapper->commerce_order_total->amount
->value();
$profile = commerce_customer_profile_load($order->commerce_customer_billing[LANGUAGE_NONE][0]['profile_id']);
$billing_address = $profile->commerce_customer_address[LANGUAGE_NONE][0];
$delivery_address = NULL;
if (isset($order->commerce_customer_shipping)) {
$delivery_profile = commerce_customer_profile_load($order->commerce_customer_shipping[LANGUAGE_NONE][0]['profile_id']);
$delivery_address = $delivery_profile->commerce_customer_address[LANGUAGE_NONE][0];
}
$settings = array();
$data = _commerce_sagepay_encrypted_order($settings, $order, $total, $billing_address, $delivery_address, SAGEPAY_SERVER);
$post = '';
foreach ($data as $name => $value) {
$post .= urlencode($name) . '=' . urlencode($value) . '&';
}
$post = substr($post, 0, -1);
switch (variable_get(SAGEPAY_SETTING_TRANSACTION_MODE)) {
case SAGEPAY_TXN_MODE_LIVE:
$server_url = SAGEPAY_SERVER_SERVER_LIVE;
break;
case SAGEPAY_TXN_MODE_TEST:
$server_url = SAGEPAY_SERVER_SERVER_TEST;
break;
case SAGEPAY_TXN_MODE_SIMULATION:
$server_url = SAGEPAY_SERVER_SERVER_SIMULATION;
break;
}
$response = _commerce_sagepay_request_post($server_url, $post);
$transaction = commerce_payment_transaction_new('commerce_sagepay_server', $order->order_id);
$transaction->amount = $total['amount'];
$transaction->currency_code = $total['currency_code'];
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
$transaction->instance_id = $payment_method['instance_id'];
$transaction->remote_id = isset($response['VPSTxId']) ? $response['VPSTxId'] : '';
$transaction->payload = $response;
$transaction->payload['VendorTxCode'] = $data['VendorTxCode'];
$transaction->payload['merchantCustomerId'] = $order->uid;
if (!isset($response['Status'])) {
$transaction->payload['vps_status'] = 'TIMEOUT';
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
$transaction->message = 'No valid response from Sagepay';
commerce_payment_transaction_save($transaction);
watchdog('custom_sagepay_server', 'No Status code received in SagePay callback.', array(), WATCHDOG_ERROR);
}
$transaction_status = explode(" ", $response["Status"]);
switch ($transaction_status[0]) {
case 'OK':
$transaction->remote_status = SAGEPAY_REMOTE_STATUS_STARTED;
commerce_payment_transaction_save($transaction);
$iframe_enabled = variable_get(SAGEPAY_SETTING_USE_IFRAME, 0);
if ($iframe_enabled == 1) {
$form['#action'] = $response["NextURL"];
$form['#attributes']['target'] = 'iframe_sagepay';
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Proceed to SagePay'),
);
$form['iframe_sagepay'] = array(
'#markup' => '<iframe style="width: 600px; height:500px" scrolling="no" frameborder="0" name="iframe_sagepay" id="iframe_sagepay" src="' . url('commerce_sagepay/sagepay_waiting_page') . '"></iframe>',
);
return $form;
}
else {
header("Location: " . $response["NextURL"]);
}
exit;
case 'FAIL':
$transaction->payload['vps_status'] = 'FAIL';
$transaction->remote_status = SAGEPAY_REMOTE_STATUS_FAIL;
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
$transaction->message = 'FAIL Response from Sagepay';
commerce_payment_transaction_save($transaction);
drupal_goto('checkout/' . $order->order_id . '/payment/return/' . $order->data['payment_redirect_key']);
break;
case 'INVALID':
$transaction->payload['vps_status'] = 'INVALID';
$transaction->remote_status = SAGEPAY_REMOTE_STATUS_INVALID;
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
$transaction->message = $response['StatusDetail'];
commerce_payment_transaction_save($transaction);
watchdog('commerce_sagepay', 'INVALID Status response from SagePay for order %order_id', array(
'%order_id' => $order->order_id,
), WATCHDOG_ERROR);
drupal_goto('checkout/' . $order->order_id . '/payment/return/' . $order->data['payment_redirect_key']);
break;
default:
$transaction->payload['vps_status'] = 'UNKNOWN';
$transaction->remote_status = SAGEPAY_REMOTE_STATUS_UNKNOWN;
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
$transaction->message = 'Unknown or invalid response from Sagepay';
commerce_payment_transaction_save($transaction);
watchdog('commerce_sagepay', 'Unrecognised Status response from SagePay for order %order_id (%response_code)', array(
'%order_id' => $order->order_id,
'%response_code' => $transaction_status[0],
), WATCHDOG_ERROR);
drupal_goto('checkout/' . $order->order_id . '/payment/return/' . $order->data['payment_redirect_key']);
break;
}
}
function commerce_sagepay_server_redirect_form_validate($order, $payment_method) {
$return = FALSE;
$transactions = commerce_payment_transaction_load_multiple(array(), array(
'order_id' => $order->order_id,
));
if (count($transactions) > 0) {
$transaction = end($transactions);
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
$failure_message = t('There was a problem processing your transaction. Your credit/debit card was not charged. Please try again later.');
switch ($transaction->payload['vps_status']) {
case 'OK':
$transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
$return = TRUE;
break;
case 'TAMPER':
drupal_set_message(check_plain($failure_message), 'error');
break;
case 'NOTAUTHED':
drupal_set_message(check_plain($failure_message), 'error');
break;
case 'REJECTED':
drupal_set_message(check_plain($failure_message), 'error');
break;
case 'ABORT':
break;
case 'ERROR':
case 'FAIL':
case 'FAIL2':
case 'UNKNOWN':
drupal_set_message(check_plain($failure_message), 'error');
break;
}
commerce_payment_transaction_save($transaction);
}
return $return;
}
function commerce_sagepay_server_waiting_page() {
print '<html><head><title></title></head><body><p>';
print t('Please wait to be redirected to the payment page.');
print '</p></body></html>';
}
function commerce_sagepay_server_handle_callback($order_id, $key, $debug_vps = array()) {
$notification = array();
$order = commerce_order_load($order_id);
$payment_key = $order->data['payment_redirect_key'];
if (!$payment_key === $key) {
$notification['status'] = 'INVALID';
$notification['message'] = t('Payment Redirect key did not match.');
}
if (empty($debug_vps)) {
$vps_data = $_POST;
}
if (empty($vps_data)) {
$notification['status'] = 'ERROR';
$notification['message'] = t('No Payload returned in the notification POST.');
watchdog('commerce_sagepay', 'VPS Callback URL accessed with no POST data
submitted.', array(), WATCHDOG_WARNING);
}
if (empty($notification)) {
$conditions = array(
'order_id' => $order_id,
'remote_id' => $vps_data['VPSTxId'],
'remote_status' => 'STARTED',
);
$transactions = commerce_payment_transaction_load_multiple(array(), $conditions);
if (empty($transactions)) {
$notification['status'] = 'INVALID';
$notification['message'] = t('No matching transaction found');
watchdog('commerce_sagepay', 'No Matching transaction found in Sage
Pay Server VPS Callback for order %order_id', array(
'%order_id' => $order_id,
), WATCHDOG_ERROR);
}
if (count($transactions) > 1) {
$notification['status'] = 'INVALID';
$notification['message'] = t('Multiple matching transaction found');
watchdog('commerce_sagepay', 'Multiple matching transaction found in
Sage Pay Server VPS Callback for order %order_id', array(
'%order_id' => $order_id,
), WATCHDOG_ERROR);
}
$transaction_values = array_values($transactions);
$transaction = $transaction_values[0];
$payment_method = $transaction->payment_method;
if ($transaction->payload['VendorTxCode'] == $vps_data["VendorTxCode"]) {
$charge = array(
'amount' => $transaction->amount,
'currency_code' => $transaction->currency_code,
);
$vendor_name = variable_get(SAGEPAY_SETTING_VENDOR_NAME);
if (isset($order->data['sagepay_overrides']['Vendor'])) {
$vendor_name = $order->data['sagepay_overrides']['Vendor'];
}
$vendor_name = drupal_strtolower($vendor_name);
$md5_check = array();
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["VPSTxId"]) ? $_REQUEST["VPSTxId"] : '', "Text");
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["VendorTxCode"]) ? $_REQUEST["VendorTxCode"] : '', "VendorTxCode");
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["Status"]) ? $_REQUEST["Status"] : '', "Text");
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["TxAuthNo"]) ? $_REQUEST["TxAuthNo"] : '', "Number");
$md5_check[] = $vendor_name;
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["AVSCV2"]) ? $_REQUEST["AVSCV2"] : '', "Text");
$md5_check[] = $transaction->payload['SecurityKey'];
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["AddressResult"]) ? $_REQUEST["AddressResult"] : '', "Text");
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["PostCodeResult"]) ? $_REQUEST["PostCodeResult"] : '', "Text");
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["CV2Result"]) ? $_REQUEST["CV2Result"] : '', "Text");
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["GiftAid"]) ? $_REQUEST["GiftAid"] : '', "Number");
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["3DSecureStatus"]) ? $_REQUEST["3DSecureStatus"] : '', "Text");
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["CAVV"]) ? $_REQUEST["CAVV"] : '', "Text");
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["AddressStatus"]) ? $_REQUEST["AddressStatus"] : '', "Text");
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["PayerStatus"]) ? $_REQUEST['PayerStatus'] : '', "Text");
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["CardType"]) ? $_REQUEST["CardType"] : '', "Text");
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["Last4Digits"]) ? $_REQUEST["Last4Digits"] : '', "Text");
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["DeclineCode"]) ? $_REQUEST["DeclineCode"] : '', "Text");
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["ExpiryDate"]) ? $_REQUEST["ExpiryDate"] : '', "Text");
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["FraudResponse"]) ? $_REQUEST["FraudResponse"] : '', "Text");
$md5_check[] = _commerce_sagepay_clean_input(isset($_REQUEST["BankAuthCode"]) ? $_REQUEST["BankAuthCode"] : '', "Text");
$str_vpssignature = _commerce_sagepay_clean_input(check_plain($_REQUEST["VPSSignature"]), "Text");
$str_message = implode('', $md5_check);
$str_mysignature = strtoupper(md5($str_message));
if ($str_mysignature == $str_vpssignature) {
$transaction->payload['vps_status'] = $vps_data['Status'];
switch ($vps_data['Status']) {
case 'OK':
watchdog('commerce_sagepay', 'OK Payment callback received from
SagePay for order %order_id with status code %status', array(
'%order_id' => $order_id,
'%status' => $transaction->payload['Status'],
));
commerce_sagepay_transaction($payment_method, $order, $charge, $vps_data, COMMERCE_PAYMENT_STATUS_SUCCESS, $vps_data['TxType'], $transaction);
$notification['status'] = 'OK';
$notification['message'] = t('Transaction notification received.');
break;
case 'NOTAUTHED':
watchdog('commerce_sagepay', 'NOTAUTHED error from SagePay for order %order_id with message %msg', array(
'%order_id' => $order_id,
'%msg' => $transaction->payload['StatusDetail'],
), WATCHDOG_ALERT);
commerce_sagepay_transaction($payment_method, $order, $charge, $vps_data, COMMERCE_PAYMENT_STATUS_FAILURE, SAGEPAY_REMOTE_STATUS_FAIL, $transaction);
$notification['status'] = 'OK';
$notification['message'] = $transaction->payload['StatusDetail'];
$notification['cancel'] = TRUE;
break;
case 'REJECTED':
watchdog('commerce_sagepay', 'REJECTED error from SagePay for order %order_id with message %msg', array(
'%order_id' => $order_id,
'%msg' => $transaction->payload['StatusDetail'],
), WATCHDOG_ALERT);
commerce_sagepay_transaction($payment_method, $order, $charge, $vps_data, COMMERCE_PAYMENT_STATUS_FAILURE, SAGEPAY_REMOTE_STATUS_FAIL, $transaction);
$notification['status'] = 'OK';
$notification['message'] = $transaction->payload['StatusDetail'];
$notification['cancel'] = TRUE;
break;
case 'ABORT':
watchdog('commerce_sagepay', 'ABORT error from SagePay for order %order_id with message %msg', array(
'%order_id' => $order_id,
'%msg' => $transaction->payload['StatusDetail'],
), WATCHDOG_ALERT);
commerce_sagepay_transaction($payment_method, $order, $charge, $vps_data, COMMERCE_PAYMENT_STATUS_FAILURE, SAGEPAY_REMOTE_STATUS_FAIL, $transaction);
$notification['status'] = 'OK';
$notification['message'] = $transaction->payload['StatusDetail'];
$notification['cancel'] = TRUE;
break;
case 'FAIL':
watchdog('commerce_sagepay', 'FAIL error from SagePay for order %order_id with message %msg', array(
'%order_id' => $order_id,
'%msg' => $transaction->payload['StatusDetail'],
), WATCHDOG_ERROR);
commerce_sagepay_transaction($payment_method, $order, $charge, $vps_data, COMMERCE_PAYMENT_STATUS_FAILURE, SAGEPAY_REMOTE_STATUS_FAIL, $transaction);
$notification['status'] = 'OK';
$notification['message'] = $transaction->payload['StatusDetail'];
$notification['cancel'] = TRUE;
break;
default:
watchdog('commerce_sagepay', 'Unknown error from SagePay for order
%order_id with message %msg', array(
'%order_id' => $order_id,
'%msg' => $transaction->payload['StatusDetail'],
), WATCHDOG_ERROR);
commerce_sagepay_transaction($payment_method, $order, $charge, $vps_data, COMMERCE_PAYMENT_STATUS_FAILURE, SAGEPAY_REMOTE_STATUS_FAIL, $transaction);
$notification['status'] = 'OK';
$notification['message'] = 'Unexpected Status code received: ' . $vps_data['Status'];
$notification['cancel'] = TRUE;
}
}
else {
$payload = $transaction->payload;
$payload['vps_status'] = 'TAMPER';
commerce_sagepay_transaction($payment_method, $order, array(), $payload, COMMERCE_PAYMENT_STATUS_FAILURE, SAGEPAY_REMOTE_STATUS_FAIL, $transaction);
$notification['status'] = 'INVALID';
$notification['message'] = t('MD5 did not match - signs of tampering
.');
}
}
else {
$payload = $transaction->payload;
$payload['vps_status'] = 'TAMPER';
commerce_sagepay_transaction($payment_method, $order, array(), $payload, COMMERCE_PAYMENT_STATUS_FAILURE, SAGEPAY_REMOTE_STATUS_FAIL, $transaction);
$notification['status'] = 'INVALID';
$notification['message'] = t('Vendor TX code did not match - signs of
tampering
.');
}
}
$eoln = chr(13) . chr(10);
if (array_key_exists('cancel', $notification)) {
$notification['redirect'] = url('checkout/' . $order_id . '/payment/back/' . $payment_key, array(
'absolute' => TRUE,
));
}
else {
$notification['redirect'] = url('checkout/' . $order_id . '/payment/return/' . $payment_key, array(
'absolute' => TRUE,
));
}
$return_notification = 'Status=' . $notification['status'] . $eoln . 'RedirectURL=' . $notification['redirect'] . $eoln . 'StatusDetail=' . $notification['message'] . $eoln;
echo $return_notification;
exit;
}