require 'active_merchant/billing/gateways/beanstream/beanstream_core'
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
# This class implements the Canadian {Beanstream}[http://www.beanstream.com] payment gateway.
# It is also named TD Canada Trust Online Mart payment gateway.
# To learn more about the specification of Beanstream gateway, please read the OM_Direct_Interface_API.pdf,
# which you can get from your Beanstream account or get from me by email.
#
# == Supported transaction types by Beanstream:
# * +P+ - Purchase
# * +PA+ - Pre Authorization
# * +PAC+ - Pre Authorization Completion
#
# == Secure Payment Profiles:
# BeanStream supports payment profiles (vaults). This allows you to store cc information with BeanStream and process subsequent transactions with a customer id.
# Secure Payment Profiles must be enabled on your account (must be done over the phone).
# Your API Access Passcode must be set in Administration => account settings => order settings.
# To learn more about storing credit cards with the Beanstream gateway, documentation can be found at http://developer.beanstream.com/documentation/classic-apis
#
# To store a credit card using Beanstream's Legato Javascript Library (http://developer.beanstream.com/documentation/legato) you must pass the singleUseToken in
# the store method's option parameter. Example: @gateway.store("gt6-0c78c25b-3637-4ba0-90e2-26105287f198")
#
# == Notes
# * Adding of order products information is not implemented.
# * Ensure that country and province data is provided as a code such as "CA", "US", "QC".
# * login is the Beanstream merchant ID, username and password should be enabled in your Beanstream account and passed in using the :user and :password options.
# * Test your app with your true merchant id and test credit card information provided in the api pdf document.
# * Beanstream does not allow Payment Profiles to be deleted with their API. The accounts are 'closed', but have to be deleted manually.
#
# Example authorization (Beanstream PA transaction type):
#
# twenty = 2000
# gateway = BeanstreamGateway.new(
# :login => '100200000',
# :user => 'xiaobozz',
# :password => 'password'
# )
#
# credit_card = CreditCard.new(
# :number => '4030000010001234',
# :month => 8,
# :year => 2011,
# :first_name => 'xiaobo',
# :last_name => 'zzz',
# :verification_value => 137
# )
# response = gateway.authorize(twenty, credit_card,
# :order_id => '1234',
# :billing_address => {
# :name => 'xiaobo zzz',
# :phone => '555-555-5555',
# :address1 => '1234 Levesque St.',
# :address2 => 'Apt B',
# :city => 'Montreal',
# :state => 'QC',
# :country => 'CA',
# :zip => 'H2C1X8'
# },
# :email => 'xiaobozzz@example.com',
# :subtotal => 800,
# :shipping => 100,
# :tax1 => 100,
# :tax2 => 100,
# :custom => 'reference one'
# )
class BeanstreamGateway < Gateway
include BeanstreamCore
def authorize(money, source, options = {})
post = {}
add_amount(post, money)
add_invoice(post, options)
add_source(post, source)
add_address(post, options)
add_transaction_type(post, :authorization)
add_customer_ip(post, options)
add_recurring_payment(post, options)
commit(post)
end
def purchase(money, source, options = {})
post = {}
add_amount(post, money)
add_invoice(post, options)
add_source(post, source)
add_address(post, options)
add_transaction_type(post, purchase_action(source))
add_customer_ip(post, options)
add_recurring_payment(post, options)
commit(post)
end
def void(authorization, options = {})
reference, amount, type = split_auth(authorization)
if type == TRANSACTIONS[:authorization]
capture(0, authorization, options)
else
post = {}
add_reference(post, reference)
add_original_amount(post, amount)
add_transaction_type(post, void_action(type))
commit(post)
end
end
def verify(source, options={})
MultiResponse.run(:use_first_response) do |r|
r.process { authorize(100, source, options) }
r.process(:ignore_result) { void(r.authorization, options) }
end
end
def success?(response)
response[:trnApproved] == '1' || response[:responseCode] == '1'
end
def recurring(money, source, options = {})
ActiveMerchant.deprecated RECURRING_DEPRECATION_MESSAGE
post = {}
add_amount(post, money)
add_invoice(post, options)
add_credit_card(post, source)
add_address(post, options)
add_transaction_type(post, purchase_action(source))
add_recurring_type(post, options)
commit(post)
end
def update_recurring(amount, source, options = {})
ActiveMerchant.deprecated RECURRING_DEPRECATION_MESSAGE
post = {}
add_recurring_amount(post, amount)
add_recurring_invoice(post, options)
add_credit_card(post, source)
add_address(post, options)
add_recurring_operation_type(post, :update)
add_recurring_service(post, options)
recurring_commit(post)
end
def cancel_recurring(options = {})
ActiveMerchant.deprecated RECURRING_DEPRECATION_MESSAGE
post = {}
add_recurring_operation_type(post, :cancel)
add_recurring_service(post, options)
recurring_commit(post)
end
def interac
@interac ||= BeanstreamInteracGateway.new(@options)
end
# To match the other stored-value gateways, like TrustCommerce,
# store and unstore need to be defined
#
# When passing a single-use token the :name option is required
def store(payment_method, options = {})
post = {}
add_address(post, options)
if payment_method.respond_to?(:number)
add_credit_card(post, payment_method)
else
post[:singleUseToken] = payment_method
end
add_secure_profile_variables(post, options)
commit(post, true)
end
# can't actually delete a secure profile with the supplicated API. This function sets the status of the profile to closed (C).
# Closed profiles will have to removed manually.
def delete(vault_id)
update(vault_id, false, {status: 'C'})
end
alias_method :unstore, :delete
# Update the values (such as CC expiration) stored at
# the gateway. The CC number must be supplied in the
# CreditCard object.
def update(vault_id, payment_method, options = {})
post = {}
add_address(post, options)
if payment_method.respond_to?(:number)
add_credit_card(post, payment_method)
else
post[:singleUseToken] = payment_method
end
options[:vault_id] = vault_id
options[:operation] = secure_profile_action(:modify)
add_secure_profile_variables(post, options)
commit(post, true)
end
def supports_scrubbing?
true
end
def scrub(transcript)
transcript.
gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
gsub(/(&?password=)[^&\s]*(&?)/, '\1[FILTERED]\2').
gsub(/(&?passcode=)[^&\s]*(&?)/, '\1[FILTERED]\2').
gsub(/(&?trnCardCvd=)\d*(&?)/, '\1[FILTERED]\2').
gsub(/(&?trnCardNumber=)\d*(&?)/, '\1[FILTERED]\2')
end
private
def build_response(*args)
Response.new(*args)
end
end
end
end