Extension:DonationInterface

DonationInterface provides fundraising mechanisms for collecting and tracking payments through various payment gateways.

Overview
The majority of the work done by the Donation Interface extension, is in the area of communicating successfully with a payment gateway. In order to do that, we must accomplish the following tasks:
 * Gather our own data (from any reasonable local source) in a consistent way
 * Stage our data specifically for the payment gateway we want to communicate with
 * Construct the request transaction we want to send to the gateway
 * Send the request to the gateway
 * Receive a response from the gateway
 * Stage the data in the response for re-integration into our system
 * Process the response data

Within DonationInterface, once a GatewayAdapter object is fully defined for a particular payment gateway, all of these things can be accomplished with a very few calls from the controlling object.

Constructing a Gateway Adapter


When a GatewayAdapter is instantiated, several things happen:

Important Classes

 * GatewayAdapter (DonationInterface/gateway_common/gateway.adapter.php) - Abstract class, containing all the functionality that could conceivably be used by more than one gateway. All functionality that must be specific to individual adapters should be defined in the inheriting class.
 * DonationData (DonationInterface/gateway_common/DonationData.php) - The purpose of this class is to harvest donation-related data from any local source, in a consistent way. Classes inheriting from GatewayAdapter will instantiate a DonationData class on construct. The DonationData class will immediately harvest all possible relevant data from whatever source is most appropriate for the situation ($wgRequest, api call, test array), sanitize it where appropriate, and present it in a consistent format. The GatewayAdapter child classes should only ever use data that has come through the DonationData class.
 * GatewayForm (DonationInterface/gateway_common/GatewayForm.php) - A class that extends UnlistedSpecialPage. This will take care of all the not-gateway-specific functionality necessary to present a donation form to a user, and get the results. Of particular interest are the mechanisms for displaying the requested form (or not), handling some of the more universal aspects of form validation, and fetching data that could be used by the specified forms (as in, a list of countries and their codes for a country drop-down).

Classes that inherit from these will be specific to a particular payment gateway. As such, they will be located in a folder named something like DonationInterface/[name]_gateway/.

Defining A New Gateway Adapter
GatewayAdapter extends the GatewayType interface. Here is a quick guide to defining the functions in GatewayType. ...or rather, here is where it's going to go. Soon.

