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