lib/active_merchant/billing/gateways/trust_commerce.rb in activemerchant-1.2.1 vs lib/active_merchant/billing/gateways/trust_commerce.rb in activemerchant-1.3.0
- old
+ new
@@ -1,18 +1,13 @@
begin
require 'tclink'
rescue LoadError
- # Ignore, but we will fail hard if someone actually tries to use this gateway
+ # Falls back to an SSL post to TrustCommerce
end
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
-
- # To get started using TrustCommerce with active_merchant, download the tclink library from http://www.trustcommerce.com/tclink.html,
- # following the instructions available there to get it working on your system. 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".
- #
# TO USE:
# First, make sure you have everything setup correctly and all of your dependencies in place with:
#
# require 'rubygems'
# require 'active_merchant'
@@ -54,14 +49,26 @@
#
# 3) Retrieve and store the unique transaction ID returned by Trust Commerece, for use in referencing the transaction in the future.
#
# response.params["transid"]
#
+ # For higher performance and failover with the TrustCommerceGateway you can install the TCLink library from http://www.trustcommerce.com/tclink.html.
+ # Follow the instructions available there to get it working on your system. ActiveMerchant will automatically use tclink if available.
+ #
+ # The TCLink library has the following added benefits:
+ # * Good transaction times. Transaction duration under 1.2 seconds are common.
+ # * Fail-over to geographically distributed servers for extreme reliability
+ #
+ # 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
+ 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",
@@ -92,21 +99,20 @@
"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"
}
- # URL
- attr_reader :url
- attr_reader :response
- attr_reader :options
-
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.
#
@@ -124,12 +130,16 @@
@options = options
super
end
+ def tclink?
+ self.class.tclink?
+ end
+
def test?
- @options[:test] || Base.gateway_mode == :test
+ @options[:test] || 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
@@ -138,32 +148,34 @@
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_address(parameters, options)
+ 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 = {})
parameters = {
:amount => amount(money),
}
+ add_order_id(parameters, options)
+ add_customer_data(parameters, options)
add_payment_source(parameters, creditcard_or_billing_id)
- add_address(parameters, options)
+ add_addresses(parameters, options)
commit('sale', parameters)
end
# capture() is the second half of the preauth(authorize)/postauth(capture) model. The TC API docs call this
# postauth, we preserve active_merchant's nomenclature of capture() for consistency with the rest of the library. To process
# a postauthorization with TC, you need an amount in cents or a money object, and a TC transid.
-
def capture(money, authorization, options = {})
parameters = {
:amount => amount(money),
:transid => authorization,
}
@@ -171,20 +183,41 @@
commit('postauth', parameters)
end
# credit() 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 credit(money, identification, options = {})
parameters = {
:amount => amount(money),
:transid => identification
}
commit('credit', parameters)
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
+ # 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:
@@ -192,11 +225,10 @@
# 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 = {})
requires!(options, [:periodicity, :bimonthly, :monthly, :biweekly, :weekly, :yearly, :daily] )
cycle = case options[:periodicity]
when :monthly
@@ -235,11 +267,11 @@
:verify => options[:verify] || 'y',
:billingid => options[:billingid] || nil,
}
add_creditcard(parameters, creditcard)
- add_address(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.
@@ -273,26 +305,45 @@
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_address(params, options)
+ 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
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
+ 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.
@@ -302,38 +353,48 @@
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)
- test = test? || parameters[:test_request]
parameters[:custid] = @options[:login]
parameters[:password] = @options[:password]
- parameters[:demo] = test ? 'y' : 'n'
+ parameters[:demo] = test? ? 'y' : 'n'
parameters[:action] = action
- if result = test_result_from_cc_number(parameters[:cc])
- return result
+ clean_and_stringify_params(parameters)
+
+ data = if tclink?
+ TCLink.send(parameters)
+ else
+ parse( ssl_post(URL, post_data(parameters)) )
end
- begin
- clean_and_stringify_params(parameters)
+ # 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?,
+ :authorization => data["transid"],
+ :cvv_result => data["cvv"],
+ :avs_result => { :code => data["avs"] }
+ )
+ end
+
+ def parse(body)
+ results = {}
- data = TCLink.send(parameters)
- # 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, :authorization => data["transid"] )
- rescue NameError => e
- if e.message =~ /constant TCLink/
- raise 'Trust Commerce requires "tclink" library from http://www.trustcommerce.com/tclink.html'
- else
- raise
- end
+ body.split(/\n/).each do |pair|
+ key,val = pair.split(/=/)
+ results[key] = val
end
+ results
end
def message_from(data)
status = case data["status"]
when "decline"
\ No newline at end of file