Fundraiser 2011 Development
Prior to Fundraiser 2011 development, the only heavily-used payment gateway was Payflow Pro (Paypal's Credit Card Processor). In an effort to allow new gateways to be easily added to DonationInterface, much of the code is being abstracted out into modular objects, with the goal of having all the gateway-specific structures and logic contained in a few files.

All refactoring work is being done in the fundraiser's Donation Interface branch.

New Installation Procedure
Prior to the refactoring, it was necessary to include the installation files for several subdirectories as if they were standalone extensions. After the refactoring is complete, the installation will be simpler than it was before.

In your LocalSettings.php file, first define which gateways you want to be enabled. For example, the following would turn on all current gateways and extensions:

Next, include the following line in LocalSettings.php:

After this, be sure to redefine the global variables for each gateway you intend to use. Particularly, the MerchantID and Password.

Important Note: As of October, DonationInterface handles its globals in a special sort of way. Any DonationInterface global used by a gateway, can be assigned a value that is either specific to that gateway, or the default for the entire extension (with the specifics overriding the default where both are present). To assign an extension-wide default for gateway globals, simply swap out the gateway prefix in the global variable name, to "wgDonationInterface". For instance: To turn on syslogging by default for the entire DonationInterface extension instead of just, say, the payflow pro gateway, change this: to this: Syslog will now be enabled for all gateways, unless you turn them off with gateway-specific globals.

Additionally, the refactored DonationInterface requires Extension:ContributionTracking to be installed and configured: DonationInterface now uses a number of static ContributionTracking functions to record relevant donation tracking data.

To create custom donation filtering rules, set the $wgCustomFiltersRefRules and $wgCustomFiltersSrcRules global variables in your LocalSettings.php file. These should be set to associative arrays which pair regex patterns with risk score numbers, for example:

Define Globals in LocalSettings.php
While there are many more globals available to override, the following probably need to be defined in LocalSettings if you want DonationInterface to actually work. The majority of additional global variables of interest can be found in donationinterface.php, /payflowpro_gateway/payflowpro_gateway.php, and /globalcollect_gateway/globalcollect_gateway.php. NOTE: There is a slight timing issue in the installation process here, so order totally matters. In general, it is probably a good idea to define your donationinterface globals before you define your gateway-specific globals.

Changed Global Variables
All gateways in DonationInterface now have a standardized global variable prefix, so GatewayAdapter::getGlobal works properly. This necessitated a number of global variable name changes.

Anywhere these old values are encountered in the code, they should be changed to the new values. For the most part, these have been changed in places where the whole page isn't about to be refactored into near-oblivion.

The current list of global variable name changes is as follows:

Things That Have Moved
The basic idea is that we'd pop everything that is of more universal use, out of the payflowpro_gateway directory. Here is a short list of things that have moved.
 * The directory that used to be located under DonationInterface/payflowpro_gateway/forms has been moved to DonationInterface/gateway_forms. Any references to DonationInterface/payflowpro_gateway/forms need to be changed.
 * All the form classes have been changed to reflect their newfound freedom from PayflowPro, and are now added to $wgAutoloadClasses out in donationinterface.php. (Though the forms have yet to be freed from PFP's globals)

New Structure
So far, there are two new classes.

GatewayAdapter class
This is an abstract class that takes care of everything a general gateway needs to do. It it also supposed to yell at you rather loudly if you fail to define any of the particulars that a gateway needs, in order to be able to do anything worthwhile.

Clearly there should be more here.

DonationData class
This class is intended to handle everything that we are likely to want to do with Donation Data. It should load the data in a consistent way from any appropriate source (including a test), saves relevant Contribution Tracking data, generates data we always need to generate (like order IDs), normalises everything, and hands it back to the gateway. It also handles edit tokens.

Ongoing Work
Now we've gotten a decent foothold on this campaign of abstraction, at least half of this thing is a busted mess. We're fixing it; General plan forthcoming.

Meanwhile, here is a list.
 * Change all the messages in an intelligent way. Lots of generally useful-type messages live in Payflow Pro's i18n somewhere, and they need to be popped out a level in a way that won't cause translatewiki to explode (much).
 * The forms (and the bits of DonationInterface that touch them directly) need love.
 * PayflowPro needs to be made to use the new GatewayAdapter class. See the globalcollect gateway for an example of how it ought to look when it's done.
 * Test coverage!
 * Make all the gateway functionality accessible via API. (Should be pretty trivial for gateways that use the new classes)
 * Actually handle the processing of the return data in the GatewayAdapter class (and children where appropriately specific to that particular gateway). Probably just another array variable map of their values to ours in the gateway child class, and a general return value handler in GatewayAdapter.

Unit Testing
Unit tests can be found in the tests directory of the DonationInterface extension.

To run unit tests go to the directory where you have extension checked out. Here is an example:

Unit testing can be run with a few extra options:

Run all DonationInterface unit tests with TestDox:

With TestDox and the group BankTransfer:

You can also get code coverage. The path to code coverage is a relative path.

You can also run debugging to see which test methods are being run:

You can view the code coverage report by going to your local Mediawiki instance:

http://wikimedia-fundraising.localhost.wikimedia.org/extensions/DonationInterface/tests/coverage/

The above testing methods work on trunk, not 1.17. For 1.17 here is an example:

To specify a group:

The Code
Very generally speaking, the process of completing a donation goes like this:
 * The donor is sent to a donation form that is already tied to a specific payment gateway. This form will be controlled by a gateway-specific unlisted special page of a class descended from the GatewayForm class.
 * The extended GatewayForm class will instantiate its gateway adapter. On construction, the gateway adapter will gather, normalize, and validate any relevant data in $wgRequest. All gateways use the same class, DonationData, to accomplish this task. Doing this means that all the data is subject to the same normalization and validation processes, no matter who's asking for it or where it came from.
 * The controlling GatewayForm child class will then use the gateway adapter to determine where the user is in the donation process, and if appropriate (no errors on validation, all data present, no strangeness with the edit token or session, that sort of thing), use that same gateway adapter to interact with the remote payment servers in order to take the next step in the donation process. As every payment gateway has its own set of rules (and in some cases, completely different transaction types that are not directly analogous to anything else in any of the other gateways) it is up to the adapter to sort all of that out internally, and only expose functionality to the controlling object in such a way that it seems more or less universal across all possible adapter objects.
 * After having the gateway adapter perform one or more transactions with the remote payment gateway, the controlling object would then use the gateway to determine what happened, and display appropriate results to the prospective donor. This could be a "Thank you" page, an error displayed on the submitted form prompting the user to try again in a meaningful way, or a more fatal sort of error page.

DonationInterface


DonationInterface has been partially mapped out into a diagram.

This diagram does not cover 100% of the code:
 * Missing API
 * Missing view
 * DonationData is incomplete
 * GlobalCollectAdapter does not map past the primary methods used in __construct.
 * Only one entry is shown with a form that is used as a demo: TwoStepAmount
 * Unit testing is not in the diagram.

This is a work in progress. Please comment on any errors or inconsistencies.

Queue Handler


This diagram is a work in progress.
 * The database functionality has not been mapped out yet.

Language-only
It is possible to load DonationInterface in such a way that ONLY its language/i18n files are exposed. We make use of this feature on the WMF cluster so messaging can stay consistent and easily translated on banners/landing pages/appeals/etc hosted in places like wikimediafoundation.org or donate.wikimedia.org as well as on the payment processing cluster (which runs the full DonationInterface).

To enable JUST language support from DonationInterface, add: require_once( "$IP/extensions/DonationInterface/donationinterface_langonly.php" ); to your LocalSettings.php file.