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