Blogs  Work culture, passion and sharing ideas

You are here: 

You are here

Home  »  Blogs  »  Webform PayPal integration in Drupal 7

Webform PayPal integration in Drupal 7

If you have used Webform module in Drupal it would have occured at some point: what if it could be further extended to support payment checkout? Good news is you can since Drupal modules are closely tied to its API. Some interesting statistics on Drupal 7 API:

  • 16,779 API functions
  • 714 API constants
  • 1,734 API classes

This is a loony approximation and you don't have to storm into them at once, but while we're at it, its also essential to have basic know-how of them. They might come in very handy if you rightfully approach without hacking and cracking your way into changing some default behavior, or markup or add/modify a functionality in your Drupal site or even creating your own module.

Back to subject, for this functionality we only need to focus on Forms & Webform API for this sole purpose. There is little to do with other API's. A preface to exactly what API functions we will be hooking:

hook_menu(); // for creating drupal page handler
hook_form_alter(); // for making some changes to form markup
hook_webform_submission_insert(); // for post processing form submission
       

Some of the code you will see here is contributed by other authors which might not be credited (please feel free to leave a comment to add your credits if the code is authored by you). To make all this work it's imperative that some hooks are set inside a module. So inside your custom module begin with:

/**
* Implements hook_menu().
*/

function mymodule_menu() {
$items = array();

// registers a callback page for event my new year party
$items['my-newyear-party/thanks'] = array(
  'title' => variable_get('mymodule_thanks_title', 'My new year party'),
  'description' => 'Handles My new year party callback request from PayPal with IPN data',
  'page callback' => 'mymodule_mynewyear_thanks',
  'access callback' => TRUE,
  'type' => MENU_CALLBACK,
);

return $items;
}
       

In Drupal, hook_menu() handles every page request with a corresponding handler function. The above function tells Drupal to create a page programmatically which shows up after payment processing with PayPal. Consequently define a callback function which will handle this page request.

/**
 * Default return_path for paypal.
 *
 * @return string
 *   (x)html output
 */

function mymodule_mynewyear_thanks() {
  $message_payment_not_received = variable_get('payment_not_received_message', 'Payment was not successful!');

  // Don't even think about going any further!
  if ((empty($_POST)) & (!isset($_POST['payer_id']))) return drupal_set_message($payment_not_received, 'error');

  // Previous conditions blocks if no transaction data is found, this checks if the transaction was successful
  if (($_POST['payment_status'] == 'Completed') | ($_POST['payment_status'] == 'Created') | ($_POST['payment_status'] == 'Pending') | ($_POST['payment_status'] == 'Processed')):

  $pending_reason = ''; // Stop PHP notice!
  $custom = -0; // Stop PHP notice!

  extract($_POST); // Import variables from post array received from PayPal IPN

  $message_payment_received = "Thank you for registering. Your payment was received successfully!\n" .
    "Your payment transaction details are below:\n" .
    "Transaction ID: " . $txn_id . "\n" .
    "Item name: " . $item_name . "\n" .
    "First name: " . $first_name . "\n" .
    "Payee email: " . $payer_email . "\n" .
    "Payment type: " . $payment_type . "\n" .
    "Payment date: " . $payment_date . "\n" .
    "Payment gross: " . $payment_gross . "\n" .
    "Payment currency: " . $mc_currency . "\n" .
    "Payment status: " . $payment_status . "\n" .
    "Pending reason: " . $pending_reason . "\n";

  // Update payment status to "paid" from "unpaid" if successful
  db_update('webform_submitted_data')
  ->fields(array(
    'data' => 'paid',
  ))
  ->condition('sid', $custom, '=')
  ->condition('data', 'unpaid', '=')
  ->execute();
  return $payment_received;
  else:
  return drupal_set_message($payment_not_received, 'error');
  endif;
}
       

Unraveling the above function; it checks for successful payment by looking at IPN data received from PayPal in $_POST variable. If transaction succeeds, prints information and updates webform table to reflect paid/unpaid status. Your webform must have a hidden field which should be responsible for handling "paid" and "unpaid" status, default status is "unpaid". Now further implementation rests on hook_webform_submission_insert() function:

/**
* Implements webform_submission_insert().
*/

