CiviCRM Developer Guide


CiviCRM has a plugin architecture for integration of payment processors/gateways. The core team has built plugins for PayPal Pro, PayPal Express, PayPal Web Payments Standard with IPN, and Google Checkout. JMA Consulting (Joe Murray) sponsored Alan Dixon to build and contribute a plugin for Moneris. Marshall Newrock for Ideal Solutions contributed a plugin for Eileen and Lucas collaborated to contribute a plugin for DPS PaymentExpress and also wrote this document. Eileen wrote the Payment processors for FirstData, Elavon and PayflowPro - which are in the core as of v 3.1. Peter Barwell wrote eWay. Circle Interactive wrote the processors for WorldPay and SagePay. 

How to Write a Payment Processor

Understand the processor you are setting up

You need to check your processor's documentation and understand the flow of the processor's process and compare it to existing processors.

Factors you should take note of are:

  • Does your processor require the user to interact directly with forms/pages on their servers (like PayPal Express), or are the data fields collected by the CiviContrib form and submitted "behind the scenes"?
  • What fields are required?
  • What authentication method(s) are used between Civi and the payment processor servers?
  • What format is data submitted in(soap/xml, arrays...)?
  • What are the steps in completing a transaction (e.g. simple POST and response, vs multi-step sequence...). Are transaction results returned real-time (PayPal Pro/Express and I think Moneris) - or posted back in a separate process (PayPal Standard w/ IPN)?

Note that none of the plugins distributed with CiviCRM use a model where the donor's credit card info is stored in the CiviCRM site's database. For PayPal Pro, and PayJunction - Credit card numbers, exp date and security codes are entered on the CiviCRM contribution page and immediately passed to the processor / not saved. For PayPal Std, Google Checkout - the info is entered at the processors' site.

Determine what 'type' of processor you are dealing with.

Figuring this out will set you on the right track. In CiviCRM there are three 'billing types':

form - a form is where the credit card information is collected on a form on your site and submitted to the payment processor

button -  buttons rely on important information (success, variables etc) being communicated directly between your server and the payment processor. (E.g. in the paypal express method, the customer is transferred to the server to enter their details but the transaction is not pushed through until an html request is sent from your server to the processor and the server replies with the response. The server also uses html to query certain variables from the server. CURL is used for this.) The user's session remains intact but I'm not sure if session variables or variables sent from the payment processor are used to identify the transaction and customise what the user sees 

notify - the notify method deals with a situation where there is not a direct two way communication between your server and the processor and there is a need for your server to identify which transaction is being responded to. Some processors may have more than one confirmation: e.g. one html GET via the user's browser and a later html GET from the payment processor server. If the user's browser never returns the processor needs to be able to figure out which transaction is involved & to complete it. If the GET is from the user's browser it needs to do the same thing but also redirect the user appropriately. 

Add the processor into the processor_types table

Add a line to `civicrm_payment_processor_type` - eg. using php myadmin or mysql

Fields are:

ID   - unique ID
DomainID - generally 1
Name - this name needs to be used as the name for the various php files as well.
Description (optional field)
Isactive  - boolean
Isdefault - boolean
user_name_label - this is what the username field is described as in the configure payment instruments screen
password_label - likewise
class_name - name of class (in code) should be Payment_xxx where xxx is the same as the name field
various self-explanatory url fields & then
BillingID - number from 1-4 - this is the important one - will add more later
Isrecurr - boolean

The BillingID will determine the process followed. ID types are:

1 = form (onsite processing with SSL set up)

3 = button (e.g. PayPal Express)

4 = notify (redirect to offsite processing)

Store any function files from your payment processor

Create an appropriately named folder in the 'packages' directory for any files provided by your payment processor which have functions you need.

Create files in the components for your processor

There needs to be a file for your processor in CRM\Event\Payment and in CRM\Contribute\Payment. The name needs to be the same as the name you inserted into the processor_types table. These are pretty easy to copy from others as they don't have much in them.

