Skip to content

Commit

Permalink
Beanstream: Do not default state and zip with empty country
Browse files Browse the repository at this point in the history
We've see transaction failures on Beanstream when `ordProvince` and
`shipProvince` are provided without a country. According to Beanstream,
these values may be optional, but they codependent and if one is
included, then both must be present.

Closes activemerchant#2582
  • Loading branch information
dtykocki committed Sep 13, 2017
1 parent 242d77e commit f19c6cb
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* PayU Latam: Pass unique buyer fields and country requirements [curiousepic] #2570
* PayU Latam: Correctly condition buyer element fields [curiousepic] #2578
* Authorize.net: Remove numeric restriction on customer ID [dtykocki] #2579
* Beanstream: Do not default state and zip with empty country [dtykocki] #2582

== Version 1.71.0 (August 22, 2017)
* Bambora formerly Beanstream: Change casing on customerIp variable [aengusbates] #2551
Expand Down
12 changes: 10 additions & 2 deletions lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
module BeanstreamCore
include Empty

RECURRING_URL = 'https://www.beanstream.com/scripts/recurring_billing.asp'
SECURE_PROFILE_URL = 'https://www.beanstream.com/scripts/payment_profile.asp'

Expand Down Expand Up @@ -177,6 +179,7 @@ def credit(money, source, options = {})
end

private

def purchase_action(source)
if source.is_a?(Check)
:check_purchase
Expand Down Expand Up @@ -227,7 +230,7 @@ def add_address(post, options)
post[:ordAddress1] = billing_address[:address1]
post[:ordAddress2] = billing_address[:address2]
post[:ordCity] = billing_address[:city]
post[:ordProvince] = STATES[billing_address[:state].upcase] || billing_address[:state] if billing_address[:state]
post[:ordProvince] = state_for(billing_address)
post[:ordPostalCode] = billing_address[:zip]
post[:ordCountry] = billing_address[:country]
end
Expand All @@ -238,16 +241,21 @@ def add_address(post, options)
post[:shipAddress1] = shipping_address[:address1]
post[:shipAddress2] = shipping_address[:address2]
post[:shipCity] = shipping_address[:city]
post[:shipProvince] = STATES[shipping_address[:state].upcase] || shipping_address[:state] if shipping_address[:state]
post[:shipProvince] = state_for(shipping_address)
post[:shipPostalCode] = shipping_address[:zip]
post[:shipCountry] = shipping_address[:country]
post[:shippingMethod] = shipping_address[:shipping_method]
post[:deliveryEstimate] = shipping_address[:delivery_estimate]
end
end

def state_for(address)
STATES[address[:state].upcase] || address[:state] if address[:state]
end

def prepare_address_for_non_american_countries(options)
[ options[:billing_address], options[:shipping_address] ].compact.each do |address|
next if empty?(address[:country])
unless ['US', 'CA'].include?(address[:country])
address[:state] = '--'
address[:zip] = '000000' unless address[:zip]
Expand Down
18 changes: 17 additions & 1 deletion test/remote/gateways/remote_beanstream_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,15 @@ def test_successful_purchase_with_state_in_iso_format
assert_equal "Approved", response.message
end

def test_successful_purchase_with_no_addresses
@options[:billing_address] = {}
@options[:shipping_address] = {}
assert response = @gateway.purchase(@amount, @visa, @options)
assert_success response
assert_false response.authorization.blank?
assert_equal "Approved", response.message
end

def test_failed_purchase_due_to_invalid_billing_state
@options[:billing_address][:state] = "Invalid"
assert response = @gateway.purchase(@amount, @visa, @options)
Expand All @@ -123,6 +132,13 @@ def test_failed_purchase_due_to_invalid_shipping_state
assert_match %r{Invalid shipping province}, response.message
end

def test_failed_purchase_due_to_missing_country_with_state
@options[:shipping_address][:country] = nil
assert response = @gateway.purchase(@amount, @visa, @options)
assert_failure response
assert_match %r{Invalid shipping country id}, response.message
end

def test_authorize_and_capture
assert auth = @gateway.authorize(@amount, @visa, @options)
assert_success auth
Expand Down Expand Up @@ -222,7 +238,7 @@ def test_invalid_login
)
assert response = gateway.purchase(@amount, @visa, @options)
assert_failure response
assert_equal 'Invalid merchant id (merchant_id = 0)', response.message
assert_equal 'merchantid=Invalid merchant id (merchant_id = )', response.message
end

def test_successful_add_to_vault_with_store_method
Expand Down
34 changes: 32 additions & 2 deletions test/unit/gateways/beanstream_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -232,12 +232,42 @@ def test_includes_network_tokenization_fields
assert_success response
end

def test_defaults_state_and_zip_with_country
address = { country: "AF" }
@options[:billing_address] = address
@options[:shipping_address] = address
response = stub_comms(@gateway, :ssl_request) do
@gateway.purchase(@amount, @decrypted_credit_card, @options)
end.check_request do |method, endpoint, data, headers|
assert_match(/ordProvince=--/, data)
assert_match(/ordPostalCode=000000/, data)
assert_match(/shipProvince=--/, data)
assert_match(/shipPostalCode=000000/, data)
end.respond_with(successful_purchase_response)

assert_success response
end

def test_no_state_and_zip_default_with_missing_country
address = { }
@options[:billing_address] = address
@options[:shipping_address] = address
response = stub_comms(@gateway, :ssl_request) do
@gateway.purchase(@amount, @decrypted_credit_card, @options)
end.check_request do |method, endpoint, data, headers|
assert_no_match(/ordProvince=--/, data)
assert_no_match(/ordPostalCode=000000/, data)
assert_no_match(/shipProvince=--/, data)
assert_no_match(/shipPostalCode=000000/, data)
end.respond_with(successful_purchase_response)

assert_success response
end

def test_transcript_scrubbing
assert_equal scrubbed_transcript, @gateway.scrub(transcript)
end


private

def successful_purchase_response
Expand Down Expand Up @@ -266,7 +296,7 @@ def unsuccessful_authorize_response

def successful_void_response
"trnApproved=1&trnId=10100563&messageId=1&messageText=Approved&trnOrderNumber=6ca476d1a29da81a5f2d5d2c92ddeb&authCode=TEST&errorType=N&errorFields=&responseType=T&trnAmount=15%2E00&trnDate=9%2F9%2F2015+10%3A13%3A12+AM&avsProcessed=0&avsId=U&avsResult=0&avsAddrMatch=0&avsPostalMatch=0&avsMessage=Address+information+is+unavailable%2E&cvdId=2&cardType=VI&trnType=VP&paymentMethod=CC&ref1=reference+one&ref2=&ref3=&ref4=&ref5="
end
end

def unsuccessful_void_response
"trnApproved=0&trnId=0&messageId=0&messageText=%3CLI%3EAdjustment+id+must+be+less+than+8+characters%3Cbr%3E&trnOrderNumber=&authCode=&errorType=U&errorFields=adjId&responseType=T&trnAmount=&trnDate=9%2F9%2F2015+10%3A15%3A20+AM&avsProcessed=0&avsId=0&avsResult=0&avsAddrMatch=0&avsPostalMatch=0&avsMessage=Address+Verification+not+performed+for+this+transaction%2E&cardType=&trnType=VP&paymentMethod=CC&ref1=&ref2=&ref3=&ref4=&ref5="
Expand Down

0 comments on commit f19c6cb

Please sign in to comment.