function mymodule_webform_submission_insert($node, $submission) {
  global $base_url;
  if ($node->nid == 30) {
    // Get mapping of form submission data using private function (see below).
    // Using this, we can use webform field names (like 'first_name') instead
    // of vague mappings like $submission->data['3']['value'][0].
    $mapping = _mymodule_webform_component_mapping($node);

  // Get vars from LM Paypal if it exists
  if (module_exists('lm_paypal')):
    $business_email = lm_paypal_api_get_business();
    $paypal_host = lm_paypal_api_get_host(TRUE) . '?';
  else:
    $business_email = 'drhuang@yahoo.com';
    $paypal_host = 'https://www.paypal.com/cgi-bin/webscr?';
  endif;

    $paypal = array();
    $paypal['cmd'] = '_xclick'; // Varies depending on type of payment sent via PayPal
  $paypal['business'] = $business_email;  // Sandbox/Live PayPal account email
    $paypal['page_style'] = 'huang_checkout'; // Set this in PayPal prefs, then change here (default = paypal)
    $paypal['amount'] = $submission->data[$mapping['paypal_amount_total']]['value'][0];
   
  $paypal['item_name'] = variable_get('mymodule_mynewyear_thanks_title', 'My new year party');
 
    $paypal['tax'] = '0'; // No tax for this payment
  $paypal['custom'] = $submission->sid; // Custom var will store the FORM submitted ID for use with IPN checkout and to change payment status to "paid"
 
  $paypal['return'] = $base_url . '/my-newyear-party/thanks'; // Page to which user is returned
  $paypal['notify_url'] = $base_url . '/my-newyear-party/thanks'; // Log the IPN data we receive from PayPal
  $paypal['cancel_return'] = $base_url . '/my-newyear-party/thanks'; // If user cancels transaction return
 
    $paypal['first_name'] = $submission->data[$mapping['your_name']]['value'][0];
    $paypal['email'] = $submission->data[$mapping['email']]['value'][0];
    $paypal['address1'] = $submission->data[$mapping['address']]['value'][0];
    $paypal['address2'] = $submission->data[$mapping['city_state__zip']]['value'][0];
    $paypal['country'] = 'US';

    // Build the URL/query for PayPal payment form.
    $query = http_build_query($paypal, '', '&');
    $url = $paypal_host . $query;

    // Redirect user to PayPal...
    drupal_goto($url);
  }
}

/**
* Function to convert webform submissions into a nicely-mapped array.
*
* @see http://www.drupalcoder.com/story/678-additional-processing-in-drupals-we...
*/

function _mymodule_webform_component_mapping($node) {
  $mapping = array();
  $components = $node->webform['components'];
  foreach ($components as $i => $component) {
    $key = $component['form_key'];
    $mapping[$key] = $i;
  }
  return $mapping;
}
       

The above function would be called after each webform submission ie. exactly after its submitted and that's where we hook into it prepare a redirect query to checkout from PayPal and return back to the "thank you" page with transaction details. You can filter out processing irrelevant forms by using $node object. Also notice usage of $paypal['custom'] variable which remembers the form ID; very important to distinguish what form we are updating in database. If you have LM Paypal module installed this function will try and fetch information for PayPal email and PayPal host if not found uses defaults.

The last function we will hook is hook_form_alter() inside your themes template.php file. This is where we can declare membership fees like:

/**
* Implementation of hook_form_alter().
*/

function mytheme_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'webform_client_form_30'):
  $i = 0;
  $membership_amt = variable_get('mynewyearparty_membership_amount', '20');
  drupal_add_js('var memberFees = '.$membership_amt.';',
    array('type' => 'inline', 'scope' => 'header', 'weight' => 5)
  );
  endif;
}
       

The final function above implements a simple javascript that is inserted on node(webform) no. 30 setting membership fees to $20 per member. As members are added the gross total gets updated. This might be very lame and amateur approach and its not the only approach you should adopt, but there can be many possibilities to implement it for a donation, event or even user signup. Finally the webform is PayPal checkout ready!

Other useful links and resources:

  1. http://drupal.org/project/webform_paypal
  2. http://drupal.org/project/webform_pay

Copyrights © 2007-2016 Raven Developers. All rights reserved.

Powered by: Drupal 7 and Foundation 4

preliminary