Write your processor 

OK, the groundwork is laid but writing the processor is the hard bit.

Depending on your billing mode there are different considerations - I have less information and it is less checked on the first two. The file will live in CRM\Core\Payment and have the same name as entered into your processor_type table.

Test your processor

Some suggestions of what you might test are here

Examples of processor code

Form Mode 

The function called by this billing mode is


If you return to the calling class at the end of the function the contribution will be confirmed. Values from the $params array will be updated based on what you return.  If the transaction does not succeed you should return an error to avoid confirming the transaction.

The params available to doDirectPayment() are: -

qfKey -
email-(bltID) -
billing_first_name (=first_name)
billing_middle_name (=middle_name)
- billing_last_name (=last_name)
- location_name-(bltID) = billing_first_name + billing_middle_name + billing_last_name
- streeet_address
- city-(bltID)
- state_province_id-(bltID) (int)
- state_province-(bltID) (XX)
- postal_code-(bltID)
- country_id-(bltID) (int)
- country-(bltID) (XX)
- credit_card_number
- cvv2 - credit_card_exp_date - M - Y
- credit_card_type
- amount
- amount_other
- year (credit_card_exp_date => Y)
- month (credit_card_exp_date => M)
- ip_address
- amount_level
- currencyID (XXX)
- payment_action
- invoiceID (hex number. hash?)

bltID = Billing Location Type ID. This is not actually seen by the payment class.

Button Mode

the function called by this billing mode is


The customer is returned to confirm.php with the rfp value set to 1 and


is called when the form is processed


is called to finalise the payment - a result is returned to the CiviCRM site.

 Notify mode

The function called is


The details from here are processor specific but you want to pass enough details back to your function to identify the transaction. You should be aiming to have these variables to passthrough the processor to the confirmation routine:

membershipID(contribution only)
participantID (event only)
eventID (event only)
component (event or contribute)

Handling the return can be the tricky part.

In order to keep the return url short (because paymentexpress appends a long hex string) our return url goes to a file (in the extern folder) which redirects through to the 'main' routine in paymentExpressIPN.php (IPN = instant payment notification).  

note - you need to  re-initialise the environment to get civi functions to work

require_once '../civicrm.config.php';
require_once 'CRM/Core/Config.php';
$config =& CRM_Core_Config::singleton();
 An appropriate structure for the return routine file is:
function newOrderNotify( $success, $privateData, $component,$amount,$transactionReference ) {
        $ids = $input = $params = array( );

this version in the paymentexpress file is not processor specific - pass it the variables above and it will complete the transaction. Success is boolean, the private data array holds, the component is (lower case) 'event' or 'contribute' , amount is obvious, transaction reference is any processor related reference.

contactID, contributionID, contributionTypeID,invoiceID, membershipID(contribution only), participantID (event only), eventID (event only)

static function getContext( $privateData, $orderNo)

generic function - taken from google version - retrieves information to complete transaction (required?)

private data as above

orderno - transactionreference is OK

Static function main (blah, blah,  blah)

this function is processor specific - it  converts whatever form your processor response is into the variables required for the above function and if necessary redirects the browser using

CRM_Utils_System::redirect( $finalURL );

Testing Payment Processor Plugins 

CiviCRM Payment Processor Test Spec

Here's some suggestions of what you might test once you have written your payment processor plug in.

Don't forget that you need to search specifically for TEST transactions

i.e. from this page /civicrm/contribute/search&reset=1 chose 'find test transactions'

Std Payment processor tests

1) Can process Successful transaction from

            - Event

            - Contribute Form

            - Individual Contact Record

Transaction should show as confirmed in CiviCRM and on the payment processor

2) Can include , . & = ' " in address and name fields without problems. Overlong ZIP code is handled

3) Can process a failed transaction from a Contribute form

Can fix up details & resubmit for a successful transaction

e-mail address is successfully passed through to payment processor and payment processor sends e-mails if configured to do so.

