Customize the Credit Card Tender Type
Workarea provides a credit card tender type, but it is not integrated with a payment processing service. Therefore, application developers must customize the credit card tender type to meet the needs of the retailer.
Workarea provides many plugins that do this work for you for various payment processors. You should use one of these plugins if you can. However, there are many payment services (for examples, see the list of gateways included in Active Merchant 1.102.0). A retailer may choose a service for which a Workarea plugin does not yet exist.
In that case, you will need to extend the credit card tender type yourself, either directly within an application or within a plugin that can be re-used across applications. This document provides the procedure to do this work, which can be summarized as:
- Set up the credit card gateway to communicate (or simulate communication with) the payment service
- Edit the proxy configuration to allow communication with the payment service (Commerce Cloud only)
- Customize credit card tokenization for your gateway
- Customize the credit card operation implementations for your gateway
- Customize the Storefront integration as desired by the retailer
Credit Card Gateway
Workarea communicates with the credit card processing service through a gateway object.
Workarea initializes a credit card gateway for you, but it uses a bogus gateway that does not communicate with an actual payment service.
(The specific gateway class is ActiveMerchant::Billing::BogusGateway
from Active Merchant 1.102.0.)
Within your application or plugin, you must replace the default gateway. The following sections explain how to do this, first for automated testing, and then for production and development environments.
Automated Testing
Workarea provides automated tests that are intended to communicate with an actual payment service, to test its integration with Workarea. However, to avoid coupling tests to a network service, you should use vcr cassettes to record the payment service's responses and commit those cassettes to your repository.
To use this pattern, create a module that sets up the gateway to be used with vcr, and then mix that module into specific tests. The following sections provide boilerplate for your test support module and test decorators, followed by concrete examples from a production implementation.
Credit Card vcr Gateway Mixin
Start with the following boilerplate to create a module that changes the credit card gateway for the duration of a test. You will mix this module into test cases that should use the vcr gateway rather than the original credit card gateway. Refer to the annotations to customize the boilerplate for your specific implementation.
# your_engine/test/support/workarea/credit_card_vcr_gateway.rb [1][2]
module Workarea
module CreditCardVcrGateway #[3]
def self.included(test)
test.setup :set_up_gateway
test.teardown :reset_gateway
end
def set_up_gateway
@_old_gateway = Workarea.config.gateways.credit_card
Workarea.config.gateways.credit_card = #[4]
ActiveMerchant::Billing::CreditCardGateway.new( #[5]
merchant_id: 'foo', #[6]
private_key: 'bar',
baz: 'qux'
)
end
def reset_gateway
Workarea.config.gateways.credit_card = @_old_gateway #[7]
end
end
end
[1]
Replace the pathname your_engine
with the pathname for your application or plugin, such as ~/discount-supercenter
or ~/workarea-processor-pro
.
[2]
Replace the filename credit_card_vcr_gateway.rb
with one specific to your gateway, such as processor_pro_vcr_gateway.rb
.
[3]
Rename this module to be specific to your gateway, such as ProcessorProVcrGateway
.
[4]
In the setup method, re-assign the gateway instance stored in Workarea.config.gateways.credit_card
.
The gateway you assign here will be used as the credit card gateway for all test cases that mix in this module.
[5] Initialize the gateway object you want to use for automated testing with vcr. Typically, this object is of the same class as your production gateway, but the initialization arguments may differ from production. Work with your retailer and/or other partners to determine the correct gateway class and arguments.
[6] Initially, use actual credentials (via Rails secrets or Rails credentials) so you can communicate with the payment service over the network and record the vcr cassettes. After the cassettes are recorded, remove all credentials (or use dummy values if the arguments are required). The credentials are no longer needed since the responses will be read from the cassettes. Don't commit secrets to your repository!
[7] In the teardown method, restore the original credit card gateway.
Integration Test Decorator
Workarea Core provides Payment::CreditCardIntegrationTest
to test the integration of Workarea and the payment processing service.
Decorate this test to include the module you created above. When your credit card tender type implementation is complete, the tests in this test case should continue to pass without modifying them.
Start with the following boilerplate, customizing as indicated by the annotations.
# your_engine/test/models/workarea/payment/credit_card_integration_test.decorator [1]
module Workarea
decorate Payment::CreditCardIntegrationTest, with: :your_engine do #[8]
decorated { include CreditCardVcrGateway } #[9]
end
end
[8]
If developing an application, you can omit the with
argument.
If developing a plugin, replace your_engine
with a slug identifying your plugin, such as processor_pro
.
[9] Include the module that sets up your vcr gateway. See [3].
Operation Implementation Test Decorators
The other tests that should use your vcr gateway are the operation implementation model tests (see section Operation Implementations). Decorate each of those test cases to mix in the vcr gateway module, and then modify or add tests to cover your specific gateway. At a minimum, you'll likely need to modify some existing tests to use new cassettes that are specific to your gateway. Start with the following boilerplate, and refer to the inline annotations and the concrete examples further below.
# your_engine/test/models/workarea/payment/authorize/credit_card_test.decorator [1]
module Workarea
decorate Payment::Authorize::CreditCardTest, with: :your_engine do #[8]
decorated { include CreditCardVcrGateway } #[9]
# [10]
#
# def test_foo
# VCR.use_cassette 'credit_card_gateway/foo' do
# super
# end
# end
#
# ...
end
end
# your_engine/test/models/workarea/payment/purchase/credit_card_test.decorator [1]
module Workarea
decorate Payment::Purchase::CreditCardTest, with: :your_engine do #[8]
decorated { include CreditCardVcrGateway } #[9]
# [10]
#
# def test_foo
# VCR.use_cassette 'credit_card_gateway/foo' do
# super
# end
# end
#
# ...
end
end
# your_engine/test/models/workarea/payment/capture/credit_card_test.decorator [1]
module Workarea
decorate Payment::Capture::CreditCardTest, with: :your_engine do #[8]
decorated { include CreditCardVcrGateway } #[9]
# [10]
#
# def test_foo
# VCR.use_cassette 'credit_card_gateway/foo' do
# super
# end
# end
#
# ...
end
end
# your_engine/test/models/workarea/payment/refund/credit_card_test.decorator [1]
module Workarea
decorate Payment::Refund::CreditCardTest, with: :your_engine do #[8]
decorated { include CreditCardVcrGateway } #[9]
# [10]
#
# def test_foo
# VCR.use_cassette 'credit_card_gateway/foo' do
# super
# end
# end
#
# ...
end
end
[10] Decorate each test in this test case to use a vcr cassette, and/or add more tests specific to your gateway that use vcr cassettes. Refer to the concrete examples in the next section.
( The full set of changes to these tests goes beyond gateway setup and gets into operation implementations, which are covered separately. See section Operation Implementations. )
Automated Testing Gateway Examples
In addition to the boilerplate above, refer to the following concrete examples from the Workarea Braintree 1.0.3 source:
- Credit card vcr gateway mixin:
BraintreeGatewayVCRConfig
- Integration test decorator:
Payment::CreditCardIntegrationTest
- Integration test vcr cassettes:
/test/vcr_cassettes/credit_card/
- Operation implementation test decorators:
- Operation implementation test vcr cassettes:
/test/vcr_cassettes/braintree
Production & Development
Workarea initializes a default credit card gateway and assigns it to Workarea.config.gateways.credit_card
(in the 11_payment.rb
initializer).
For use cases other than automated testing, change the credit card gateway by creating your own initializer where you can initialize and re-assign an appropriate credit card gateway for each use case.
The following sections provide boilerplate for an initializer and a concrete example.
Gateway Initializer Boilerplate
Start with the following boilerplate to initialize your credit card gateway. Refer to the inline annotations and customize as needed.
# your_engine/config/initializers/credit_card_gateway.rb [1][2]
credentials = Rails.application.secrets.credit_card_gateway #[2][3]
if credentials.present? #[4]
Workarea.config.gateways.credit_card = #[5]
ActiveMerchant::Billing::CreditCardGateway.new( #[6]
credentials.deep_symbolize_keys
)
else
Workarea.config.gateways.credit_card = #[5]
ActiveMerchant::Billing::BogusCreditCardGateway.new #[6]
end
[1]
Replace the pathname your_engine
with the pathname for your application or plugin, such as ~/discount-supercenter
or ~/workarea-processor-pro
.
[2]
Replace the substring credit_card_gateway
with one specific to your gateway, such as processor_pro
.
[3] Optionally replace Rails secrets with Rails credentials, or another solution that keeps your credentials out of your source code repository.
[4] Replace the logic here with your own use cases. You may want to vary your gateway initialization logic based on credentials, rails environment, current site (if multi site), or other application states or combination of states.
[5]
Within each logical branch, ensure you assign an initialized gateway object to Workarea.config.gateways.credit_card
.
[6] Within each logical branch, replace the class and initialization arguments with the gateway class and arguments appropriate to your payment service and use case. Work with your retailer and/or other partners to determine the correct gateway classes, arguments, and credentials.
Gateway Initializer Example
For a concrete example, refer to the workarea.rb
initializer from Workarea Braintree 1.0.3, which delegates much of the logic to Workarea::Braintree.auto_configure_gateway
.
( You can use this pattern yourself if creating a plugin, but it's overkill for an implementation directly within an application. )
With Workarea Braintree 1.0.3 installed, calling Workarea.config.gateways.credit_card
returns an instance of either:
ActiveMerchant::Billing::BraintreeGateway
from Active Merchant 1.102.0, or
ActiveMerchant::Billing::BogusBraintreeGateway
from Workarea Braintree 1.0.3.
Proxy Configuration
( The following step applies to Workarea Commerce Cloud only. )
With your gateway set up, you must allow Workarea to communicate with the payment service over the network. To do so, add the endpoint(s) for the payment service to the proxy configuration using the Workarea CLI.
See CLI, Edit.
Credit Card Tokenization
Your credit card gateway must support tokenization. Workarea persists credit cards during payment transactions (see section Operation Implementations), and also when shoppers manage cards in their accounts in the Storefront. Before persisting a card, it must be tokenized.
Workarea encapsulates its tokenization logic in the class Payment::StoreCreditCard
.
You may need to decorate this class to customize it for your gateway.
The following sections provide boilerplate for this decorator and a concrete example.
Tokenization Decorator Boilerplate
# your_engine/app/models/workarea/payment/store_credit_card.decorator [1]
module Workarea
decorate Payment::StoreCreditCard, with: :your_engine do #[2]
def perform! #[3]
return true if @credit_card.token.present?
response = handle_active_merchant_errors do #[4]
gateway.store(@credit_card.to_active_merchant) #[5]
end
@credit_card.token = response.params['billingid'] #[6]
response.success?
end
end
end
[1]
Replace the pathname your_engine
with the pathname for your application or plugin, such as ~/discount-supercenter
or ~/workarea-processor-pro
.
[2]
If developing an application, you can omit the with
argument.
If developing a plugin, replace your_engine
with a slug identifying your plugin, such as processor_pro
.
[3]
Re-implement perform!
with the appropriate changes for your gateway.
The method gateway
returns the gateway object described in the section Credit Card Gateway.
[4]
You should handle gateway exceptions.
For an Active Merchant gateway, you can re-use handle_active_merchant_errors
from the base implementation.
If you are not using an Active Merchant gateway, consider implementing your own error handling here.
[5]
Use the appropriate API call to store a credit card for your gateway, and pass the appropriate arguments.
The correct API call is often store
.
Refer to your gateway's documentation for the method name and expected arguments.
[6] If your gateway returns the token in a different param, update that here. Check your gateway's documentation.
Tokenization Decorator Example
For a concrete example, refer to the Payment::StoreCreditCard
decorator from Workarea Braintree 1.0.3.
Operation Implementations
Workarea uses operation implementations to create authorize, purchase, capture, and refund transactions.
Each operation implementation communicates with the payment service through the gateway and implements #complete!
and #cancel!
.
For each implementation operation, you may need to customize one or both of those methods for your gateway.
The following sections look at each operation implementation.
Authorize
For the authorize operation implementation, decorate Payment::Authorize::CreditCard
to customize it for your gateway.
Use the boilerplate and examples that follow for guidance.
Authorize Decorator Boilerplate
Start with the following boilerplate and customize it as necessary.
# your_engine/app/models/workarea/payment/authorize/credit_card.decorator [1]
module Workarea
decorate Payment::Authorize::CreditCard, with: :your_engine do #[2]
def complete! #[3]
return unless StoreCreditCard.new(tender, options).save! #[4]
transaction.response = #[5][6]
handle_active_merchant_errors do #[7]
gateway.authorize( #[8]
transaction.amount.cents, #[9]
tender.to_token_or_active_merchant,
transaction_options #[10]
)
end
end
def cancel! #[11]
return unless transaction.success? #[12]
transaction.cancellation = #[13][6]
handle_active_merchant_errors do #[7]
gateway.void( #[14]
transaction.response.authorization #[15]
)
end
end
private
def transaction_options #[10]
{
order_id: tender.payment.id, #[16]
foo: 'bar',
baz: 'qux'
}
end
end
end
[1]
Replace the pathname your_engine
with the pathname for your application or plugin, such as ~/discount-supercenter
or ~/workarea-processor-pro
.
[2]
If developing an application, you can omit the with
argument.
If developing a plugin, replace your_engine
with a slug identifying your plugin, such as processor_pro
.
[3]
If necessary for your gateway, re-implement complete!
, which is part of the operation implementation contract.
Note that you may be able to decorate transaction_options
instead.
See [10].
[4]
You must tokenize the credit card and persist the token for future use.
You can do this in a dedicated request to the gateway using StoreCreditCard#save
(see section Credit Card Tokenization).
Or, if supported by your gateway, you can tokenize in the same request as the authorization.
If you do so, you must save the token after the authorize request succeeds.
Here is boilerplate for that scenario:
def complete!
transaction.response =
handle_active_merchant_errors do
if tender.token.present?
gateway.authorize(
transaction.amount.cents,
tender.token,
transaction_options
)
else
gateway.authorize(
transaction.amount.cents,
tender.to_active_merchant,
transaction_options
)
end
end
if transaction.response.success? && tender.token.blank?
tender.token =
transaction.response.params['credit_card']['token']
tender.save!
end
end
[5]
You must assign transaction.response
to fulfill the contract for #complete!
.
[6]
The value assigned must be an object of type ActiveMerchant::Billing::Response
, which is returned by Active Merchant gateways.
If your gateway doesn't return this type of object, you'll have to initialize the instance yourself, constructing the arguments from the gateway's response.
[7]
You should handle gateway exceptions.
For an Active Merchant gateway, you can re-use handle_active_merchant_errors
from the base implementation.
If you are not using an Active Merchant gateway, consider implementing your own error handling here.
[8]
Use the appropriate API call for your gateway, which is represented by gateway
(see section Credit Card Gateway).
The correct API call is often authorize
, but refer to your gateway's documentation.
[9]
Pass the proper arguments for the gateway API call.
The first argument is typically the amount in cents.
To construct this data, you have access to transaction
, tender
, options
, and address
.
[10]
Implement transaction_options
to return a hash containing the data required by your gateway.
You may be able to decorate only this method if your gateway uses the same tokenization logic and API calls as the base implementation.
[11]
If necessary for your gateway, re-implement cancel!
, which is part of the operation implementation contract.
[12]
You must return early if the transaction wasn't successful to fulfill the contract for #cancel!
.
[13]
You must assign transaction.cancellation
to fulfill the contract for #cancel!
.
[14]
Use the appropriate API call for your gateway, which is represented by gateway
(see section Credit Card Gateway).
The correct API call is often void
or cancel
, but refer to your gateway's documentation.
[15]
Pass the proper arguments for the gateway API call.
The first argument is typically a reference to the original authorization.
To construct this data, you have access to transaction
, tender
, options
, and address
.
[16]
To construct the transaction options data, you have access to transaction
, tender
, options
, and address
.
Authorize Decorator Example
Here is a concrete example of an authorize operation implementation decorator used in production:
Payment::Authorize::CreditCard
decorator from Workarea Braintree 1.0.3
Purchase
For the purchase operation implementation, decorate Payment::Purchase::CreditCard
to customize it for your gateway.
Use the boilerplate and examples that follow for guidance.
Purchase Decorator Boilerplate
Start with the following boilerplate and customize it as necessary.
# your_engine/app/models/workarea/payment/purchase/credit_card.decorator [1]
module Workarea
decorate Payment::Purchase::CreditCard, with: :your_engine do #[1][2]
def complete! #[3]
return unless StoreCreditCard.new(tender, options).save! #[4]
transaction.response = #[5][6]
handle_active_merchant_errors do #[7]
gateway.purchase( #[17]
transaction.amount.cents, #[9]
tender.to_token_or_active_merchant,
transaction_options #[10]
)
end
end
def cancel! #[11]
return unless transaction.success? #[12]
transaction.cancellation = #[13][6]
handle_active_merchant_errors do #[7]
gateway.void( #[14]
transaction.response.authorization #[15]
)
end
end
private
def transaction_options #[10]
{
order_id: tender.payment.id, #[16]
foo: 'bar',
baz: 'qux'
}
end
end
end
[17]
Use the appropriate API call for your gateway, which is represented by gateway
(see section Credit Card Gateway).
The correct API call is often purchase
, but refer to your gateway's documentation.
Purchase Decorator Example
Here is a concrete example of a purchase operation implementation decorator used in production.
Payment::Purchase::CreditCard
decorator from Workarea Braintree 1.0.3
Capture
For the capture operation implementation, decorate Payment::Capture::CreditCard
to customize it for your gateway.
Use the boilerplate and examples that follow for guidance.
Capture Decorator Boilerplate
Start with the following boilerplate and customize it as necessary.
# your_engine/app/models/workarea/payment/capture/credit_card.decorator [1]
module Workarea
decorate Payment::Capture::CreditCard, with: :your_engine do #[1][2]
def complete! #[3]
validate_reference! #[18]
transaction.response = #[5][6]
handle_active_merchant_errors do #[7]
gateway.capture( #[19]
transaction.amount.cents, #[20]
transaction.reference.response.authorization
)
end
end
# def cancel! #[21]
# end
end
end
[18]
You must use validate_reference!
for a capture operation, unless this does not apply to your gateway.
[19]
Use the appropriate API call for your gateway, which is represented by gateway
(see section Credit Card Gateway).
The correct API call is often capture
, but refer to your gateway's documentation.
[20]
Pass the proper arguments for the gateway API call.
The first argument is typically the amount in cents.
The second argument is typically a reference to the original transaction's authorization.
To construct this data, you have access to transaction
, tender
, options
, and address
.
[21] For a credit card capture, there isn't anything to cancel, so this is implemented in the base implementation to do nothing. However, if your gateway provides an appropriate API call, use it here instead.
Refund Decorator Example
Here is a concrete example of a capture operation implementation decorator used in production:
Payment::Capture::CreditCard
decorator from Workarea Braintree 1.0.3
Refund
For the refund operation implementation, decorate Payment::Refund::CreditCard
to customize it for your gateway.
Use the boilerplate and examples that follow for guidance.
Refund Decorator Boilerplate
Start with the following boilerplate and customize it as necessary.
# your_engine/app/models/workarea/payment/refund/credit_card.decorator [1]
module Workarea
decorate Payment::Refund::CreditCard, with: :your_engine do #[1][2]
def complete! #[3]
validate_reference! #[18]
transaction.response = #[5][6]
handle_active_merchant_errors do #[7]
gateway.refund( #[22]
transaction.amount.cents, #[20]
transaction.reference.response.authorization
)
end
end
# def cancel! #[23]
# end
end
end
[22]
Use the appropriate API call for your gateway, which is represented by gateway
(see section Credit Card Gateway).
The correct API call is often refund
, but refer to your gateway's documentation.
[23]
For a credit card refund, there is nothing to cancel, so this is implemented as a noop in the base implementation.
However, if your gateway provides an appropriate API call, use it here instead.
Use validate_reference!
if the API call requires a reference transaction.
Decorator Example
Here is a concrete example of a refund operation implementation decorator used in production:
Payment::Refund::CreditCard
decorator from Workarea Braintree 1.0.3
Storefront Integration
The credit card tender type is already fully integrated into the Workarea Storefront. However, the base implementation provides a configuration for credit card issuers that you may want to modify.
Modify Workarea.config.credit_card_issuers
to change which credit card issuer icons are displayed in the checkout UI.
Since Workarea 3.5, this config is also used by the validation Payment::CreditCard#issuer_accepted
, which determines the credit card issuers that are accepted.
( See Configuration for coverage of Workarea configuration. )
Help Us Improve this Doc
Was this helpful? Open a GitHub issue to report a problem with this doc, suggest an improvement, or otherwise provide feedback. Thanks!