Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

2.10.2

  • Loading branch information...
commit 86dad7831981079f30d6c6c19d9ce1a82914062d 1 parent e2f9950
@braintreeps braintreeps authored
View
4 CHANGELOG.rdoc
@@ -1,3 +1,7 @@
+== 2.10.2
+
+* Added support for international Maestro cards with payer authentication
+
== 2.10.1
* Support builder >= 2.0
View
2  lib/braintree.rb
@@ -41,6 +41,8 @@ module Braintree
require File.dirname(__FILE__) + "/braintree/errors"
require File.dirname(__FILE__) + "/braintree/gateway"
require File.dirname(__FILE__) + "/braintree/http"
+require File.dirname(__FILE__) + "/braintree/payer_authentication"
+require File.dirname(__FILE__) + "/braintree/payer_authentication_gateway"
require File.dirname(__FILE__) + "/braintree/resource_collection"
require File.dirname(__FILE__) + "/braintree/subscription"
require File.dirname(__FILE__) + "/braintree/subscription_gateway"
View
2  lib/braintree/error_codes.rb
@@ -184,6 +184,8 @@ module Transaction
TaxAmountIsTooLarge = "81536"
TypeIsInvalid = "91523"
TypeIsRequired = "91524"
+ UnsupportedVoiceAuthorization = "91539"
+
module Options
VaultIsDisabled = "91525"
end
View
6 lib/braintree/error_result.rb
@@ -3,12 +3,14 @@ module Braintree
class ErrorResult
attr_reader :credit_card_verification, :transaction, :subscription, :errors, :params, :message
+ attr_reader :payer_authentication
def initialize(gateway, data) # :nodoc:
@gateway = gateway
@params = data[:params]
@credit_card_verification = CreditCardVerification._new(data[:verification]) if data[:verification]
@message = data[:message]
+ @payer_authentication = PayerAuthentication._new(gateway, data[:payer_authentication]) if data[:payer_authentication]
@transaction = Transaction._new(gateway, data[:transaction]) if data[:transaction]
@subscription = Subscription._new(gateway, data[:subscription]) if data[:subscription]
@errors = Errors.new(data[:errors])
@@ -24,6 +26,10 @@ def inspect # :nodoc:
"#<#{self.class} params:{...} errors:<#{@errors._inner_inspect}>#{verification_inspect}#{transaction_inspect}>"
end
+ def payer_authentication_required?
+ !!@payer_authentication
+ end
+
# Always returns false.
def success?
false
View
4 lib/braintree/gateway.rb
@@ -24,6 +24,10 @@ def customer
CustomerGateway.new(self)
end
+ def payer_authentication
+ PayerAuthenticationGateway.new(self)
+ end
+
def subscription
SubscriptionGateway.new(self)
end
View
31 lib/braintree/payer_authentication.rb
@@ -0,0 +1,31 @@
+module Braintree
+ class PayerAuthentication
+ include BaseModule # :nodoc:
+
+ attr_reader :id, :post_params, :post_url
+
+ def self.authenticate(payer_authentication_id, response_payload)
+ Configuration.gateway.payer_authentication.authenticate(
+ payer_authentication_id,
+ response_payload
+ )
+ end
+
+ def initialize(gateway, attributes) # :nodoc:
+ @gateway = gateway
+ set_instance_variables_from_hash(attributes)
+
+ @post_params = (@post_params || []).map do |params|
+ OpenStruct.new(:name => params[:name], :value => params[:value])
+ end
+ end
+
+ class << self
+ protected :new
+ end
+
+ def self._new(*args) # :nodoc:
+ self.new *args
+ end
+ end
+end
View
25 lib/braintree/payer_authentication_gateway.rb
@@ -0,0 +1,25 @@
+module Braintree
+ class PayerAuthenticationGateway # :nodoc:
+ def initialize(gateway)
+ @gateway = gateway
+ @config = gateway.config
+ end
+
+ def authenticate(payer_authentication_id, response_payload)
+ response = @config.http.post(
+ "/payer_authentications/#{payer_authentication_id}/authenticate",
+ :payer_authentication => {
+ :response_payload => response_payload
+ }
+ )
+
+ if response[:transaction]
+ SuccessfulResult.new(:transaction => Transaction._new(@gateway, response[:transaction]))
+ elsif response[:api_error_response]
+ ErrorResult.new(@gateway, response[:api_error_response])
+ else
+ raise UnexpectedError, "expected :transaction or :api_error_response"
+ end
+ end
+ end
+end
View
5 lib/braintree/successful_result.rb
@@ -19,7 +19,10 @@ def inspect # :nodoc:
"#<#{self.class} #{inspected_attributes.join(" ")}>"
end
- # Always returns true.
+ def payer_authentication_required?
+ false
+ end
+
def success?
true
end
View
30 lib/braintree/test/credit_card_numbers.rb
@@ -6,20 +6,14 @@ module Test # :nodoc:
#
# See http://www.braintreepayments.com/docs/ruby/reference/sandbox
module CreditCardNumbers
- AmExes = %w[
- 378282246310005
- 371449635398431
- 378734493671000
- ]
+ AmExes = %w[378282246310005 371449635398431 378734493671000]
CarteBlanches = %w[30569309025904] # :nodoc:
DinersClubs = %w[38520000023237] # :nodoc:
- Discovers = %w[
- 6011111111111117
- 6011000990139424
- ]
+ Discovers = %w[6011111111111117 6011000990139424]
JCBs = %w[3530111333300000 3566002020360505] # :nodoc:
+ Maestro = "6304000000000000" # :nodoc:
MasterCard = "5555555555554444"
MasterCardInternational = "5105105105105100" # :nodoc:
@@ -28,15 +22,15 @@ module CreditCardNumbers
Visa = "4012888888881881"
VisaInternational = "4009348888881881" # :nodoc:
- Visas = %w[
- 4009348888881881
- 4012888888881881
- 4111111111111111
- 4000111111111115
- ]
- Unknowns = %w[
- 1000000000000008
- ]
+ Visas = %w[4009348888881881 4012888888881881 4111111111111111 4000111111111115]
+ Unknowns = %w[1000000000000008]
+
+ module PayerAuthentication
+ ValidMaestro = "6304000000000000"
+ InvalidMaestro = "6773900000000000007"
+ AuthenticationSuccessfulPayload = "authentication_successful_payload"
+ AuthenticationFailedPayload = "authentication_failed_payload"
+ end
module FailsSandboxVerification
AmEx = "378734493671000"
View
2  lib/braintree/version.rb
@@ -2,7 +2,7 @@ module Braintree
module Version
Major = 2
Minor = 10
- Tiny = 1
+ Tiny = 2
String = "#{Major}.#{Minor}.#{Tiny}"
end
View
6 spec/integration/braintree/credit_card_spec.rb
@@ -40,7 +40,7 @@
end
it "can specify the desired token" do
- token = "token_#{rand(1_000_000)}"
+ token = "token_#{rand(10**10)}"
customer = Braintree::Customer.create!
result = Braintree::CreditCard.create(
:customer_id => customer.id,
@@ -673,8 +673,8 @@
describe "self.update_from_transparent_redirect" do
it "updates the credit card" do
- old_token = "token#{rand(1_000_000)}"
- new_token = "token#{rand(1_000_000)}"
+ old_token = "token_#{rand(10**10)}"
+ new_token = "token_#{rand(10**10)}"
customer = Braintree::Customer.create!(
:credit_card => {
:cardholder_name => "Old Cardholder Name",
View
2  spec/integration/braintree/customer_search_spec.rb
@@ -11,7 +11,7 @@
end
it "can search on text fields" do
- cctoken = "cctoken#{rand(1_000_000)}"
+ cctoken = "cctoken_#{rand(10**10)}"
customer = Braintree::Customer.create!(
:first_name => "Timmy",
:last_name => "O'Toole",
View
6 spec/integration/braintree/transaction_search_spec.rb
@@ -11,9 +11,9 @@
end
it "can search on text fields" do
- first_name = "Tim#{rand(10000)}"
- token = "creditcard#{rand(10000)}"
- customer_id = "customer#{rand(10000)}"
+ first_name = "Tim_#{rand(10**10)}"
+ token = "creditcard_#{rand(10**10)}"
+ customer_id = "customer_#{rand(10**10)}"
transaction = Braintree::Transaction.sale!(
:amount => Braintree::Test::TransactionAmounts::Authorize,
View
104 spec/integration/braintree/transaction_spec.rb
@@ -152,6 +152,106 @@
codes.should include(Braintree::ErrorCodes::Address::CountryCodeNumericIsNotAccepted)
end
+ context "maestro authentication" do
+ it "returns an authentication response on successful lookup" do
+ result = Braintree::Transaction.create(
+ :type => "sale",
+ :amount => Braintree::Test::TransactionAmounts::Authorize,
+ :merchant_account_id => "secure_code_ma",
+ :credit_card => {
+ :number => Braintree::Test::CreditCardNumbers::PayerAuthentication::ValidMaestro,
+ :expiration_date => "01/2012"
+ }
+ )
+
+ result.success?.should == false
+ result.payer_authentication_required?.should == true
+
+ payer_authentication = result.payer_authentication
+ payer_authentication.id.should match(/\A[a-z0-9]+\z/)
+ payer_authentication.post_url.should match(%r{\Ahttps?://})
+ payer_authentication.post_params.size.should == 1
+ payer_authentication.post_params.first.name.should == "PaReq"
+ payer_authentication.post_params.first.value.should_not be_empty
+
+ result = Braintree::PayerAuthentication.authenticate(
+ payer_authentication.id,
+ Braintree::Test::CreditCardNumbers::PayerAuthentication::AuthenticationSuccessfulPayload
+ )
+
+ result.success?.should == true
+ result.transaction.id.should =~ /^\w{6}$/
+ result.transaction.type.should == "sale"
+ result.transaction.amount.should == BigDecimal.new(Braintree::Test::TransactionAmounts::Authorize)
+ result.transaction.processor_authorization_code.should_not be_nil
+ result.transaction.credit_card_details.bin.should == Braintree::Test::CreditCardNumbers::Maestro[0, 6]
+ result.transaction.credit_card_details.last_4.should == Braintree::Test::CreditCardNumbers::Maestro[-4..-1]
+ result.transaction.credit_card_details.expiration_date.should == "01/2012"
+ result.transaction.credit_card_details.customer_location.should == "US"
+ end
+
+ it "attempts to create the transaction on an unsuccessful authentication" do
+ result = Braintree::Transaction.create(
+ :type => "sale",
+ :amount => Braintree::Test::TransactionAmounts::Authorize,
+ :merchant_account_id => "secure_code_ma",
+ :credit_card => {
+ :number => Braintree::Test::CreditCardNumbers::PayerAuthentication::ValidMaestro,
+ :expiration_date => "01/2012"
+ }
+ )
+
+ result.success?.should == false
+ result.payer_authentication_required?.should == true
+
+ payer_authentication = result.payer_authentication
+ payer_authentication.id.should match(/\A[a-z0-9]+\z/)
+ payer_authentication.post_url.should match(%r{\Ahttps?://})
+ payer_authentication.post_params.size.should == 1
+ payer_authentication.post_params.first.name.should == "PaReq"
+ payer_authentication.post_params.first.value.should_not be_empty
+
+ result = Braintree::PayerAuthentication.authenticate(
+ payer_authentication.id,
+ Braintree::Test::CreditCardNumbers::PayerAuthentication::AuthenticationFailedPayload
+ )
+
+ result.success?.should == true
+ result.transaction.id.should =~ /^\w{6}$/
+ result.transaction.type.should == "sale"
+ result.transaction.amount.should == BigDecimal.new(Braintree::Test::TransactionAmounts::Authorize)
+ result.transaction.processor_authorization_code.should_not be_nil
+ result.transaction.credit_card_details.bin.should == Braintree::Test::CreditCardNumbers::Maestro[0, 6]
+ result.transaction.credit_card_details.last_4.should == Braintree::Test::CreditCardNumbers::Maestro[-4..-1]
+ result.transaction.credit_card_details.expiration_date.should == "01/2012"
+ result.transaction.credit_card_details.customer_location.should == "US"
+ end
+
+ it "runs a regular transaction on unsuccessful lookup" do
+ cc_number = Braintree::Test::CreditCardNumbers::PayerAuthentication::InvalidMaestro
+ result = Braintree::Transaction.create(
+ :type => "sale",
+ :amount => Braintree::Test::TransactionAmounts::Authorize,
+ :merchant_account_id => "secure_code_ma",
+ :credit_card => {
+ :number => cc_number,
+ :expiration_date => "01/2012"
+ }
+ )
+
+ result.payer_authentication_required?.should == false
+ result.success?.should == true
+ result.transaction.id.should =~ /^\w{6}$/
+ result.transaction.type.should == "sale"
+ result.transaction.amount.should == BigDecimal.new(Braintree::Test::TransactionAmounts::Authorize)
+ result.transaction.processor_authorization_code.should_not be_nil
+ result.transaction.credit_card_details.bin.should == cc_number[0, 6]
+ result.transaction.credit_card_details.last_4.should == cc_number[-4..-1]
+ result.transaction.credit_card_details.expiration_date.should == "01/2012"
+ result.transaction.credit_card_details.customer_location.should == "US"
+ end
+ end
+
context "gateway rejection reason" do
it "exposes the cvv gateway rejection reason" do
old_merchant = Braintree::Configuration.merchant_id
@@ -1042,8 +1142,8 @@
end
it "can specify the customer id and payment method token" do
- customer_id = "customer_#{rand(1000000)}"
- payment_mehtod_token = "credit_card_#{rand(1000000)}"
+ customer_id = "customer_#{rand(10**10)}"
+ payment_mehtod_token = "credit_card_#{rand(10**10)}"
result = Braintree::Transaction.sale(
:amount => "100",
:customer => {
View
13 spec/unit/braintree/error_result_spec.rb
@@ -74,4 +74,17 @@
result.inspect.should_not include("transaction")
end
end
+
+ describe "payer_authentication_required?" do
+ it "is true if payer_authentication is in the response" do
+ result = Braintree::ErrorResult.new(
+ :gateway,
+ :payer_authentication => {:id => "public_id", :post_url => "post_url", :post_params => []},
+ :errors => {},
+ :verification => nil,
+ :transaction => nil
+ )
+ result.payer_authentication_required?.should be_true
+ end
+ end
end
View
38 spec/unit/braintree/payer_authentication_spec.rb
@@ -0,0 +1,38 @@
+require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
+
+describe Braintree::PayerAuthentication do
+ describe "id" do
+ it "returns the given id" do
+ payer_authentication = Braintree::PayerAuthentication._new(
+ :gateway,
+ :id => :test_id
+ )
+
+ payer_authentication.id.should == :test_id
+ end
+ end
+
+ describe "post_params" do
+ it "returns the given post params" do
+ payer_authentication = Braintree::PayerAuthentication._new(
+ :gateway,
+ :post_params => [{:name => 'imaname', :value => 'andimavalue'}]
+ )
+
+ post_param = payer_authentication.post_params.first
+ post_param.name.should == 'imaname'
+ post_param.value.should == 'andimavalue'
+ end
+ end
+
+ describe "post_url" do
+ it "returns the given post url" do
+ payer_authentication = Braintree::PayerAuthentication._new(
+ :gateway,
+ :post_url => "http://example.com"
+ )
+
+ payer_authentication.post_url.should == "http://example.com"
+ end
+ end
+end
View
6 spec/unit/braintree/successful_result_spec.rb
@@ -24,4 +24,10 @@
result.inspect.should == "#<Braintree::SuccessfulResult foo:\"foo_value\">"
end
end
+
+ describe "payer_authentication_required?" do
+ it "is always false" do
+ Braintree::SuccessfulResult.new.payer_authentication_required?.should be_false
+ end
+ end
end
Please sign in to comment.
Something went wrong with that request. Please try again.