The invoice ID is processed and maintained in an adequate manner

7) Any result references and transaction codes are stored in an adequate manner

Recurring Payment Processor tests

NB - IN Paypal Manager the recurring billing profiles are in Service Settings/Recurring Billing/ Manage Profiles

1) Process a recurring contribution. Check

            - wording on confirm page is acceptable

            - wording on thankyou pages is acceptable

             - wording on any confirmation e-mails is acceptable

           - the payment processor shows the original transaction is successful

            - the payment processor shows the correct date for the next transaction

            - the payment processor shows the correct total number of transactions and / or the correct final date

2) Try processing different types of frequencies. Preferably test a monthly contribution on the last day of a month where there isn't a similar day in the following month (e.g. 30 January)

3) Process a transaction without filling in the total number of transactions (there should be no end date set)

4) Process a recurring contribution with the total instalments set to 1 (it should be treated as a one-off rather than a rec urring transaction). It should not show 'recurring contribution' when you search for it in CiviCRM

5) PayflowPro - check that you can edit the frequencies available on the configure contribution page form

6) Depending on your processor it may be important to identify the transactions that need to be updated or checked. You may wish to check what it being recorded in the civicrm_contribution_recur table for payment processor id, end date and next transaction date.

Specific Live tests

1) Successful and unsuccessful REAL transactions work

2) Money vests into the bank account

3) For recurring transactions wait for the first recurrent transaction to vest 

An example of packaging a Payment Processor

Start by creating a directory named exactly the same as the unique key that you're choosing for your extension. Let's use the example of a Google Checkout payment processor - both the key and the name of directory will be org.civicrm.googlecheckout.

Sample info.xml file for a Payment Processor

<?xml version="1.0" encoding="UTF-8" ?>
 <extension key="org.civicrm.googlecheckout" type="payment">
  <name>Google Checkout</name>
  <description>Google Checkout Payment Processor</description>
  <maintainer>CiviCRM Core Team<></maintainer>
  <comments>For support, please contact project team on the forums. (</comments>

Then once you have the info file, you can start putting the extension package together. 

  • Create the directory org.civicrm.googlecheckout and add the info file
  • Put the report PHP class file in the directory. Remember to name it the same way as the callback attribute in your info.xml, also don't forget to follow the convention in class naming ("Extension_<Type>_<key>_<callback>" - Extension_Report_org_civicrm_report_grant_Grant in this case).

Payment processors do not require templates; however, we need to do some more stuff in order for our extension to work. First of all, this payment processor requires some external libraries to work. Let's put them in packages directory inside org.civicrm.googlecheckout. So now, we have the following structure: 

|-- CRM
|   |-- Contribute
|   |   `-- Payment
|   |       `-- Google.php
|   `-- Event
|       `-- Payment
|           `-- Google.php
|-- Google.php
|-- info.xml
|-- packages
|   `-- Google
|       |-- README
|       |-- demo
|       |   |-- cartdemo.php
|       |   `-- responsehandlerdemo.php
|       |-- googlemessage.log
|       `-- library
|           |-- googlecart.php
|           |-- googleitem.php
|           |-- googlemerchantcalculations.php
|           |-- googleresponse.php
|           |-- googleresult.php
|           |-- googleshipping.php
|           |-- googletaxrule.php
|           |-- googletaxtable.php
|           `-- xml-processing
|               |-- xmlbuilder.php
|               `-- xmlparser.php
`-- processor-type.xml

Finally, there is one more file. We are going to need some additional information - hence the processor-type.xml file. It's contents reflects the contents of civicrm_payment_processor_type database table. Here's an example for Google Checkout payment processor.

<?xml version="1.0" encoding="UTF-8" ?>
  <user_name_label>Merchant ID</user_name_label>

As with other Extensions, the last thing required is to put it in the archive - "zip" it and call the file after the key defined at the beginning: Congratulations, you have now packaged your first Payment  Processor.