lib/active_merchant/billing/gateways/ct_payment.rb
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class CtPaymentGateway < Gateway
self.test_url = 'https://test.ctpaiement.ca/v1/'
self.live_url = 'https://www.ctpaiement.com/v1/'
self.supported_countries = %w[US CA]
self.default_currency = 'CAD'
self.supported_cardtypes = %i[visa master american_express discover diners_club]
self.homepage_url = 'http://www.ct-payment.com/'
self.display_name = 'CT Payment'
STANDARD_ERROR_CODE_MAPPING = {
'14' => STANDARD_ERROR_CODE[:invalid_number],
'05' => STANDARD_ERROR_CODE[:card_declined],
'M6' => STANDARD_ERROR_CODE[:card_declined],
'9068' => STANDARD_ERROR_CODE[:incorrect_number],
'9067' => STANDARD_ERROR_CODE[:incorrect_number]
}
CARD_BRAND = {
'american_express' => 'A',
'master' => 'M',
'diners_club' => 'I',
'visa' => 'V',
'discover' => 'O'
}
def initialize(options = {})
requires!(options, :api_key, :company_number, :merchant_number)
super
end
def purchase(money, payment, options = {})
requires!(options, :order_id)
post = {}
add_terminal_number(post, options)
add_money(post, money)
add_operator_id(post, options)
add_invoice(post, money, options)
add_payment(post, payment)
add_address(post, payment, options)
add_customer_data(post, options)
payment.is_a?(String) ? commit('purchaseWithToken', post) : commit('purchase', post)
end
def authorize(money, payment, options = {})
requires!(options, :order_id)
post = {}
add_money(post, money)
add_terminal_number(post, options)
add_operator_id(post, options)
add_invoice(post, money, options)
add_payment(post, payment)
add_address(post, payment, options)
add_customer_data(post, options)
payment.is_a?(String) ? commit('preAuthorizationWithToken', post) : commit('preAuthorization', post)
end
def capture(money, authorization, options = {})
requires!(options, :order_id)
post = {}
add_invoice(post, money, options)
add_money(post, money)
add_customer_data(post, options)
transaction_number, authorization_number, invoice_number = split_authorization(authorization)
post[:OriginalTransactionNumber] = transaction_number
post[:OriginalAuthorizationNumber] = authorization_number
post[:OriginalInvoiceNumber] = invoice_number
commit('completion', post)
end
def refund(money, authorization, options = {})
requires!(options, :order_id)
post = {}
add_invoice(post, money, options)
add_money(post, money)
add_customer_data(post, options)
transaction_number, _, invoice_number = split_authorization(authorization)
post[:OriginalTransactionNumber] = transaction_number
post[:OriginalInvoiceNumber] = invoice_number
commit('refundWithoutCard', post)
end
def credit(money, payment, options = {})
requires!(options, :order_id)
post = {}
add_terminal_number(post, options)
add_money(post, money)
add_operator_id(post, options)
add_invoice(post, money, options)
add_payment(post, payment)
add_address(post, payment, options)
add_customer_data(post, options)
payment.is_a?(String) ? commit('refundWithToken', post) : commit('refund', post)
end
def void(authorization, options = {})
post = {}
post[:InputType] = 'I'
post[:LanguageCode] = 'E'
transaction_number, _, invoice_number = split_authorization(authorization)
post[:OriginalTransactionNumber] = transaction_number
post[:OriginalInvoiceNumber] = invoice_number
add_operator_id(post, options)
add_customer_data(post, options)
commit('void', post)
end
def verify(credit_card, options = {})
requires!(options, :order_id)
post = {}
add_terminal_number(post, options)
add_operator_id(post, options)
add_invoice(post, 0, options)
add_payment(post, credit_card)
add_address(post, credit_card, options)
add_customer_data(post, options)
commit('verifyAccount', post)
end
def store(credit_card, options = {})
requires!(options, :email)
post = {
LanguageCode: 'E',
Name: credit_card.name.rjust(50, ' '),
Email: options[:email].rjust(240, ' ')
}
add_operator_id(post, options)
add_payment(post, credit_card)
add_customer_data(post, options)
commit('recur/AddUser', post)
end
def supports_scrubbing?
true
end
def scrub(transcript)
transcript.
gsub(%r((&?auth-api-key=)[^&]*)i, '\1[FILTERED]').
gsub(%r((&?payload=)[a-zA-Z%0-9=]+)i, '\1[FILTERED]').
gsub(%r((&?token:)[^&]*)i, '\1[FILTERED]').
gsub(%r((&?cardNumber:)[^&]*)i, '\1[FILTERED]')
end
private
def add_terminal_number(post, options)
post[:MerchantTerminalNumber] = options[:merchant_terminal_number] || ' ' * 5
end
def add_money(post, money)
post[:Amount] = money.to_s.rjust(11, '0')
end
def add_operator_id(post, options)
post[:OperatorID] = options[:operator_id] || '0' * 8
end
def add_customer_data(post, options)
post[:CustomerNumber] = options[:customer_number] || '0' * 8
end
def add_address(post, creditcard, options)
if address = options[:billing_address] || options[:address]
post[:CardHolderAddress] = "#{address[:address1]} #{address[:address2]} #{address[:city]} #{address[:state]}".rjust(20, ' ')
post[:CardHolderPostalCode] = address[:zip].gsub(/\s+/, '').rjust(9, ' ')
end
end
def add_invoice(post, money, options)
post[:CurrencyCode] = options[:currency] || (currency(money) if money)
post[:InvoiceNumber] = options[:order_id].rjust(12, '0')
post[:InputType] = 'I'
post[:LanguageCode] = 'E'
end
def add_payment(post, payment)
if payment.is_a?(String)
post[:Token] = split_authorization(payment)[3].strip
else
post[:CardType] = CARD_BRAND[payment.brand] || ' '
post[:CardNumber] = payment.number.rjust(40, ' ')
post[:ExpirationDate] = expdate(payment)
post[:Cvv2Cvc2Number] = payment.verification_value
end
end
def parse(body)
JSON.parse(body)
end
def split_authorization(authorization)
authorization.split(';')
end
def commit_raw(action, parameters)
url = (test? ? test_url : live_url) + action
response = parse(ssl_post(url, post_data(action, parameters)))
Response.new(
success_from(response),
message_from(response),
response,
authorization: authorization_from(response),
avs_result: AVSResult.new(code: response['avsStatus']),
cvv_result: CVVResult.new(response['cvv2Cvc2Status']),
test: test?,
error_code: error_code_from(response)
)
end
def commit(action, parameters)
if action == 'void'
commit_raw(action, parameters)
else
MultiResponse.run(true) do |r|
r.process { commit_raw(action, parameters) }
r.process {
split_auth = split_authorization(r.authorization)
auth = (action.include?('recur') ? split_auth[4] : split_auth[0])
action.include?('recur') ? commit_raw('recur/ack', { ID: auth }) : commit_raw('ack', { TransactionNumber: auth })
}
end
end
end
def success_from(response)
return true if response['returnCode'] == ' 00'
return true if response['returnCode'] == 'true'
return true if response['recurReturnCode'] == ' 00'
return false
end
def message_from(response)
response['errorDescription'] || response['terminalDisp']&.strip
end
def authorization_from(response)
"#{response['transactionNumber']};#{response['authorizationNumber']};"\
"#{response['invoiceNumber']};#{response['token']};#{response['id']}"
end
def post_data(action, parameters = {})
parameters[:CompanyNumber] = @options[:company_number]
parameters[:MerchantNumber] = @options[:merchant_number]
parameters = parameters.collect do |key, value|
"#{key}=#{value}" unless value.nil? || value.empty?
end.join('&')
payload = Base64.strict_encode64(parameters)
"auth-api-key=#{@options[:api_key]}&payload=#{payload}".strip
end
def error_code_from(response)
STANDARD_ERROR_CODE_MAPPING[response['returnCode'].strip || response['recurReturnCode'.strip]] unless success_from(response)
end
end
end
end