commerce_sermepa.module in Commerce sermepa 7
Provides a payment method for Drupal Commerce using Sermepa/Redsys gateway.
File
commerce_sermepa.moduleView source
<?php
/**
* @file
* Provides a payment method for Drupal Commerce using Sermepa/Redsys gateway.
*/
use CommerceRedsys\Payment\Sermepa;
/**
* Implements hook_hook_info().
*/
function commerce_sermepa_hook_info() {
$hooks = array(
'commerce_sermepa_gateway' => array(
'group' => 'commerce_sermepa',
),
);
return $hooks;
}
/**
* Implements hook_libraries_info().
*/
function commerce_sermepa_libraries_info() {
$libraries['sermepa'] = array(
'name' => 'Sermepa/Redsys API',
'vendor url' => 'https://github.com/CommerceRedsys/sermepa',
'download url' => 'https://github.com/CommerceRedsys/sermepa/releases',
'version arguments' => array(
'file' => 'CHANGELOG.md',
'pattern' => '@([0-9.]+)@',
'lines' => 1,
'cols' => 20,
),
'files' => array(
'php' => array(
'src/SermepaException.php',
'src/SermepaInterface.php',
'src/Sermepa.php',
),
),
);
return $libraries;
}
/**
* Helper function to create a Sermepa instance.
*
* @param array $settings
* An array of the current payment method settings.
*
* @return mixed
* Initialized \CommerceRedsys\Payment\Sermepa Object, otherwise FALSE.
*/
function commerce_sermepa_library_initialize($settings) {
// Load Sermepa API.
if (!class_exists('Sermepa')) {
$library = libraries_load('sermepa');
if (!$library || empty($library['loaded'])) {
return FALSE;
}
}
// Create a Sermepa instance.
if (!empty($settings['advanced']['override_url'])) {
$environment = $settings['advanced']['override_url'];
}
else {
$environment = $settings['mode'];
}
$gateway = new Sermepa($settings['Ds_MerchantName'], $settings['Ds_MerchantCode'], $settings['Ds_Merchant_Terminal'], $settings['Ds_MerchantPassword'], $environment);
return $gateway;
}
/**
* Returns an array of Sermepa payment method icon img elements.
*
* @return array
* The array of themed payment method icons keyed by name: visa, mastercard,
* maestro.
*/
function commerce_sermepa_get_icons() {
$icons = array();
$payment_methods = array(
'mastercard' => t('MasterCard'),
'visa' => t('Visa'),
'maestro' => t('Maestro'),
'sermepa_redsys' => t('Redsýs'),
);
foreach ($payment_methods as $name => $title) {
$variables = array(
'path' => drupal_get_path('module', 'commerce_sermepa') . '/images/' . $name . '.png',
'title' => $title,
'alt' => $title,
'attributes' => array(
'class' => array(
'commerce-sermepa-icon',
),
),
);
$icons[$name] = theme('image', $variables);
}
return $icons;
}
/**
* Implements hook_menu().
*/
function commerce_sermepa_menu() {
$items['sermepa/callback/%commerce_order'] = array(
'page callback' => 'commerce_sermepa_callback',
'page arguments' => array(
2,
),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Get POST response from sermepa.
*/
function commerce_sermepa_callback($order) {
if (!$order) {
watchdog('commerce_sermepa', "Bad '%order_id' order id received in feedback values.", array(
'%order_id' => arg(2),
), WATCHDOG_WARNING);
return FALSE;
}
// Load the payment method.
$payment_method = commerce_payment_method_instance_load($order->data['payment_method']);
if (!$payment_method || $payment_method['method_id'] != 'commerce_sermepa') {
watchdog('commerce_sermepa', "Unknown or non-existent '%method_id' payment method.", array(
'%method_id' => 'commerce_sermepa',
), WATCHDOG_WARNING);
return FALSE;
}
// Create a sermepa instance.
if (!($gateway = commerce_sermepa_library_initialize($payment_method['settings']))) {
return FALSE;
}
// Get response data.
if (!($feedback = $gateway
->getFeedback())) {
watchdog('commerce_sermepa', "Bad feedback response data.", array(), WATCHDOG_WARNING);
return FALSE;
}
// Get order number from feedback data and compare it with the order object
// argument.
$parameters = $gateway
->decodeMerchantParameters($feedback['Ds_MerchantParameters']);
$order_id = $parameters['Ds_MerchantData'];
if ($order_id != $order->order_id) {
watchdog('commerce_sermepa', "The order id from feedback and the order object argument id don't match.", array(), WATCHDOG_WARNING);
}
// Validate feedback values.
if (!$gateway
->validSignatures($feedback)) {
watchdog('commerce_sermepa', "Bad feedback response, signatures don't match.", array(), WATCHDOG_WARNING);
return FALSE;
}
// Process the transaction.
commerce_sermepa_process_transaction($order, $payment_method, $parameters);
return FALSE;
}
/**
* Implements hook_commerce_payment_method_info().
*/
function commerce_sermepa_commerce_payment_method_info() {
$payment_methods = array();
// Get sermepa and card icons.
$icons = commerce_sermepa_get_icons();
$display_title = t('!logo - pay with credit or debit card', array(
'!logo' => $icons['sermepa_redsys'],
));
// Remove sermepa icon logo.
unset($icons['sermepa_redsys']);
$display_title .= '<div class="commerce-sermepa-icons"><span class="label">' . t('Includes:') . '</span>' . implode(' ', $icons) . '</div>';
$payment_methods['commerce_sermepa'] = array(
'base' => 'commerce_sermepa',
'title' => t('Sermepa Payment'),
'display_title' => $display_title,
'short_title' => t('Sermepa'),
'description' => t('Sermepa/Redsýs payment gateway integration'),
'terminal' => TRUE,
'offsite' => TRUE,
'offsite_autoredirect' => TRUE,
'active' => TRUE,
);
return $payment_methods;
}
/**
* Payment method callback: settings form.
*
* @return array
* Form elements for the payment method's settings form included
* as part of the payment method's enabling action in Rules.
*/
function commerce_sermepa_settings_form($settings = NULL) {
libraries_load('sermepa');
$form = array();
$settings = (array) $settings + array(
'mode' => 'test',
'Ds_MerchantName' => '',
'Ds_MerchantCode' => '',
'Ds_MerchantPassword' => '',
'Ds_Merchant_Terminal' => '',
'Ds_Merchant_PayMethods' => array(
'C',
),
'Ds_Merchant_ConsumerLanguage' => '001',
'currency' => '978',
'advanced' => array(
'override_url' => '',
),
);
$form['mode'] = array(
'#type' => 'radios',
'#title' => t('Mode of the transactions'),
'#default_value' => $settings['mode'],
'#options' => array(
'test' => t('Test'),
'live' => t('Live'),
),
'#required' => TRUE,
);
$form['Ds_MerchantName'] = array(
'#type' => 'textfield',
'#title' => t('Merchant Name'),
'#default_value' => $settings['Ds_MerchantName'],
'#size' => 80,
'#maxlength' => Sermepa::getMerchantNameMaxLength(),
'#required' => TRUE,
);
$form['Ds_MerchantCode'] = array(
'#type' => 'textfield',
'#title' => t('Merchant Code'),
'#default_value' => $settings['Ds_MerchantCode'],
'#size' => 80,
'#maxlength' => Sermepa::getMerchantCodeMaxLength(),
'#required' => TRUE,
);
$form['Ds_MerchantPassword'] = array(
'#type' => 'textfield',
'#title' => t('SHA256 Merchant Password'),
'#default_value' => $settings['Ds_MerchantPassword'],
'#size' => 80,
'#maxlength' => Sermepa::getMerchantPasswordMaxLength(),
'#required' => TRUE,
);
$form['Ds_Merchant_Terminal'] = array(
'#type' => 'textfield',
'#title' => t('Merchant Terminal'),
'#default_value' => $settings['Ds_Merchant_Terminal'],
'#size' => 5,
'#maxlength' => Sermepa::getMerchantTerminalMaxLength(),
'#required' => TRUE,
);
$form['Ds_Merchant_PayMethods'] = array(
'#type' => 'select',
'#multiple' => TRUE,
'#title' => t('Merchant Consumer Language'),
'#default_value' => $settings['Ds_Merchant_PayMethods'],
'#options' => Sermepa::getAvailablePaymentMethods(),
'#required' => FALSE,
);
$form['Ds_Merchant_ConsumerLanguage'] = array(
'#type' => 'select',
'#title' => t('Merchant Consumer Language'),
'#default_value' => $settings['Ds_Merchant_ConsumerLanguage'],
'#options' => Sermepa::getAvailableConsumerLanguages(),
'#required' => TRUE,
);
$form['currency'] = array(
'#type' => 'select',
'#title' => t('Currency'),
'#default_value' => $settings['currency'],
'#options' => Sermepa::getAvailableCurrencies(),
'#required' => TRUE,
);
$form['description'] = array(
'#type' => 'textarea',
'#title' => t('Payment instructions'),
'#description' => t('Instructions for customers on the checkout page. Use <br /> for line break.'),
'#default_value' => isset($settings['description']) ? $settings['description'] : '',
'#required' => FALSE,
'#rows' => 3,
);
$form['advanced'] = array(
'#type' => 'fieldset',
'#title' => t('Advanced options'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['advanced']['override_url'] = array(
'#type' => 'textfield',
'#title' => t('Override bank connect url'),
'#default_value' => $settings['advanced']['override_url'],
'#size' => 80,
'#maxlength' => 255,
'#element_validate' => array(
'commerce_sermepa_settings_override_url_validate',
),
);
return $form;
}
/**
* Form element validation handler for override bank connect url.
*/
function commerce_sermepa_settings_override_url_validate($element, &$form_state, $form) {
if (!empty($element['#value']) && !filter_var($element['#value'], FILTER_VALIDATE_URL)) {
form_error($element, t('The specified "Override bank connect url" is not valid.'));
}
}
/**
* Payment method callback: redirect form to sermepa gateway.
*
* @param commerce_order $order
* The fully loaded order being paid for.
* @param array $payment_method
* An array of the current settings.
*
* @return array
* Form elements that should be submitted to the redirected
* payment service.
*/
function commerce_sermepa_redirect_form($form, &$form_state, $order, $payment_method) {
// Return an error if the enabling action's settings haven't been configured.
if (empty($payment_method['settings']['Ds_MerchantCode'])) {
drupal_set_message(t('Sermepa is not configured for use. Merchant code has not been specified.'), 'error');
return array();
}
if (empty($payment_method['settings']['Ds_MerchantPassword'])) {
drupal_set_message(t('SHA256 Sermepa password is not set for use. Merchant password has not been specified.'), 'error');
return array();
}
if (empty($payment_method['settings']['Ds_Merchant_Terminal'])) {
drupal_set_message(t('Sermepa is not configured for use. Merchant terminal has not been specified.'), 'error');
return array();
}
$settings = array(
// Return to the previous page when payment is canceled.
'cancel_return' => url('checkout/' . $order->order_id . '/payment/back/' . $order->data['payment_redirect_key'], array(
'absolute' => TRUE,
)),
// Return to the payment redirect page for processing successful payments.
'return' => url('checkout/' . $order->order_id . '/payment/return/' . $order->data['payment_redirect_key'], array(
'absolute' => TRUE,
)),
// Url to get POST result of payment.
'merchant_url' => url('sermepa/callback/' . $order->order_id, array(
'absolute' => TRUE,
)),
// Specify the current payment method instance ID in the notify_url.
'payment_method' => $payment_method['instance_id'],
);
return commerce_sermepa_order_form($form, $form_state, $order, $payment_method['settings'] + $settings);
}
/**
* Build the order form for the sermepa.
*
* @param commerce_order $order
* The fully loaded order being paid for.
* @param array $settings
* An array of the current settings.
*
* @return array
* Form elements that should be submitted to the redirected
* payment service.
*/
function commerce_sermepa_order_form($form, &$form_state, $order, $settings) {
// Create a sermepa instance.
if (!($gateway = commerce_sermepa_library_initialize($settings))) {
return FALSE;
}
// Prepare transaction data.
// Set Authorization trasaction type.
// See \CommerceRedsys\Payment\Sermepa::getAvailableTransactionTypes() for full list.
$gateway
->setTransactionType('0')
->setOrder(substr(date('ymdHis') . $order->order_id, -12, 12))
->setAmount($order->commerce_order_total[LANGUAGE_NONE][0]['amount'])
->setCurrency($settings['currency'])
->setMerchantURL($settings['merchant_url'])
->setUrlOK($settings['return'])
->setUrlKO($settings['cancel_return'])
->setConsumerLanguage($settings['Ds_Merchant_ConsumerLanguage'])
->setMerchantData($order->order_id);
// Set payment methods.
if (!empty($settings['Ds_Merchant_PayMethods'])) {
$gateway
->setPaymentMethod(implode('', $settings['Ds_Merchant_PayMethods']));
}
// Allow user to make changes in the gateway configuration data.
rules_invoke_all('commerce_sermepa_gateway', $gateway, $order);
$form['#action'] = $gateway
->getEnvironment();
// Force rebuild the form.
$form_sate['rebuild'] = TRUE;
// Create hidden fields.
$parameters = $gateway
->composeMerchantParameters();
if ($parameters) {
$form['Ds_SignatureVersion'] = array(
'#type' => 'hidden',
'#value' => $gateway
->getSignatureVersion(),
);
$form['Ds_MerchantParameters'] = array(
'#type' => 'hidden',
'#value' => $parameters,
);
$form['Ds_Signature'] = array(
'#type' => 'hidden',
'#value' => $gateway
->composeMerchantSignature(),
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Redirect to Redsys platform'),
);
return $form;
}
/**
* Implements hook_redirect_form_validate().
*
* @param commerce_order $order
* The order object.
* @param array $payment_method
* The payment method array.
*
* @return boolean
* TRUE if the customer should proceed to checkout completion or FALSE to go
* back one step in the checkout process.
*/
function commerce_sermepa_redirect_form_validate($order, $payment_method) {
commerce_sermepa_process_callback($order, $payment_method);
}
/**
* Process callback information from Sermepa.
*
* This can either be through a redirect after payment,
* or a Direct HTTP server-to-server request.
*
* @param commerce_order $order
* The order object.
* @param array $payment_method
* The payment method array.
*
* @return boolean
* TRUE if is valid, otherwise FALSE.
*/
function commerce_sermepa_process_callback($order, $payment_method) {
// Create a sermepa instance.
if (!($gateway = commerce_sermepa_library_initialize($payment_method['settings']))) {
return FALSE;
}
// Get response data.
if (!($feedback = $gateway
->getFeedback())) {
watchdog('commerce_sermepa', "Bad feedback response data.", array(), WATCHDOG_WARNING);
return FALSE;
}
// Validate feedback values.
if (!$gateway
->validSignatures($feedback)) {
watchdog('commerce_sermepa', "Bad feedback response, signatures don't match.", array(), WATCHDOG_WARNING);
return FALSE;
}
$parameters = $gateway
->decodeMerchantParameters($feedback['Ds_MerchantParameters']);
commerce_sermepa_process_transaction($order, $payment_method, $parameters);
return TRUE;
}
/**
* Get transaction with a specific Sermepa Ds_AuthorisationCode.
*
* @param string $authorisation_code
* The authorisation code received from Sermepa regarding the payment.
*/
function commerce_sermepa_get_payment_transaction($authorisation_code) {
$query = new EntityFieldQuery();
$result = $query
->entityCondition('entity_type', 'commerce_payment_transaction')
->propertyCondition('payment_method', 'commerce_sermepa')
->propertyCondition('remote_id', $authorisation_code)
->execute();
if (!empty($result['commerce_payment_transaction']) && count($result['commerce_payment_transaction']) > 0) {
$transaction = array_pop($result['commerce_payment_transaction']);
return $transaction->transaction_id;
}
return FALSE;
}
/**
* Save the payment transaction for the order.
*
* @param commerce_order $order
* The loaded order that is being processed.
* @param array $payment_method
* The payment method settings.
* @param array $feedback_parameters
* An associative array of feedback merchant parameters values:
* - Ds_Date: Transaction date (dd/mm/yyyy).
* - Ds_Hour: Transaction time (HH:mm).
* - Ds_Amount: Same of the transaction.
* - Ds_Currency: Same of the transaction.
* - Ds_Order: Same of the transaction.
* - Ds_MerchantCode: Same of the transaction.
* - Ds_Terminal: Assigned by Sermepa.
* - Ds_Response: Response values, see $this->handleResponse.
* - Ds_MerchantData: Optional sended from commerce form.
* - Ds_SecurePayment: 0 for no secure payment, 1 for secure.
* - Ds_TrasactionType: Trasaction type sended from commerce form.
* - Ds_Card_Country: (Optional) Country of issuance of the card that has
* tried to make the payment..
* - Ds_AuthorisationCode: (Optional) Assigned authorisation code.
* - Ds_ConsumerLanguage: (Optional) 0 indicates that has not been
* determined the customer's language..
* - Ds_Card_Type: (Optional) C for credit, D for debit.
* @param boolean $redirect
* Specifies whether to call redirect functions or not.
*/
function commerce_sermepa_process_transaction($order, $payment_method, $feedback_parameters, $redirect = TRUE) {
$authorisation_code = $feedback_parameters['Ds_AuthorisationCode'];
$transaction_id = commerce_sermepa_get_payment_transaction($authorisation_code);
if (!$transaction_id) {
$transaction = commerce_payment_transaction_new('commerce_sermepa', $order->order_id);
}
else {
$transaction = commerce_payment_transaction_load($transaction_id);
}
// Handle the response of the bank.
if (!class_exists('Sermepa')) {
libraries_load('sermepa');
}
$response_code = $feedback_parameters['Ds_Response'];
$transaction_status = array(
'code' => $response_code <= 99 ? COMMERCE_PAYMENT_STATUS_SUCCESS : COMMERCE_PAYMENT_STATUS_FAILURE,
'message' => Sermepa::handleResponse($response_code),
);
$transaction->instance_id = $payment_method['instance_id'];
$transaction->amount = $order->commerce_order_total[LANGUAGE_NONE][0]['amount'];
$transaction->currency_code = $order->commerce_order_total[LANGUAGE_NONE][0]['currency_code'];
$transaction->remote_id = $authorisation_code;
$transaction->remote_status = $feedback_parameters['Ds_Response'];
$transaction->status = $transaction_status['code'];
if ($transaction_status['code'] == COMMERCE_PAYMENT_STATUS_SUCCESS) {
$transaction->message = 'Transaction accepted with id @transaction_id';
}
elseif ($transaction_status['code'] == COMMERCE_PAYMENT_STATUS_FAILURE) {
$transaction->message = 'Error for the transaction with id @transaction_id: ' . $transaction_status['message'];
}
$transaction->message_variables = array(
'@transaction_id' => $authorisation_code,
);
commerce_payment_transaction_save($transaction);
if ($redirect) {
if ($transaction_status['code'] == COMMERCE_PAYMENT_STATUS_FAILURE) {
commerce_payment_redirect_pane_previous_page($order);
}
else {
commerce_payment_redirect_pane_next_page($order);
}
}
}
/**
* Payment method callback: checkout form.
*
* @param array $payment_method
* An array of the current settings.
* @param array $pane_values
* The current values of the pane.
* @param array $checkout_pane
* The checkout pane array. The checkout pane will be NULL if the payment is
* being added through the administration form.
* @param commerce_order $order
* The order object.
*
* @return array
* A form snippet for the checkout pane.
*/
function commerce_sermepa_submit_form($payment_method, $pane_values, $checkout_pane, $order) {
$form = array();
if (!empty($payment_method['settings']['description'])) {
$form['sermepa_description'] = array(
'#type' => 'item',
'#title' => t('Payment instructions'),
'#markup' => '<p class="sermepa-description">' . $payment_method['settings']['description'] . '</p>',
);
}
if (is_null($checkout_pane)) {
// The authorisationCode must be required as it is used to check if the
// transaction already exists.
// @see commerce_sermepa_process_transaction();
// @see commerce_sermepa_get_payment_transaction();
$form['Ds_AuthorisationCode'] = array(
'#type' => 'textfield',
'#title' => t('Ds_AuthorisationCode'),
'#description' => t('Assigned authorisation code.'),
'#required' => TRUE,
);
}
return $form;
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function commerce_sermepa_form_commerce_checkout_form_alter(&$form, &$form_state) {
// If this checkout form contains the payment method radios...
if (!empty($form['commerce_payment']['payment_method']['#options'])) {
// Loop over its options array looking for a Commerce Sermepa options.
foreach (array_keys($form['commerce_payment']['payment_method']['#options']) as $key) {
list($method_id, $rule_name) = explode('|', $key);
// If we find Commerce Sermepa, include its CSS on the form and exit the
// loop.
if ($method_id == 'commerce_sermepa') {
$form['commerce_payment']['payment_method']['#attached']['css'][] = drupal_get_path('module', 'commerce_sermepa') . '/theme/commerce_sermepa.theme.css';
break;
}
}
}
}
/**
* Payment method callback: submit form submission.
*/
function commerce_sermepa_submit_form_submit($payment_method, $pane_form, $pane_values, $order, $charge) {
// We need to discern if the payment is being added through the administration
// form to create the transaction. We haven't found a better way than checking
// that the form element "Ds_AuthorisationCode" is present.
if (!empty($pane_form['Ds_AuthorisationCode'])) {
commerce_sermepa_process_transaction($order, $payment_method, $pane_values);
}
}
Functions
Name![]() |
Description |
---|---|
commerce_sermepa_callback | Get POST response from sermepa. |
commerce_sermepa_commerce_payment_method_info | Implements hook_commerce_payment_method_info(). |
commerce_sermepa_form_commerce_checkout_form_alter | Implements hook_form_FORM_ID_alter(). |
commerce_sermepa_get_icons | Returns an array of Sermepa payment method icon img elements. |
commerce_sermepa_get_payment_transaction | Get transaction with a specific Sermepa Ds_AuthorisationCode. |
commerce_sermepa_hook_info | Implements hook_hook_info(). |
commerce_sermepa_libraries_info | Implements hook_libraries_info(). |
commerce_sermepa_library_initialize | Helper function to create a Sermepa instance. |
commerce_sermepa_menu | Implements hook_menu(). |
commerce_sermepa_order_form | Build the order form for the sermepa. |
commerce_sermepa_process_callback | Process callback information from Sermepa. |
commerce_sermepa_process_transaction | Save the payment transaction for the order. |
commerce_sermepa_redirect_form | Payment method callback: redirect form to sermepa gateway. |
commerce_sermepa_redirect_form_validate | Implements hook_redirect_form_validate(). |
commerce_sermepa_settings_form | Payment method callback: settings form. |
commerce_sermepa_settings_override_url_validate | Form element validation handler for override bank connect url. |
commerce_sermepa_submit_form | Payment method callback: checkout form. |
commerce_sermepa_submit_form_submit | Payment method callback: submit form submission. |