Release status: stable
|Description||Provides fundraising mechanisms for collecting payments|
|Check usage and version matrix; code metrics|
|Bugs: list open list all report|
DonationInterface provides fundraising mechanisms for collecting and tracking payments through various payment gateways.
- 1 Overview
- 2 Fundraiser 2011 Development
- 3 The Code
- 4 Special cases
- 5 See also
Overview[edit | edit source]
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[edit | edit source]
When a GatewayAdapter is instantiated, several things happen:
Important Classes[edit | edit source]
- 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[edit | edit source]
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[edit | edit source]
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[edit | edit source]
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:
$wgDonationInterfaceEnablePayflowPro = true; $wgDonationInterfaceEnableGlobalCollect = true; $wgDonationInterfaceEnableStomp = true; $wgDonationInterfaceEnableConversionLog = true; $wgDonationInterfaceEnableMinfraud = true; $wgDonationInterfaceEnableRecaptcha = true; $wgDonationInterfaceEnableMinfraud_as_filter = true; //you wouldn't ever actually want this AND minfraud as a standalone enabled at the same time. $wgDonationInterfaceEnableReferrerFilter = true; $wgDonationInterfaceEnableSourceFilter = true;
Next, include the following line in LocalSettings.php:
require_once( "$IP/extensions/DonationInterface/donationinterface.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:
$wgPayflowProGatewayUseSyslog = true;
$wgDonationInterfaceUseSyslog = true;
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:
// Filter for suspicious HTTP referrer URLs $wgCustomFiltersRefRules = array( '/hackers\.com/' => 100 '/wikipedia\.org/' => -50, ); // Filter for suspicious utm_source values $wgCustomFiltersSrcRules = array( '/TestNotice1/' => 50 '/foobar/' => -100, );
Define Globals in LocalSettings.php[edit | edit source]
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.
|$wgGlobalCollectGatewayMerchantID||Our Merchant ID with GlobalCollect||string|
|$wgDonationInterfaceAllowedHtmlForms||A global whitelist of forms allowed to be loaded via RapidHtml. Intended to be used by any gateway. NOTE: This is not actually used anywhere directly. You must add these values to a gateway's AllowedHtmlForms for them to actually get used on any given gateway.||Array. For each form, the key should be the name of the form (aka "ffname"), and the value should be the file's location.|
|$wgGlobalCollectGatewayAllowedHtmlForms||The definitive whitelist of forms allowed to be loaded via RapidHtml, for GlobalCollect.||Array. For each form, the key should be the name of the form (aka "ffname"), and the value should be the file's location. NOTE: If you wish to use the global list of forms as well, you will have to set $wgGlobalCollectGatewayAllowedHtmlForms = $wgDonationInterfaceAllowedHtmlForms manually, before adding (or removing) forms locally.|
|$wgDonationInterfaceDisplayDebug||Set to true to get extra debugging information dumped to the screen.||boolean|
Changed Global Variables[edit | edit source]
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:
|Old Global Name||New Global Name||Purpose|
|$wgPayflowGatewayTest||$wgPayflowProGatewayTest||Denotes if the Payflow Pro gateway has been installed in test mode or not. Could also be set in unit tests.|
|$wgPayflowProTimeout||$wgPayflowProGatewayTimeout||Sets the CURLOPT_TIMEOUT header for Payflow Pro.|
|$wgPayflowGatewayDefaultForm||$wgPayflowProGatewayDefaultForm||Sets the default form to use with Payflow Pro.|
|$wgPayflowGatewaySalt||$wgPayflowProGatewaySalt||Sets a unique Salt string for Payflow Pro's edit token matching.|
|$wgPayflowGatewayDBserver||DELETED||Now uses the self-contained ContributionTracking database configuration directly|
|$wgPayflowGatewayDBname||DELETED||Now uses the self-contained ContributionTracking database configuration directly|
|$wgPayflowGatewayDBuser||DELETED||Now uses the self-contained ContributionTracking database configuration directly|
|$wgPayflowGatewayDBpassword||DELETED||Now uses the self-contained ContributionTracking database configuration directly|
|$wgPayflowGatewayHeader||$wgPayflowProGatewayHeader||"A string that can contain wikitext to display at the head of the credit card form", for Payflow Pro|
|$wgPayflowGatewayUseHTTPProxy||$wgPayflowProGatewayUseHTTPProxy||Boolean indicator for having pfp use a proxy.|
|$wgPayflowGatewayHTTPProxy||$wgPayflowProGatewayHTTPProxy||Pfp's proxy URL.|
|$wgPayflowGatewayPaypalURL||$wgPayflowProGatewayPaypalURL||"The URL to redirect a transaction to PayPal" for PayflowPro. This should probably be moved out to $wgDonationInterfacePaypalURL, if it survives the refactoring at all.|
|$wgPayflowSMaxAge||$wgPayflowProGatewaySMaxAge||max-age value for squid, for Payflow Pro. Another one that maybe should apply to the entire extension, rather than just pfp.|
|$wgPayflowHtmlFormDir||$wgPayflowProGatewayHtmlFormDir||Directory for HTML forms, for Payflow Pro. Yet another one that might make sense to define elsewhere.|
|$wgPayflowAllowedHtmlForms||$wgPayflowProGatewayAllowedHtmlForms||"An array of allowed HTML forms" for Payflow Pro. Likely to stay right where it is, or disappear entirely in favor of a more complicated solution that doesn't involve editing install variables.|
|$wgPayflowGatewayUseSyslog||$wgPayflowProGatewayUseSyslog||Boolean indicator for pfp's logging methods: Are we using the SysLog, or not?|
|$wgPayflowPriceFloor||$wgPayflowProGatewayPriceFloor||Lower bounds for a contribution amount for pfp (in USD)|
|$wgPayflowPriceCieling||$wgPayflowProGatewayPriceCieling||Upper bounds for a contribution amount for pfp (in USD)|
|$wgDonationInterfaceTest||NEW||Not exactly configurable from LocalSettings in a way that will stick, so don't try. The idea here is that if you're in a gateway that has been installed in test mode, it will set this more universal global so all the statically-called functions can pick up on the test state. Functions that check for a test state should look at this, and not at the locally defined gateway-specific global.|
|$wgPayflowGatewayPaypalURL||$wgDonationInterfacePaypalURL||URL for PayPal redirects (typically Special:ContributionTracking?gateway=paypal)|
|$wgPayflowProURL||$wgPayflowProGatewayURL||URL to post the transaction to for PayflowPro tranactions.|
|$wgPayflowProPartnerID||$wgPayflowProGatewayPartnerID||PartnerID for PayflowPro Gateway|
|$wgPayflowProVendorID||$wgPayflowProGatewayVendorID||VendorID for PayflowPro Gateway|
|$wgPayflowProUserID||$wgPayflowProGatewayUserID||UserID for PayflowPro Gateway|
|$wgPayflowProPassword||$wgPayflowProGatewayPassword||Password for PayflowPro Gateway|
Things That Have Moved[edit | edit source]
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[edit | edit source]
So far, there are two new classes.
GatewayAdapter class[edit | edit source]
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[edit | edit source]
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[edit | edit source]
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[edit | edit source]
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:
cd /www/sites/localhost/wikimedia-fundraising.localhost.wikimedia.org cd extensions/DonationInterface/tests
Unit testing can be run with a few extra options:
Run all DonationInterface unit tests with TestDox:
../../../tests/phpunit/phpunit.php --testdox-html /tmp/testdox.DonationInterface.html AllTests.php
With TestDox and the group BankTransfer:
../../../tests/phpunit/phpunit.php --group BankTransfer --testdox-html /tmp/testdox.DonationInterface.html AllTests.php
You can also get code coverage. The path to code coverage is a relative path.
../../../tests/phpunit/phpunit.php --testdox-html /tmp/testdox.DonationInterface.html --coverage-html coverage AllTests.php
You can also run debugging to see which test methods are being run:
../../../tests/phpunit/phpunit.php --debug --testdox-html /tmp/testdox.DonationInterface.html --coverage-html coverage AllTests.php
You can view the code coverage report by going to your local Mediawiki instance:
The above testing methods work on trunk, not 1.17. For 1.17 here is an example:
/www/library/phpunit/phpunit/phpunit.php --verbose --debug --coverage-html coverage/html --coverage-clover coverage/clover/index.xml --testdox-html coverage/html/testdox.html AllTests
To specify a group:
/www/library/phpunit/phpunit/phpunit.php --verbose --debug --coverage-html coverage/html --coverage-clover coverage/clover/index.xml --testdox-html coverage/html/testdox.html --group BankTransfer AllTests
The Code[edit | edit source]
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.
Diagram[edit | edit source]
DonationInterface[edit | edit source]
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[edit | edit source]
This diagram is a work in progress.
- The database functionality has not been mapped out yet.
Special cases[edit | edit source]
Language-only[edit | edit source]
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.
See also[edit | edit source]
|This extension is being used on one or more Wikimedia projects. This probably means that the extension is stable and works well enough to be used by such high-traffic websites. Look for this extension's name in Wikimedia's CommonSettings.php and InitialiseSettings.php configuration files to see where it's installed. A full list of the extensions installed on a particular wiki can be seen on the wiki's Special:Version page.|