-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Contributing
This is a brief guide to contributing new payment gateways to Active Merchant. This guide includes instructions on checking out the source code, developing the new gateway, and finally contributing the new gateway back to the project.
Following is information on coding the new payment gateway. Also included are some of the common patterns and data structures used by gateways in Active Merchant.
Please note that while some payment gateways use SOAP as the format for communication, we do not accept any gateways that have a requirement on soap4r. We find that the overhead required in using the soap4r library is greater than just creating the SOAP document by hand using Nokogiri
It should also be noted that when there is a choice between the implementation of a Name-Value pair API and an XML API that the Name-Value pair API code will usually be faster to develop, more elegant, and smaller in size than equivalent implementation of the XML API.
$ git clone git://github.com/activemerchant/active_merchant.git
$ cd ./active_merchant
$ bundle install
All new gateways should use the gateway generator to create their implementation. From the root directory of Active Merchant, run (replace CardStream
with the name of your gateway):
$ script/generate gateway card_stream
Writing file lib/active_merchant/billing/gateways/card_stream.rb
Writing file test/unit/gateways/card_stream_test.rb
Writing file test/remote_tests/remote_card_stream_test.rb
This will generate an adapter with default implementations for the following methods:
purchase(money, creditcard, options = {})
authorize(money, creditcard, options = {})
capture(money, tx_reference, options = {})
void(tx_reference, options = {})
refund(money, tx_reference, options = {})
These are the primary transactions that most gateways support (though there are other standard transactions described below, as well).
The first step is usually getting a successful `purchase()` with the minimum number of required parameters. You can use the remote tests (see below) as a test harness.
There are also several other transactional methods part of an adapter’s public API that should be implemented, where possible. These include:
-
verify(creditcard, options = {})
which verifies that a credit card is valid. This lets you verify that a payment method is valid before trying to run an actual transaction (which might be done at some later date). Some gateways support a verify-like call directly, but for ones that don’t the adapter should run and void a small-amount auth transaction (which is the default implementation). -
credit(money, creditcard, options = {})
which is like `refund`, but allows you to add money back to a card without having to reference a previous transaction. -
store(creditcard, options = {})
adds/copies the payment method information to the gateway. Some gateways offer a vault-like capability where a card can be stored for future use outside any one specific transaction.
There are also a few non-transactional methods part of an adapter’s public API that should always be implemented:
-
scrub(transcript)
which, given a text representation of the HTTP request/response to the gateway, replaces any sensitive values like the PAN, CVV, and gateway credentials with `[FILTERED]`. -
supports_scrubbing?
returnstrue
ifscrub
is implemented.
The base Gateway class provides a lot of helpful methods that will save you effort when implementing your gateway.
The money
value used in all methods is an Integer
value in cents (even if the gateway API uses decimal format). There are two different styles for formatting amounts in use:
-
:dollars
– The amount is formatted as a float dollar amount with two decimal places -
:cents
– The amount is formatted as an integer value in cents
You set the money format using the class accessor money_format
. For example:
class ProtxGateway < Gateway
self.money_format = :dollars
end
Setting the money format allows you to use the amount
method defined on the base gateway class. Simply pass in the amount as an Integer
value in cents (which is what all AM public methods accept, independent of the underlying gateway requirements) and the amount will be formatted appropriately.
# if self.money_format = :dollars, will set as 1.01
post[:amount] = amount(101)
Please note that passing a Money
object is deprecated and will be removed from a future release.
You need to set the default currency of the gateway if the gateway supports multiple currencies. This is done by using the class accessor default_currency
. For example:
class ProtxGateway < Gateway
self.default_currency = 'GBP'
end
Transactions can specify a currency override by using the :currency
option in the options
hash.
Each gateway contains metadata, which are available as class accessors. The metadata is needed for generating documentation, and for intelligent display within applications such as Shopify. Please determine these values from the gateway documentation or by contacting their support directly.
-
Gateway.supported_countries
– sets the countries of merchants the gateway supports in ISO 3166-1-alpha-2 code -
Gateway.supported_cardtypes
– sets the card types supported by the gateway. -
Gateway.homepage_url
– sets the homepage URL of the payment gateway. -
Gateway.display_name
– sets the name of the gateway for display purposes, such as generating documentation. -
Gateway.application_id
– This is the application making calls to the gateway. This is useful for things like the Paypal build notation (BN) id fields.
-
:visa
– Visa -
:master
– MasterCard -
:discover
– Discover Card -
:american_express
– American Express -
:diners_club
– Diners Club -
:jcb
– JCB -
:switch
– UK Maestro, formerly Switch -
:solo
– Solo -
:dankort
– Dankort -
:maestro
– International Maestro -
:forbrugsforeningen
– Forbrugsforeningen -
:laser
– Laser
The Visa Electron, and Visa Delta cards are not in the list because payment gateways accept the cards as the :visa
card type.
Example usage:
class AuthorizeNetGateway < Gateway
self.supported_countries = ['US']
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
self.homepage_url = 'http://www.authorize.net/'
self.display_name = 'Authorize.net'
end
You shouldn’t be inventing new options for the options hash that gets passed into the public methods. Try to look at the other gateways for the hash keys they use as options and copy those. This allows the easy substitution of one gateway for another.
The options hash can include the following keys:
:order_id
:ip
:customer
:invoice
:merchant
:description
:email
:currency
The reason that the option has a :currency
option is because we are phasing out support for the Money
object. This means that in the future the money amount will always be an Integer
in cents, and the currency will be set by the :currency
option.
The current pattern to use when setting the currency is:
options[:currency] || currency(money)
The currency()
method is defined in the base Gateway
class, and will use the currency of the money, if it has one, or return the default currency for the gateway.
There are 3 different addresses you can use. There are :billing_address
, :shipping_address
, or you can just pass in :address
and it will be used for both.
:billing_address
:shipping_address
:address
This is the common pattern to use for the address:
billing_address = options[:billing_address] || options[:address]
shipping_address = options[:shipping_address]
The address is a hash with the following keys:
:name
:company
:address1
:address2
:city
:state
:country
:zip
:phone
Optionally, a gateway action may accept an :three_d_secure
options hash containing 3DS-specific options that may contain the following fields:
:version
:eci
:cavv
:ds_transaction_id
:xid
Please see the Standardized 3DS Fields page for an expanded discussion of our established conventions for passing 3DS options to a gateway.
A complete options
hash might be:
options = {
:order_id => '1',
:ip => '10.0.0.1',
:customer => 'Cody Fauser',
:description => '200 Web 2.0 M&Ms',
:email => '[email protected]',
:address => {
:name => 'Cody Fauser',
..
..
:zip => '90210'
}
}
Note that the Address-Hash does not include the email-address – you have to supply that with the normal Options-Hash!
Make sure to test your gateway before contributing!
Examples of suggested gateways and tests are here .
Ready to contribute your gateway ?