lib/active_merchant/billing/gateways/trust_commerce.rb in activemerchant-1.28.0 vs lib/active_merchant/billing/gateways/trust_commerce.rb in activemerchant-1.29.0
- old
+ new
@@ -6,11 +6,11 @@
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
# TO USE:
# First, make sure you have everything setup correctly and all of your dependencies in place with:
- #
+ #
# require 'rubygems'
# require 'active_merchant'
#
# ActiveMerchant expects amounts to be Integer values in cents
#
@@ -61,66 +61,66 @@
# Once it is installed, you should be able to make sure
# that it is visible to your ruby install by opening irb and typing "require 'tclink'", which should return "true".
#
# This should be enough to get you started with Trust Commerce and active_merchant. For further information, review the methods
# below and the rest of active_merchant's documentation, as well as Trust Commerce's user and developer documentation.
-
+
class TrustCommerceGateway < Gateway
self.live_url = self.test_url = 'https://vault.trustcommerce.com/trans/'
-
+
SUCCESS_TYPES = ["approved", "accepted"]
-
+
DECLINE_CODES = {
"decline" => "The credit card was declined",
"avs" => "AVS failed; the address entered does not match the billing address on file at the bank",
"cvv" => "CVV failed; the number provided is not the correct verification number for the card",
"call" => "The card must be authorized manually over the phone",
"expiredcard" => "Issuer was not certified for card verification",
"carderror" => "Card number is invalid",
"authexpired" => "Attempt to postauth an expired (more than 14 days old) preauth",
"fraud" => "CrediGuard fraud score was below requested threshold",
- "blacklist" => "CrediGuard blacklist value was triggered",
+ "blacklist" => "CrediGuard blacklist value was triggered",
"velocity" => "CrediGuard velocity control value was triggered",
"dailylimit" => "Daily limit in transaction count or amount as been reached",
"weeklylimit" => "Weekly limit in transaction count or amount as been reached",
"monthlylimit" => "Monthly limit in transaction count or amount as been reached"
}
-
+
BADDATA_CODES = {
"missingfields" => "One or more parameters required for this transaction type were not sent",
"extrafields" => "Parameters not allowed for this transaction type were sent",
"badformat" => "A field was improperly formatted, such as non-digit characters in a number field",
"badlength" => "A field was longer or shorter than the server allows",
"merchantcantaccept" => "The merchant can't accept data passed in this field",
"mismatch" => "Data in one of the offending fields did not cross-check with the other offending field"
}
-
+
ERROR_CODES = {
"cantconnect" => "Couldn't connect to the TrustCommerce gateway",
"dnsfailure" => "The TCLink software was unable to resolve DNS hostnames",
"linkfailure" => "The connection was established, but was severed before the transaction could complete",
"failtoprocess" => "The bank servers are offline and unable to authorize transactions"
}
-
+
TEST_LOGIN = 'TestMerchant'
TEST_PASSWORD = 'password'
-
+
self.money_format = :cents
self.supported_cardtypes = [:visa, :master, :discover, :american_express, :diners_club, :jcb]
self.supported_countries = ['US']
self.homepage_url = 'http://www.trustcommerce.com/'
self.display_name = 'TrustCommerce'
-
+
def self.tclink?
defined?(TCLink)
end
-
+
# Creates a new TrustCommerceGateway
- #
+ #
# The gateway requires that a valid login and password be passed
# in the +options+ hash.
- #
+ #
# ==== Options
#
# * <tt>:login</tt> -- The TrustCommerce account login.
# * <tt>:password</tt> -- The TrustCommerce account password.
# * <tt>:test => +true+ or +false+</tt> -- Perform test transactions
@@ -128,48 +128,46 @@
# ==== Test Account Credentials
# * <tt>:login</tt> -- TestMerchant
# * <tt>:password</tt> -- password
def initialize(options = {})
requires!(options, :login, :password)
-
- @options = options
+
super
end
-
+
def tclink?
self.class.tclink?
end
-
+
def test?
- @options[:login] == TEST_LOGIN &&
- @options[:password] == TEST_PASSWORD || @options[:test] || super
+ ((@options[:login] == TEST_LOGIN && @options[:password] == TEST_PASSWORD) || super)
end
-
+
# authorize() is the first half of the preauth(authorize)/postauth(capture) model. The TC API docs call this
# preauth, we preserve active_merchant's nomenclature of authorize() for consistency with the rest of the library. This
# method simply checks to make sure funds are available for a transaction, and returns a transid that can be used later to
# postauthorize (capture) the funds.
-
+
def authorize(money, creditcard_or_billing_id, options = {})
parameters = {
:amount => amount(money),
- }
-
+ }
+
add_order_id(parameters, options)
add_customer_data(parameters, options)
add_payment_source(parameters, creditcard_or_billing_id)
add_addresses(parameters, options)
commit('preauth', parameters)
end
-
+
# purchase() is a simple sale. This is one of the most common types of transactions, and is extremely simple. All that you need
# to process a purchase are an amount in cents or a money object and a creditcard object or billingid string.
- def purchase(money, creditcard_or_billing_id, options = {})
+ def purchase(money, creditcard_or_billing_id, options = {})
parameters = {
:amount => amount(money),
- }
-
+ }
+
add_order_id(parameters, options)
add_customer_data(parameters, options)
add_payment_source(parameters, creditcard_or_billing_id)
add_addresses(parameters, options)
commit('sale', parameters)
@@ -181,66 +179,66 @@
def capture(money, authorization, options = {})
parameters = {
:amount => amount(money),
:transid => authorization,
}
-
+
commit('postauth', parameters)
end
-
+
# refund() allows you to return money to a card that was previously billed. You need to supply the amount, in cents or a money object,
# that you want to refund, and a TC transid for the transaction that you are refunding.
- def refund(money, identification, options = {})
+ def refund(money, identification, options = {})
parameters = {
:amount => amount(money),
:transid => identification
}
-
+
commit('credit', parameters)
end
- def credit(money, identification, options = {})
+ def credit(money, identification, options = {})
deprecated CREDIT_DEPRECATION_MESSAGE
refund(money, identification, options)
end
-
+
# void() clears an existing authorization and releases the reserved fund
- # s back to the cardholder. The TC API refers to this transaction as a
- # reversal. After voiding, you will no longer be able to capture funds
- # from this authorization. TrustCommerce seems to always return a status
- # of "accepted" even if the transid you are trying to deauthorize has
- # already been captured. Note: Your account needs to be configured by
- # TrustCommerce to allow for reversal transactions before you can use this
- # method.
- #
- # NOTE: AMEX preauth's cannot be reversed. If you want to clear it more
- # quickly than the automatic expiration (7-10 days), you will have to
- # capture it and then immediately issue a credit for the same amount
+ # s back to the cardholder. The TC API refers to this transaction as a
+ # reversal. After voiding, you will no longer be able to capture funds
+ # from this authorization. TrustCommerce seems to always return a status
+ # of "accepted" even if the transid you are trying to deauthorize has
+ # already been captured. Note: Your account needs to be configured by
+ # TrustCommerce to allow for reversal transactions before you can use this
+ # method.
+ #
+ # NOTE: AMEX preauth's cannot be reversed. If you want to clear it more
+ # quickly than the automatic expiration (7-10 days), you will have to
+ # capture it and then immediately issue a credit for the same amount
# which should clear the customers credit card with 48 hours according to
# TC.
def void(authorization, options = {})
parameters = {
:transid => authorization,
}
-
+
commit('reversal', parameters)
end
-
+
# recurring() a TrustCommerce account that is activated for Citatdel, TrustCommerce's
# hosted customer billing info database.
#
# Recurring billing uses the same TC action as a plain-vanilla 'store', but we have a separate method for clarity. It can be called
# like store, with the addition of a required 'periodicity' parameter:
- #
+ #
# The parameter :periodicity should be specified as either :bimonthly, :monthly, :biweekly, :weekly, :yearly or :daily
#
# gateway.recurring(tendollar, creditcard, :periodicity => :weekly)
#
# You can optionally specify how long you want payments to continue using 'payments'
- def recurring(money, creditcard, options = {})
+ def recurring(money, creditcard, options = {})
requires!(options, [:periodicity, :bimonthly, :monthly, :biweekly, :weekly, :yearly, :daily] )
-
+
cycle = case options[:periodicity]
when :monthly
'1m'
when :bimonthly
'2m'
@@ -251,110 +249,110 @@
when :yearly
'1y'
when :daily
'1d'
end
-
+
parameters = {
:amount => amount(money),
:cycle => cycle,
:verify => options[:verify] || 'y',
:billingid => options[:billingid] || nil,
:payments => options[:payments] || nil,
}
-
+
add_creditcard(parameters, creditcard)
-
+
commit('store', parameters)
- end
-
+ end
+
# store() requires a TrustCommerce account that is activated for Citatdel. You can call it with a credit card and a billing ID
# you would like to use to reference the stored credit card info for future captures. Use 'verify' to specify whether you want
# to simply store the card in the DB, or you want TC to verify the data first.
-
- def store(creditcard, options = {})
+
+ def store(creditcard, options = {})
parameters = {
:verify => options[:verify] || 'y',
:billingid => options[:billingid] || options[:billing_id] || nil,
}
-
+
add_creditcard(parameters, creditcard)
- add_addresses(parameters, options)
+ add_addresses(parameters, options)
commit('store', parameters)
end
-
+
# To unstore a creditcard stored in Citadel using store() or recurring(), all that is required is the billing id. When you run
# unstore() the information will be removed and a Response object will be returned indicating the success of the action.
def unstore(identification, options = {})
parameters = {
:billingid => identification,
}
-
+
commit('unstore', parameters)
- end
-
+ end
+
private
def add_payment_source(params, source)
if source.is_a?(String)
add_billing_id(params, source)
else
add_creditcard(params, source)
end
end
-
+
def expdate(creditcard)
year = sprintf("%.4i", creditcard.year)
month = sprintf("%.2i", creditcard.month)
"#{month}#{year[-2..-1]}"
end
-
+
def add_creditcard(params, creditcard)
params[:media] = "cc"
params[:name] = creditcard.name
- params[:cc] = creditcard.number
+ params[:cc] = creditcard.number
params[:exp] = expdate(creditcard)
params[:cvv] = creditcard.verification_value if creditcard.verification_value?
end
-
+
def add_order_id(params, options)
params[:ticket] = options[:order_id] unless options[:order_id].blank?
end
-
+
def add_billing_id(params, billingid)
params[:billingid] = billingid
end
-
+
def add_customer_data(params, options)
params[:email] = options[:email] unless options[:email].blank?
params[:ip] = options[:ip] unless options[:ip].blank?
end
-
+
def add_addresses(params, options)
address = options[:billing_address] || options[:address]
-
- if address
+
+ if address
params[:address1] = address[:address1] unless address[:address1].blank?
params[:address2] = address[:address2] unless address[:address2].blank?
params[:city] = address[:city] unless address[:city].blank?
params[:state] = address[:state] unless address[:state].blank?
params[:zip] = address[:zip] unless address[:zip].blank?
params[:country] = address[:country] unless address[:country].blank?
params[:avs] = 'n'
end
-
+
if shipping_address = options[:shipping_address]
params[:shipto_name] = shipping_address[:name] unless shipping_address[:name].blank?
params[:shipto_address1] = shipping_address[:address1] unless shipping_address[:address1].blank?
params[:shipto_address2] = shipping_address[:address2] unless shipping_address[:address2].blank?
params[:shipto_city] = shipping_address[:city] unless shipping_address[:city].blank?
params[:shipto_state] = shipping_address[:state] unless shipping_address[:state].blank?
params[:shipto_zip] = shipping_address[:zip] unless shipping_address[:zip].blank?
params[:shipto_country] = shipping_address[:country] unless shipping_address[:country].blank?
end
end
-
+
def clean_and_stringify_params(parameters)
# TCLink wants us to send a hash with string keys, and activemerchant pushes everything around with
# symbol keys. Before sending our input to TCLink, we convert all our keys to strings and dump the symbol keys.
# We also remove any pairs with nil values, as these confuse TCLink.
parameters.keys.reverse.each do |key|
@@ -362,62 +360,62 @@
parameters[key.to_s] = parameters[key]
end
parameters.delete(key)
end
end
-
+
def post_data(parameters)
parameters.collect { |key, value| "#{key}=#{ CGI.escape(value.to_s)}" }.join("&")
end
-
+
def commit(action, parameters)
parameters[:custid] = @options[:login]
parameters[:password] = @options[:password]
parameters[:demo] = test? ? 'y' : 'n'
parameters[:action] = action
-
+
clean_and_stringify_params(parameters)
-
+
data = if tclink?
TCLink.send(parameters)
else
parse( ssl_post(self.live_url, post_data(parameters)) )
end
-
+
# to be considered successful, transaction status must be either "approved" or "accepted"
success = SUCCESS_TYPES.include?(data["status"])
message = message_from(data)
- Response.new(success, message, data,
- :test => test?,
+ Response.new(success, message, data,
+ :test => test?,
:authorization => data["transid"],
:cvv_result => data["cvv"],
:avs_result => { :code => data["avs"] }
)
end
-
+
def parse(body)
results = {}
-
+
body.split(/\n/).each do |pair|
key,val = pair.split(/=/)
results[key] = val
end
-
+
results
end
-
- def message_from(data)
+
+ def message_from(data)
status = case data["status"]
- when "decline"
+ when "decline"
return DECLINE_CODES[data["declinetype"]]
when "baddata"
return BADDATA_CODES[data["error"]]
when "error"
return ERROR_CODES[data["errortype"]]
else
return "The transaction was successful"
end
end
-
+
end
end
end