Integrating Off-Site Payment Gateways In Drupal Commerce
Hey Plastik Magazine readers! Let's dive into the world of Drupal Commerce and explore how to integrate off-site payment gateways. If you're building an e-commerce site with Drupal 7 and the Commerce module (Kickstart), you'll likely need to accept payments through a third-party payment gateway. This article will guide you through the process of developing a custom Commerce payment method to handle those off-site transactions. Let's get started!
Understanding Off-Site Payment Gateways
Off-site payment gateways, like PayPal, Stripe, or Authorize.net, handle the payment processing on their servers. This means your Drupal site redirects the user to the payment gateway's website to complete the transaction. Once the payment is processed, the gateway sends the user back to your site with the transaction status. This approach reduces the burden of handling sensitive financial data on your server and simplifies compliance with PCI DSS standards.
When dealing with off-site payment gateways, the initial step is to redirect the user to the payment gateway's secure environment. This redirection usually occurs after the customer confirms their order on your Drupal Commerce site. Your site sends the necessary order details to the payment gateway, such as the order total, customer information, and a return URL. The return URL is where the payment gateway will redirect the user after the payment process is complete, regardless of whether the payment was successful or not. It's crucial to ensure this redirection is seamless to maintain a positive user experience.
Next, the payment gateway takes over, presenting the customer with a secure payment form. The customer enters their payment details directly on the gateway's site, ensuring that sensitive financial information never touches your servers. This significantly reduces your PCI compliance burden, as you're not directly handling credit card data. The gateway processes the payment, handles fraud checks, and communicates the transaction status back to your Drupal Commerce site.
Finally, the payment gateway redirects the user back to your Drupal Commerce site, typically to a designated return URL. Along with the user, the gateway sends transaction details, such as the transaction ID and payment status. Your Drupal Commerce module then captures this data, updates the order status accordingly, and displays a confirmation message to the user. This entire process must be secure and reliable to ensure a smooth and trustworthy payment experience for your customers. Proper error handling is also vital to manage failed transactions and provide informative feedback to the user.
Setting Up Your Drupal Commerce Payment Method
To integrate an off-site payment gateway, you'll need to create a custom Commerce payment method module. Here’s a step-by-step guide:
1. Module Structure
Start by creating a new module in your Drupal site's sites/all/modules/custom directory (or wherever you keep your custom modules). Create the following files:
your_module.info: This file contains metadata about your module.your_module.module: This is the main module file where you'll implement the Commerce payment method.
2. Info File (your_module.info)
Add the following information to your .info file:
name = Your Payment Gateway
description = Integrates Your Payment Gateway with Drupal Commerce.
core = 7.x
dependencies[] = commerce
dependencies[] = commerce_payment
Make sure to replace Your Payment Gateway with the actual name of your payment gateway.
3. Module File (your_module.module)
This is where the magic happens. You'll implement the hook_commerce_payment_method_info() to define your payment method.
<?php
/**
* Implements hook_commerce_payment_method_info().
*/
function your_module_commerce_payment_method_info() {
$payment_methods = array();
$payment_methods['your_gateway'] = array(
'base' => 'your_gateway_payment_method',
'title' => t('Your Payment Gateway'),
'description' => t('Accept payments via Your Payment Gateway.'),
'terminal' => FALSE, // Set to FALSE for off-site gateways
);
return $payment_methods;
}
/**
* Payment method callback.
*/
function your_gateway_payment_method_settings_form($settings) {
$form = array();
$form['api_key'] = array(
'#type' => 'textfield',
'#title' => t('API Key'),
'#description' => t('Enter your API Key provided by Your Payment Gateway.'),
'#default_value' => isset($settings['api_key']) ? $settings['api_key'] : '',
'#required' => TRUE,
);
$form['secret_key'] = array(
'#type' => 'textfield',
'#title' => t('Secret Key'),
'#description' => t('Enter your Secret Key provided by Your Payment Gateway.'),
'#default_value' => isset($settings['secret_key']) ? $settings['secret_key'] : '',
'#required' => TRUE,
);
return $form;
}
function your_gateway_payment_method_submit_form($pane_form, $order, $payment_method, $form_values) {
$form = array();
return $form;
}
function your_gateway_payment_method_redirect_form($form, &$form_state, $order, $payment_method) {
// Get the order total.
$wrapper = entity_metadata_wrapper('commerce_order', $order);
$amount = commerce_currency_amount_to_decimal($wrapper->commerce_order_total->value()['amount'], $wrapper->commerce_order_total->value()['currency_code']);
// Get the currency code.
$currency_code = $wrapper->commerce_order_total->value()['currency_code'];
$payment_details = array(
'amount' => $amount,
'currency_code' => $currency_code,
'order_id' => $order->order_id,
'return_url' => url('payment/your_gateway/callback', array('absolute' => TRUE)),
);
// Build the form that will redirect the user to Your Payment Gateway.
$form['#action'] = 'https://yourpaymentgateway.com/payment'; // Replace with the actual payment gateway URL
$form['#method'] = 'POST';
$form['amount'] = array(
'#type' => 'hidden',
'#value' => $payment_details['amount'],
);
$form['currency'] = array(
'#type' => 'hidden',
'#value' => $payment_details['currency_code'],
);
$form['order_id'] = array(
'#type' => 'hidden',
'#value' => $payment_details['order_id'],
);
$form['return_url'] = array(
'#type' => 'hidden',
'#value' => $payment_details['return_url'],
);
// Add a submit button to automatically submit the form.
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Redirecting to Your Payment Gateway...'),
);
// Automatically submit the form using JavaScript.
drupal_add_js('jQuery(document).ready(function() { jQuery(\'#commerce-payment-redirect-form\').submit(); });', 'inline');
return $form;
}
/**
* Implements hook_menu().
*/
function your_module_menu() {
$items = array();
$items['payment/your_gateway/callback'] = array(
'title' => 'Your Gateway Callback',
'page callback' => 'your_gateway_payment_callback',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Payment gateway callback function.
*/
function your_gateway_payment_callback() {
// Get the transaction details from the payment gateway.
$transaction_id = $_POST['transaction_id'];
$payment_status = $_POST['payment_status'];
$order_id = $_POST['order_id'];
// Load the order.
$order = commerce_order_load($order_id);
if ($order) {
if ($payment_status == 'success') {
// Create a transaction.
$transaction = commerce_payment_transaction_new('your_gateway', $order->order_id);
$transaction->instance_id = 'your_gateway';
$transaction->amount = $order->commerce_order_total['und'][0]['amount'];
$transaction->currency_code = $order->commerce_order_total['und'][0]['currency_code'];
$transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
$transaction->message = t('Payment received successfully.');
$transaction->transaction_id = $transaction_id;
commerce_payment_transaction_save($transaction);
// Update the order status.
commerce_order_status_update($order, 'completed');
drupal_set_message(t('Thank you for your order! Your payment was successful.'));
} else {
// Create a failed transaction.
$transaction = commerce_payment_transaction_new('your_gateway', $order->order_id);
$transaction->instance_id = 'your_gateway';
$transaction->amount = $order->commerce_order_total['und'][0]['amount'];
$transaction->currency_code = $order->commerce_order_total['und'][0]['currency_code'];
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
$transaction->message = t('Payment failed.');
$transaction->transaction_id = $transaction_id;
commerce_payment_transaction_save($transaction);
// Update the order status.
commerce_order_status_update($order, 'pending');
drupal_set_message(t('Your payment failed. Please try again.'));
}
} else {
drupal_set_message(t('Order not found.'), 'error');
}
// Redirect the user to the order view page.
drupal_goto('checkout/' . $order->order_id . '/complete');
}
4. Explanation of the Code
hook_commerce_payment_method_info(): Defines the payment method, its title, and whether it's a terminal (on-site) or off-site gateway.your_gateway_payment_method_settings_form(): Creates a settings form for the payment method where you can add fields like API keys, secret keys, etc.your_gateway_payment_method_redirect_form(): Builds the form that redirects the user to the payment gateway. It includes hidden fields for the amount, currency, order ID, and return URL.hook_menu(): Defines a menu item for the payment gateway callback URL.your_gateway_payment_callback(): Handles the callback from the payment gateway. It retrieves the transaction details, updates the order status, and creates a payment transaction.
5. Enabling the Module
Enable your module in the Drupal admin interface (admin/modules).
6. Configuring the Payment Method
Go to admin/commerce/config/payment-methods and enable your payment method. Configure the settings as needed.
Handling the Payment Gateway Callback
The your_gateway_payment_callback() function is crucial for handling the response from the payment gateway. It retrieves the transaction details (typically sent via POST), verifies the payment status, and updates the Drupal Commerce order accordingly. This function also creates a Commerce payment transaction to record the payment in Drupal Commerce.
Security Considerations
- Validate Data: Always validate the data received from the payment gateway to prevent malicious attacks.
- Use HTTPS: Ensure your site uses HTTPS to encrypt the communication between your site and the payment gateway.
- Secure API Keys: Store API keys and secret keys securely and never expose them in client-side code.
Error Handling
Implement robust error handling to manage failed transactions gracefully. Provide informative error messages to the user and log errors for debugging purposes.
Enhancing the User Experience
- Clear Communication: Keep the user informed about the payment process. Display messages like