lib/active_merchant/billing/gateways/brain_tree.rb in activemerchant-1.2.1 vs lib/active_merchant/billing/gateways/brain_tree.rb in activemerchant-1.3.0

- old
+ new

@@ -1,82 +1,82 @@ +require File.join(File.dirname(__FILE__), '..', 'check.rb') + module ActiveMerchant #:nodoc: module Billing #:nodoc: class BrainTreeGateway < Gateway URL = 'https://secure.braintreepaymentgateway.com/api/transact.php' - attr_reader :url - attr_reader :response - attr_reader :options - + self.supported_countries = ['US'] self.supported_cardtypes = [:visa, :master, :american_express] self.homepage_url = 'http://www.braintreepaymentsolutions.com' self.display_name = 'Braintree' - AVS_MESSAGES = { - "X" => "Exact match, 9-character numeric ZIP", - "Y" => "Exact match, 5-character numeric ZIP", - "D" => "Exact match, 5-character numeric ZIP", - "M" => "Exact match, 5-character numeric ZIP", - "A" => "Address match only", - "B" => "Address match only", - "W" => "9-character numeric ZIP match only", - "Z" => "5-character Zip match only", - "P" => "5-character Zip match only", - "L" => "5-character Zip match only", - "N" => "No address or ZIP match", - "C" => "No address or ZIP match", - "U" => "Address unavailable", - "G" => "Non-U.S. Issuer does not participate", - "I" => "Non-U.S. Issuer does not participate", - "R" => "Issuer system unavailable", - "E" => "Not a mail/phone order", - "S" => "Service not supported", - "0" => "AVS Not Available", - "O" => "AVS Not Available", - "B" => "AVS Not Available" - } - - CARD_CODE_MESSAGES = { - "M" => "CVV2/CVC2 Match", - "N" => "CVV2/CVC2 No Match", - "P" => "Not Processed", - "S" => "Merchant has indicated that CVV2/CVC2 is not present on card", - "U" => "Issuer is not certified and/or has not provided Visa encryption keys" - } - def initialize(options = {}) requires!(options, :login, :password) @options = options super end + # Pass :store => true in the options to store the + # payment info at BrainTree and get a generated + # customer_vault_id in the response. + # Pass :store => some_number_or_string to specify the + # customer_vault_id BrainTree should use (make sure it's + # unique). def authorize(money, creditcard, options = {}) post = {} add_invoice(post, options) add_payment_source(post, creditcard,options) add_address(post, creditcard, options) add_customer_data(post, options) commit('auth', money, post) end - def purchase(money, creditcard, options = {}) + def purchase(money, payment_source, options = {}) post = {} add_invoice(post, options) - add_payment_source(post, creditcard,options) - add_address(post, creditcard, options) + add_payment_source(post, payment_source, options) + add_address(post, payment_source, options) add_customer_data(post, options) commit('sale', money, post) end def capture(money, authorization, options = {}) post ={} post[:transactionid] = authorization commit('capture', money, post) end + + def void(authorization, options = {}) + post ={} + post[:transactionid] = authorization + commit('void', nil, post) + end + + # Update the values (such as CC expiration) stored at + # BrainTree. The CC number must be supplied in the + # CreditCard object. + def update(vault_id, creditcard, options = {}) + post = {} + post[:customer_vault] = "update_customer" + add_customer_vault_id(post, vault_id) + add_creditcard(post, creditcard, options) + add_address(post, creditcard, options) + add_customer_data(post, options) + + commit(nil, nil, post) + end + def delete(vault_id) + post = {} + post[:customer_vault] = "delete_customer" + add_customer_vault_id(post, vault_id) + commit(nil, nil, post) + end + private def add_customer_data(post, options) if options.has_key? :email post[:email] = options[:email] end @@ -101,58 +101,63 @@ def add_invoice(post, options) post[:orderid] = options[:order_id].to_s.gsub(/[^\w.]/, '') end - def add_payment_source(params, source,options) - if source.is_a?(String) - add_customer_vault_id(params, source) - else - add_creditcard(params, source,options) + def add_payment_source(params, source, options={}) + case determine_funding_source(source) + when :vault then add_customer_vault_id(params, source) + when :credit_card then add_creditcard(params, source, options) + when :check then add_check(params, source) end end def add_customer_vault_id(params,vault_id) params[:customer_vault_id] = vault_id end - def add_creditcard(post, creditcard,options) - post[:customer_vault] = "add_customer" if options[:store] - + def add_creditcard(post, creditcard,options) + if options[:store] + post[:customer_vault] = "add_customer" + post[:customer_vault_id] = options[:store] unless options[:store] == true + end post[:ccnumber] = creditcard.number post[:cvv] = creditcard.verification_value if creditcard.verification_value? post[:ccexp] = expdate(creditcard) post[:firstname] = creditcard.first_name post[:lastname] = creditcard.last_name end + def add_check(post, check) + post[:payment] = 'check' # Set transaction to ACH + post[:checkname] = check.name # The name on the customer's Checking Account + post[:checkaba] = check.routing_number # The customer's bank routing number + post[:checkaccount] = check.account_number # The customer's account number + post[:account_holder_type] = check.account_holder_type # The customer's type of ACH account + post[:account_type] = check.account_type # The customer's type of ACH account + end + def parse(body) results = {} body.split(/&/).each do |pair| key,val = pair.split(/=/) results[key] = val end - results[:card_code_message] = CARD_CODE_MESSAGES[results[:cvvresponse]] if results[:cvvresponse] - results[:avs_message] = AVS_MESSAGES[results["avsresponse"]] if results["avsresponse"] - results + results end def commit(action, money, parameters) parameters[:amount] = amount(money) if money - if result = test_result_from_cc_number(parameters[:ccnumber]) - return result - end - - data = ssl_post URL, post_data(action,parameters) + response = parse( ssl_post(URL, post_data(action,parameters)) ) - @response = parse(data) - - Response.new(@response["response"]=="1", message_from(@response), @response, - :authorization => @response["transactionid"], - :test => test? + Response.new(response["response"] == "1", message_from(response), response, + :authorization => response["transactionid"], + :test => test?, + :cvv_result => response["cvvresponse"], + :avs_result => { :code => response["avsresponse"] } ) end def expdate(creditcard) @@ -162,30 +167,37 @@ "#{month}#{year[-2..-1]}" end def message_from(response) - r=response["responsetext"] - case r - when "SUCCESS","Approved" - "This transaction has been approved" - when "DECLINE" - "This transaction has been declined" - else - r - end + case response["responsetext"] + when "SUCCESS","Approved" + "This transaction has been approved" + when "DECLINE" + "This transaction has been declined" + else + response["responsetext"] + end end def post_data(action, parameters = {}) post = {} post[:username] = @options[:login] post[:password] = @options[:password] - post[:type] = action + post[:type] = action if action request = post.merge(parameters).map {|key,value| "#{key}=#{CGI.escape(value.to_s)}"}.join("&") request end + def determine_funding_source(source) + case + when source.is_a?(String) then :vault + when CreditCard.card_companies.keys.include?(source.type) then :credit_card + when source.type == 'check' then :check + else raise ArgumentError, "Unsupported funding source provided" + end + end end end end