CiviCRM 4.2 Payment Extensions Framework - Overview of New Features

Published
2012-06-07 09:53
Written by

Following the 2012 London Code Sprint, a number of new features were added to the CiviCRM payment extensions framework, with a view to providing a programming interface for fully self-contained payment extensions, which can be installed and uninstalled via a single click.

Install / Uninstall

The first of these are 4 new methods which can be implemented within the payment processor class for performing basic setup / cleanup tasks, which will be called at the appropriate time:

enable()
disable()
install()
uninstall()

These can potentially be used to create and remove any additional tables your payment extension may need, or for recurring payment functionality triggered by cron (see below), the install() method serves as a good place to set that up.

NB: It is not necessary to create an entry in the civicrm_payment_processor_type table when installing, as would have been done in the past by supplying an install.sql file with your payment processor. This is now done automatically from the information in your extension's info.xml file.

Payment Notification Callbacks (IPNs)

Previously, payment notifications had been implemented by means of a standalone callback script which needed to be placed in <civicrm_root>/extern. This had long proved problematic, not only from the point of view of upgrades, and having to remember to replace the script after performing one, but also one of how to reliably bootstrap your host CMS. You can make a few educated guesses as to where you may be located in relation to it, and be right 90% of the time - the chances are you're in sites/all/modules of a Drupal installation, but you can never be 100% certain ...

To add to our problems, under the new system for payment extensions, the use of a standalone script to receive payment notifications becomes virtually impossible. Firstly, we would not want to encourage people to place their CiviCRM extensions directory (where your notification script would now need to live) in a web accessible location, in fact we would want to heavily discourage that. Secondly, a CiviCRM extensions directory can be placed anywhere on the local filing system, and not necessarily under the CMS root, so it now becomes impossible to even find CiviCRM in relation to your script, let alone any CMS it may be connected to.

So as a result of some discussions on the forum and at the code sprint, it was decided that we should solve all these problems by providing a common url through Civi to receive payment notifications, load the appropriate payment class, and invoke a method within the class to handle the request.

This can be done like so:

  1. When constructing a payment notification url, for example in doTransferCheckout(), the url is now:

    http://yoursite.com/civicrm/payment/ipn?processor_name=MyProcessor

    (replacing MyProcessor with the payment_processor_type of your payment extension, ie: whatever is contained in the <name> tag of your extension's info.xml file).

  2. Implement a handlePaymentNotification method in your payment processor class, into which is placed code to receive the payment notification (ie: code that would have previously resided in <civicrm_root>/extern/MyProcessorIPN.php), for example:

    public function handlePaymentNotification() {
        require_once 'CRM/Utils/Array.php';
        $module = CRM_Utils_Array::value('module', $_GET);
       
        // Attempt to determine component type ...
        switch ($module) {
            case 'contribute':
                // Handle contributions
                break;
            case 'event':
                // Handle events
                break;
            default:
                require_once 'CRM/Core/Error.php';
                CRM_Core_Error::debug_log_message("Could not get module name from request url");
                echo "Could not get module name from request url\r\n";
        }
        
    }
    

Recurring Payments Using the Job API and Consolidated Cron

With recurring payments, the approach of many payment gateways is one where they themselves are responsible for triggering the recurring payment. The gateway then sends you a notification when the payment completes (or fails), as is the case with Paypal. If that's the case, that is great, and makes implementing recurring payments into your payment extension a whole lot easier.

Some however, use a system where the site is responsible for running any recurring payments through at the correct time, and so to support recurring payments through this type of gateway, our payment extension needs a way of setting up cron jobs to perform this type of task (or, indeed, any other type of scheduled task it may need to perform).

Naturally, it made sense to use CiviCRM's Consolidated Cron (aka Scheduled Jobs) for this. If you're not already familiar with that, you can read all about it here.

To make things a little easier, an api function (run_payment_cron) has been defined to route the cron call through to a method in the appropriate payment processor class, so to implement a cron job for your payment processor, there are basically two things to do:

  1. Put some sort of code like the following into the install() method of your payment class. This will create a job which appears on the Scheduled Jobs pages and can be enabled by systems administrators.

    // Create entry in civicrm_job table for cron call
    CRM_Core_DAO::executeQuery("
        INSERT INTO civicrm_job (
           id, domain_id, run_frequency, last_run, name, description,
           api_prefix, api_entity, api_action, parameters, is_active
        ) VALUES (
           NULL, %1, 'Daily', NULL, 'Process Recurring Payments through MyProcessor',
           'Processes any MyProcessor recurring payments that are due',
           'civicrm_api3', 'job', 'run_payment_cron', 'processor_name=MyProcessor', 0
        )
        ", array(
            1 => array(CIVICRM_DOMAIN_ID, 'Integer')
        )
    );
    

    NB: processor_name should be the payment_processor_type of your payment extension, ie: whatever is contained in the <name> tag of your extension's info.xml file.

  2. Implement a handlePaymentCron method in your payment class containing code to trigger recurring payments, eg:

    public function handlePaymentCron() {
       // Process recurring payments here
    }

Also, for a more general overview of the process of creating a payment extension, be sure to check out this article here