activemerchant 1.125.0 → 1.126.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +75 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +12 -0
- data/lib/active_merchant/billing/gateway.rb +2 -1
- data/lib/active_merchant/billing/gateways/adyen.rb +7 -4
- data/lib/active_merchant/billing/gateways/airwallex.rb +341 -0
- data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +2 -1
- data/lib/active_merchant/billing/gateways/blue_pay.rb +1 -1
- data/lib/active_merchant/billing/gateways/blue_snap.rb +31 -21
- data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +6 -1
- data/lib/active_merchant/billing/gateways/braintree/token_nonce.rb +113 -0
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +87 -15
- data/lib/active_merchant/billing/gateways/card_connect.rb +1 -1
- data/lib/active_merchant/billing/gateways/checkout_v2.rb +1 -1
- data/lib/active_merchant/billing/gateways/credorax.rb +10 -0
- data/lib/active_merchant/billing/gateways/cyber_source.rb +13 -33
- data/lib/active_merchant/billing/gateways/d_local.rb +49 -0
- data/lib/active_merchant/billing/gateways/decidir.rb +17 -1
- data/lib/active_merchant/billing/gateways/decidir_plus.rb +185 -14
- data/lib/active_merchant/billing/gateways/ebanx.rb +3 -2
- data/lib/active_merchant/billing/gateways/global_collect.rb +26 -16
- data/lib/active_merchant/billing/gateways/ipg.rb +1 -2
- data/lib/active_merchant/billing/gateways/litle.rb +93 -1
- data/lib/active_merchant/billing/gateways/moneris.rb +35 -8
- data/lib/active_merchant/billing/gateways/nmi.rb +12 -7
- data/lib/active_merchant/billing/gateways/orbital.rb +349 -327
- data/lib/active_merchant/billing/gateways/payflow.rb +62 -0
- data/lib/active_merchant/billing/gateways/paymentez.rb +26 -7
- data/lib/active_merchant/billing/gateways/paysafe.rb +15 -15
- data/lib/active_merchant/billing/gateways/payu_latam.rb +25 -15
- data/lib/active_merchant/billing/gateways/priority.rb +158 -136
- data/lib/active_merchant/billing/gateways/rapyd.rb +258 -0
- data/lib/active_merchant/billing/gateways/safe_charge.rb +1 -4
- data/lib/active_merchant/billing/gateways/simetrik.rb +362 -0
- data/lib/active_merchant/billing/gateways/stripe.rb +4 -2
- data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +93 -48
- data/lib/active_merchant/billing/gateways/visanet_peru.rb +6 -2
- data/lib/active_merchant/version.rb +1 -1
- metadata +6 -2
@@ -78,7 +78,7 @@ module ActiveMerchant
|
|
78
78
|
def purchase(money, payment_method, options = {})
|
79
79
|
payment_method_details = PaymentMethodDetails.new(payment_method)
|
80
80
|
|
81
|
-
commit(:purchase, :post, payment_method_details) do |doc|
|
81
|
+
commit(:purchase, options, :post, payment_method_details) do |doc|
|
82
82
|
if payment_method_details.alt_transaction?
|
83
83
|
add_alt_transaction_purchase(doc, money, payment_method_details, options)
|
84
84
|
else
|
@@ -88,13 +88,13 @@ module ActiveMerchant
|
|
88
88
|
end
|
89
89
|
|
90
90
|
def authorize(money, payment_method, options = {})
|
91
|
-
commit(:authorize) do |doc|
|
91
|
+
commit(:authorize, options) do |doc|
|
92
92
|
add_auth_purchase(doc, money, payment_method, options)
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
96
|
def capture(money, authorization, options = {})
|
97
|
-
commit(:capture, :put) do |doc|
|
97
|
+
commit(:capture, options, :put) do |doc|
|
98
98
|
add_authorization(doc, authorization)
|
99
99
|
add_order(doc, options)
|
100
100
|
add_amount(doc, money, options) if options[:include_capture_amount] == true
|
@@ -102,15 +102,16 @@ module ActiveMerchant
|
|
102
102
|
end
|
103
103
|
|
104
104
|
def refund(money, authorization, options = {})
|
105
|
-
|
106
|
-
|
107
|
-
add_amount(doc, money, options)
|
108
|
-
|
105
|
+
options[:endpoint] = options[:merchant_transaction_id] ? "/refund/merchant/#{options[:merchant_transaction_id]}" : "/refund/#{authorization}"
|
106
|
+
commit(:refund, options, :post) do |doc|
|
107
|
+
add_amount(doc, money, options) if money
|
108
|
+
%i[reason cancel_subscription tax_amount].each { |field| send_when_present(doc, field, options) }
|
109
|
+
add_metadata(doc, options)
|
109
110
|
end
|
110
111
|
end
|
111
112
|
|
112
113
|
def void(authorization, options = {})
|
113
|
-
commit(:void, :put) do |doc|
|
114
|
+
commit(:void, options, :put) do |doc|
|
114
115
|
add_authorization(doc, authorization)
|
115
116
|
add_order(doc, options)
|
116
117
|
end
|
@@ -123,7 +124,7 @@ module ActiveMerchant
|
|
123
124
|
def store(payment_method, options = {})
|
124
125
|
payment_method_details = PaymentMethodDetails.new(payment_method)
|
125
126
|
|
126
|
-
commit(:store, :post, payment_method_details) do |doc|
|
127
|
+
commit(:store, options, :post, payment_method_details) do |doc|
|
127
128
|
add_personal_info(doc, payment_method, options)
|
128
129
|
add_echeck_company(doc, payment_method) if payment_method_details.check?
|
129
130
|
doc.send('payment-sources') do
|
@@ -149,7 +150,7 @@ module ActiveMerchant
|
|
149
150
|
|
150
151
|
def verify_credentials
|
151
152
|
begin
|
152
|
-
ssl_get(url.to_s, headers)
|
153
|
+
ssl_get(url.to_s, headers(options))
|
153
154
|
rescue ResponseError => e
|
154
155
|
return false if e.response.code.to_i == 401
|
155
156
|
end
|
@@ -234,6 +235,7 @@ module ActiveMerchant
|
|
234
235
|
doc.send('meta-key', truncate(entry[:meta_key], 40))
|
235
236
|
doc.send('meta-value', truncate(entry[:meta_value], 500))
|
236
237
|
doc.send('meta-description', truncate(entry[:meta_description], 40))
|
238
|
+
doc.send('is-visible', truncate(entry[:meta_is_visible], 5))
|
237
239
|
end
|
238
240
|
end
|
239
241
|
end
|
@@ -386,13 +388,12 @@ module ActiveMerchant
|
|
386
388
|
|
387
389
|
def parse(response)
|
388
390
|
return bad_authentication_response if response.code.to_i == 401
|
389
|
-
return generic_error_response(response.body) if [403, 429].include?(response.code.to_i)
|
391
|
+
return generic_error_response(response.body) if [403, 405, 429].include?(response.code.to_i)
|
390
392
|
|
391
393
|
parsed = {}
|
392
394
|
doc = Nokogiri::XML(response.body)
|
393
395
|
doc.root.xpath('*').each do |node|
|
394
396
|
name = node.name.downcase
|
395
|
-
|
396
397
|
if node.elements.empty?
|
397
398
|
parsed[name] = node.text
|
398
399
|
elsif name == 'transaction-meta-data'
|
@@ -433,15 +434,15 @@ module ActiveMerchant
|
|
433
434
|
end
|
434
435
|
end
|
435
436
|
|
436
|
-
def api_request(action, request, verb, payment_method_details)
|
437
|
-
ssl_request(verb, url(action, payment_method_details), request, headers)
|
437
|
+
def api_request(action, request, verb, payment_method_details, options)
|
438
|
+
ssl_request(verb, url(action, options, payment_method_details), request, headers(options))
|
438
439
|
rescue ResponseError => e
|
439
440
|
e.response
|
440
441
|
end
|
441
442
|
|
442
|
-
def commit(action, verb = :post, payment_method_details = PaymentMethodDetails.new())
|
443
|
+
def commit(action, options, verb = :post, payment_method_details = PaymentMethodDetails.new())
|
443
444
|
request = build_xml_request(action, payment_method_details) { |doc| yield(doc) }
|
444
|
-
response = api_request(action, request, verb, payment_method_details)
|
445
|
+
response = api_request(action, request, verb, payment_method_details, options)
|
445
446
|
parsed = parse(response)
|
446
447
|
|
447
448
|
succeeded = success_from(action, response)
|
@@ -457,9 +458,10 @@ module ActiveMerchant
|
|
457
458
|
)
|
458
459
|
end
|
459
460
|
|
460
|
-
def url(action = nil, payment_method_details = PaymentMethodDetails.new())
|
461
|
+
def url(action = nil, options = {}, payment_method_details = PaymentMethodDetails.new())
|
461
462
|
base = test? ? test_url : live_url
|
462
463
|
resource = action == :store ? 'vaulted-shoppers' : payment_method_details.resource_url
|
464
|
+
resource += options[:endpoint] if action == :refund
|
463
465
|
"#{base}/#{resource}"
|
464
466
|
end
|
465
467
|
|
@@ -532,20 +534,28 @@ module ActiveMerchant
|
|
532
534
|
end
|
533
535
|
|
534
536
|
def root_element(action, payment_method_details)
|
535
|
-
|
537
|
+
return 'refund' if action == :refund
|
538
|
+
return 'vaulted-shopper' if action == :store
|
539
|
+
|
540
|
+
payment_method_details.root_element
|
536
541
|
end
|
537
542
|
|
538
|
-
def headers
|
539
|
-
|
543
|
+
def headers(options)
|
544
|
+
idempotency_key = options[:idempotency_key] if options[:idempotency_key]
|
545
|
+
|
546
|
+
headers = {
|
540
547
|
'Content-Type' => 'application/xml',
|
541
548
|
'Authorization' => ('Basic ' + Base64.strict_encode64("#{@options[:api_username]}:#{@options[:api_password]}").strip)
|
542
549
|
}
|
550
|
+
|
551
|
+
headers['Idempotency-Key'] = idempotency_key if idempotency_key
|
552
|
+
headers
|
543
553
|
end
|
544
554
|
|
545
555
|
def build_xml_request(action, payment_method_details)
|
546
556
|
builder = Nokogiri::XML::Builder.new
|
547
557
|
builder.__send__(root_element(action, payment_method_details), root_attributes) do |doc|
|
548
|
-
doc.send('card-transaction-type', TRANSACTIONS[action]) if TRANSACTIONS[action] && !payment_method_details.alt_transaction?
|
558
|
+
doc.send('card-transaction-type', TRANSACTIONS[action]) if TRANSACTIONS[action] && !payment_method_details.alt_transaction? && action != :refund
|
549
559
|
yield(doc)
|
550
560
|
end
|
551
561
|
builder.doc.root.to_xml
|
@@ -18,6 +18,11 @@ module BraintreeCommon
|
|
18
18
|
transcript.
|
19
19
|
gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
|
20
20
|
gsub(%r((&?ccnumber=)\d*(&?)), '\1[FILTERED]\2').
|
21
|
-
gsub(%r((&?cvv=)\d*(&?)), '\1[FILTERED]\2')
|
21
|
+
gsub(%r((&?cvv=)\d*(&?)), '\1[FILTERED]\2').
|
22
|
+
gsub(%r((<account-number>)\d+(</account-number>)), '\1[FILTERED]\2').
|
23
|
+
gsub(%r((<payment-method-nonce>)[^<]+(</payment-method-nonce>)), '\1[FILTERED]\2').
|
24
|
+
gsub(%r((<payment-method-token>)[^<]+(</payment-method-token>)), '\1[FILTERED]\2').
|
25
|
+
gsub(%r((<value>)[^<]{100,}(</value>)), '\1[FILTERED]\2').
|
26
|
+
gsub(%r((<token>)[^<]+(</token>)), '\1[FILTERED]\2')
|
22
27
|
end
|
23
28
|
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
class TokenNonce #:nodoc:
|
4
|
+
include PostsData
|
5
|
+
# This class emulates the behavior of the front-end js library to
|
6
|
+
# create token nonce for a bank account base on the docs:
|
7
|
+
# https://developer.paypal.com/braintree/docs/guides/ach/client-side
|
8
|
+
|
9
|
+
attr_reader :braintree_gateway, :options
|
10
|
+
|
11
|
+
def initialize(gateway, options = {})
|
12
|
+
@braintree_gateway = gateway
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def url
|
17
|
+
sandbox = @braintree_gateway.config.environment == :sandbox
|
18
|
+
"https://payments#{'.sandbox' if sandbox}.braintree-api.com/graphql"
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_token_nonce_for_payment_method(payment_method)
|
22
|
+
headers = {
|
23
|
+
'Accept' => 'application/json',
|
24
|
+
'Authorization' => "Bearer #{client_token}",
|
25
|
+
'Content-Type' => 'application/json',
|
26
|
+
'Braintree-Version' => '2018-05-10'
|
27
|
+
}
|
28
|
+
resp = ssl_post(url, build_nonce_request(payment_method), headers)
|
29
|
+
json_response = JSON.parse(resp)
|
30
|
+
|
31
|
+
message = json_response['errors'].map { |err| err['message'] }.join("\n") if json_response['errors'].present?
|
32
|
+
token = json_response.dig('data', 'tokenizeUsBankAccount', 'paymentMethod', 'id')
|
33
|
+
|
34
|
+
return token, message
|
35
|
+
end
|
36
|
+
|
37
|
+
def client_token
|
38
|
+
base64_token = @braintree_gateway.client_token.generate
|
39
|
+
JSON.parse(Base64.decode64(base64_token))['authorizationFingerprint']
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def graphql_query
|
45
|
+
<<-GRAPHQL
|
46
|
+
mutation TokenizeUsBankAccount($input: TokenizeUsBankAccountInput!) {
|
47
|
+
tokenizeUsBankAccount(input: $input) {
|
48
|
+
paymentMethod {
|
49
|
+
id
|
50
|
+
details {
|
51
|
+
... on UsBankAccountDetails {
|
52
|
+
last4
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
}
|
58
|
+
GRAPHQL
|
59
|
+
end
|
60
|
+
|
61
|
+
def billing_address_from_options
|
62
|
+
return nil if options[:billing_address].blank?
|
63
|
+
|
64
|
+
address = options[:billing_address]
|
65
|
+
|
66
|
+
{
|
67
|
+
streetAddress: address[:address1],
|
68
|
+
extendedAddress: address[:address2],
|
69
|
+
city: address[:city],
|
70
|
+
state: address[:state],
|
71
|
+
zipCode: address[:zip]
|
72
|
+
}.compact
|
73
|
+
end
|
74
|
+
|
75
|
+
def build_nonce_request(payment_method)
|
76
|
+
input = {
|
77
|
+
usBankAccount: {
|
78
|
+
achMandate: options[:ach_mandate],
|
79
|
+
routingNumber: payment_method.routing_number,
|
80
|
+
accountNumber: payment_method.account_number,
|
81
|
+
accountType: payment_method.account_type.upcase,
|
82
|
+
billingAddress: billing_address_from_options
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
if payment_method.account_holder_type == 'personal'
|
87
|
+
input[:usBankAccount][:individualOwner] = {
|
88
|
+
firstName: payment_method.first_name,
|
89
|
+
lastName: payment_method.last_name
|
90
|
+
}
|
91
|
+
else
|
92
|
+
input[:usBankAccount][:businessOwner] = {
|
93
|
+
businessName: payment_method.name
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
{
|
98
|
+
clientSdkMetadata: {
|
99
|
+
platform: 'web',
|
100
|
+
source: 'client',
|
101
|
+
integration: 'custom',
|
102
|
+
sessionId: SecureRandom.uuid,
|
103
|
+
version: '3.83.0'
|
104
|
+
},
|
105
|
+
query: graphql_query,
|
106
|
+
variables: {
|
107
|
+
input: input
|
108
|
+
}
|
109
|
+
}.to_json
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_merchant/billing/gateways/braintree/braintree_common'
|
2
|
+
require 'active_merchant/billing/gateways/braintree/token_nonce'
|
2
3
|
require 'active_support/core_ext/array/extract_options'
|
3
4
|
|
4
5
|
begin
|
@@ -46,6 +47,8 @@ module ActiveMerchant #:nodoc:
|
|
46
47
|
cannot_refund_if_unsettled: 91506
|
47
48
|
}
|
48
49
|
|
50
|
+
DIRECT_BANK_ERROR = 'Direct bank account transactions are not supported. Bank accounts must be successfully stored before use.'.freeze
|
51
|
+
|
49
52
|
def initialize(options = {})
|
50
53
|
requires!(options, :merchant_id, :public_key, :private_key)
|
51
54
|
@merchant_account_id = options[:merchant_account_id]
|
@@ -73,6 +76,8 @@ module ActiveMerchant #:nodoc:
|
|
73
76
|
end
|
74
77
|
|
75
78
|
def authorize(money, credit_card_or_vault_id, options = {})
|
79
|
+
return Response.new(false, DIRECT_BANK_ERROR) if credit_card_or_vault_id.is_a? Check
|
80
|
+
|
76
81
|
create_transaction(:sale, money, credit_card_or_vault_id, options)
|
77
82
|
end
|
78
83
|
|
@@ -148,21 +153,13 @@ module ActiveMerchant #:nodoc:
|
|
148
153
|
end
|
149
154
|
end
|
150
155
|
|
151
|
-
def store(
|
152
|
-
if options
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
add_credit_card_to_customer(creditcard, options)
|
159
|
-
else
|
160
|
-
add_customer_with_credit_card(creditcard, options)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
else
|
165
|
-
add_customer_with_credit_card(creditcard, options)
|
156
|
+
def store(payment_method, options = {})
|
157
|
+
return Response.new(false, bank_account_errors(payment_method, options)) if payment_method.is_a?(Check) && bank_account_errors(payment_method, options).present?
|
158
|
+
|
159
|
+
MultiResponse.run do |r|
|
160
|
+
r.process { check_customer_exists(options[:customer]) }
|
161
|
+
process_by = payment_method.is_a?(Check) ? :store_bank_account : :store_credit_card
|
162
|
+
send process_by, payment_method, options, r
|
166
163
|
end
|
167
164
|
end
|
168
165
|
|
@@ -227,6 +224,8 @@ module ActiveMerchant #:nodoc:
|
|
227
224
|
private
|
228
225
|
|
229
226
|
def check_customer_exists(customer_vault_id)
|
227
|
+
return Response.new true, 'Customer not found', { exists: false } if customer_vault_id.blank?
|
228
|
+
|
230
229
|
commit do
|
231
230
|
@braintree_gateway.customer.find(customer_vault_id)
|
232
231
|
ActiveMerchant::Billing::Response.new(true, 'Customer found', { exists: true }, authorization: customer_vault_id)
|
@@ -827,6 +826,79 @@ module ActiveMerchant #:nodoc:
|
|
827
826
|
end
|
828
827
|
end
|
829
828
|
end
|
829
|
+
|
830
|
+
def bank_account_errors(payment_method, options)
|
831
|
+
if payment_method.validate.present?
|
832
|
+
payment_method.validate
|
833
|
+
elsif options[:billing_address].blank?
|
834
|
+
'billing_address is required parameter to store and verify Bank accounts.'
|
835
|
+
elsif options[:ach_mandate].blank?
|
836
|
+
'ach_mandate is a required parameter to process bank acccount transactions see (https://developer.paypal.com/braintree/docs/guides/ach/client-side#show-required-authorization-language)'
|
837
|
+
end
|
838
|
+
end
|
839
|
+
|
840
|
+
def add_bank_account_to_customer(payment_method, options)
|
841
|
+
bank_account_nonce, error_message = TokenNonce.new(@braintree_gateway, options).create_token_nonce_for_payment_method payment_method
|
842
|
+
return Response.new(false, error_message) unless bank_account_nonce.present?
|
843
|
+
|
844
|
+
result = @braintree_gateway.payment_method.create(
|
845
|
+
customer_id: options[:customer],
|
846
|
+
payment_method_nonce: bank_account_nonce,
|
847
|
+
options: {
|
848
|
+
us_bank_account_verification_method: 'network_check'
|
849
|
+
}
|
850
|
+
)
|
851
|
+
|
852
|
+
verified = result.success? && result.payment_method&.verified
|
853
|
+
message = message_from_result(result)
|
854
|
+
message = not_verified_reason(result.payment_method) unless verified
|
855
|
+
|
856
|
+
Response.new(verified, message,
|
857
|
+
{
|
858
|
+
customer_vault_id: options[:customer],
|
859
|
+
bank_account_token: result.payment_method&.token,
|
860
|
+
verified: verified
|
861
|
+
},
|
862
|
+
authorization: result.payment_method&.token)
|
863
|
+
end
|
864
|
+
|
865
|
+
def not_verified_reason(bank_account)
|
866
|
+
return unless bank_account.verifications.present?
|
867
|
+
|
868
|
+
verification = bank_account.verifications.first
|
869
|
+
"verification_status: [#{verification.status}], processor_response: [#{verification.processor_response_code}-#{verification.processor_response_text}]"
|
870
|
+
end
|
871
|
+
|
872
|
+
def store_bank_account(payment_method, options, multi_response)
|
873
|
+
multi_response.process { create_customer_from_bank_account payment_method, options } unless multi_response.params['exists']
|
874
|
+
multi_response.process { add_bank_account_to_customer payment_method, options }
|
875
|
+
end
|
876
|
+
|
877
|
+
def store_credit_card(payment_method, options, multi_response)
|
878
|
+
process_by = multi_response.params['exists'] ? :add_credit_card_to_customer : :add_customer_with_credit_card
|
879
|
+
multi_response.process { send process_by, payment_method, options }
|
880
|
+
end
|
881
|
+
|
882
|
+
def create_customer_from_bank_account(payment_method, options)
|
883
|
+
parameters = {
|
884
|
+
id: options[:customer],
|
885
|
+
first_name: payment_method.first_name,
|
886
|
+
last_name: payment_method.last_name,
|
887
|
+
email: scrub_email(options[:email]),
|
888
|
+
phone: options[:phone] || options.dig(:billing_address, :phone),
|
889
|
+
device_data: options[:device_data]
|
890
|
+
}.compact
|
891
|
+
|
892
|
+
result = @braintree_gateway.customer.create(parameters)
|
893
|
+
customer_id = result.customer.id if result.success?
|
894
|
+
options[:customer] = customer_id
|
895
|
+
|
896
|
+
Response.new(
|
897
|
+
result.success?,
|
898
|
+
message_from_result(result),
|
899
|
+
{ customer_vault_id: customer_id, 'exists': true }
|
900
|
+
)
|
901
|
+
end
|
830
902
|
end
|
831
903
|
end
|
832
904
|
end
|
@@ -169,7 +169,7 @@ module ActiveMerchant #:nodoc:
|
|
169
169
|
def add_address(post, options)
|
170
170
|
if address = options[:billing_address] || options[:address]
|
171
171
|
post[:address] = address[:address1] if address[:address1]
|
172
|
-
post[:
|
172
|
+
post[:address2] = address[:address2] if address[:address2]
|
173
173
|
post[:city] = address[:city] if address[:city]
|
174
174
|
post[:region] = address[:state] if address[:state]
|
175
175
|
post[:country] = address[:country] if address[:country]
|
@@ -193,6 +193,7 @@ module ActiveMerchant #:nodoc:
|
|
193
193
|
add_submerchant_id(post, options)
|
194
194
|
add_processor(post, options)
|
195
195
|
add_email(post, options)
|
196
|
+
add_recipient(post, options)
|
196
197
|
|
197
198
|
if options[:referral_cft]
|
198
199
|
add_customer_name(post, options)
|
@@ -320,6 +321,15 @@ module ActiveMerchant #:nodoc:
|
|
320
321
|
post[:c3] = options[:email] || 'unspecified@example.com'
|
321
322
|
end
|
322
323
|
|
324
|
+
def add_recipient(post, options)
|
325
|
+
return unless options[:recipient_street_address] || options[:recipient_city] || options[:recipient_province_code] || options[:recipient_country_code]
|
326
|
+
|
327
|
+
post[:j6] = options[:recipient_street_address] if options[:recipient_street_address]
|
328
|
+
post[:j7] = options[:recipient_city] if options[:recipient_city]
|
329
|
+
post[:j8] = options[:recipient_province_code] if options[:recipient_province_code]
|
330
|
+
post[:j9] = options[:recipient_country_code] if options[:recipient_country_code]
|
331
|
+
end
|
332
|
+
|
323
333
|
def add_customer_name(post, options)
|
324
334
|
post[:j5] = options[:first_name] if options[:first_name]
|
325
335
|
post[:j13] = options[:last_name] if options[:last_name]
|
@@ -15,9 +15,6 @@ module ActiveMerchant #:nodoc:
|
|
15
15
|
# CyberSource what kind of item you are selling. It is used when
|
16
16
|
# calculating tax/VAT.
|
17
17
|
# * All transactions use dollar values.
|
18
|
-
# * To process pinless debit cards through the pinless debit card
|
19
|
-
# network, your Cybersource merchant account must accept pinless
|
20
|
-
# debit card payments.
|
21
18
|
# * The order of the XML elements does matter, make sure to follow the order in
|
22
19
|
# the documentation exactly.
|
23
20
|
class CyberSourceGateway < Gateway
|
@@ -139,7 +136,6 @@ module ActiveMerchant #:nodoc:
|
|
139
136
|
commit(build_capture_request(money, authorization, options), :capture, money, options)
|
140
137
|
end
|
141
138
|
|
142
|
-
# options[:pinless_debit_card] => true # attempts to process as pinless debit card
|
143
139
|
def purchase(money, payment_method_or_reference, options = {})
|
144
140
|
setup_address_hash(options)
|
145
141
|
commit(build_purchase_request(money, payment_method_or_reference, options), :purchase, money, options)
|
@@ -158,9 +154,10 @@ module ActiveMerchant #:nodoc:
|
|
158
154
|
end
|
159
155
|
|
160
156
|
def verify(payment, options = {})
|
157
|
+
amount = eligible_for_zero_auth?(payment, options) ? 0 : 100
|
161
158
|
MultiResponse.run(:use_first_response) do |r|
|
162
|
-
r.process { authorize(
|
163
|
-
r.process(:ignore_result) { void(r.authorization, options) }
|
159
|
+
r.process { authorize(amount, payment, options) }
|
160
|
+
r.process(:ignore_result) { void(r.authorization, options) } unless amount == 0
|
164
161
|
end
|
165
162
|
end
|
166
163
|
|
@@ -229,12 +226,6 @@ module ActiveMerchant #:nodoc:
|
|
229
226
|
commit(build_tax_calculation_request(creditcard, options), :calculate_tax, nil, options)
|
230
227
|
end
|
231
228
|
|
232
|
-
# Determines if a card can be used for Pinless Debit Card transactions
|
233
|
-
def validate_pinless_debit_card(creditcard, options = {})
|
234
|
-
requires!(options, :order_id)
|
235
|
-
commit(build_validate_pinless_debit_request(creditcard, options), :validate_pinless_debit_card, nil, options)
|
236
|
-
end
|
237
|
-
|
238
229
|
def supports_scrubbing?
|
239
230
|
true
|
240
231
|
end
|
@@ -291,6 +282,7 @@ module ActiveMerchant #:nodoc:
|
|
291
282
|
xml = Builder::XmlMarkup.new indent: 2
|
292
283
|
add_customer_id(xml, options)
|
293
284
|
add_payment_method_or_subscription(xml, money, creditcard_or_reference, options)
|
285
|
+
add_other_tax(xml, options)
|
294
286
|
add_threeds_2_ucaf_data(xml, creditcard_or_reference, options)
|
295
287
|
add_decision_manager_fields(xml, options)
|
296
288
|
add_mdd_fields(xml, options)
|
@@ -349,6 +341,7 @@ module ActiveMerchant #:nodoc:
|
|
349
341
|
xml = Builder::XmlMarkup.new indent: 2
|
350
342
|
add_customer_id(xml, options)
|
351
343
|
add_payment_method_or_subscription(xml, money, payment_method_or_reference, options)
|
344
|
+
add_other_tax(xml, options)
|
352
345
|
add_threeds_2_ucaf_data(xml, payment_method_or_reference, options)
|
353
346
|
add_decision_manager_fields(xml, options)
|
354
347
|
add_mdd_fields(xml, options)
|
@@ -362,7 +355,7 @@ module ActiveMerchant #:nodoc:
|
|
362
355
|
add_purchase_service(xml, payment_method_or_reference, options)
|
363
356
|
add_threeds_services(xml, options)
|
364
357
|
add_payment_network_token(xml) if network_tokenization?(payment_method_or_reference)
|
365
|
-
add_business_rules_data(xml, payment_method_or_reference, options)
|
358
|
+
add_business_rules_data(xml, payment_method_or_reference, options)
|
366
359
|
add_stored_credential_subsequent_auth(xml, options)
|
367
360
|
add_issuer_additional_data(xml, options)
|
368
361
|
add_partner_solution_id(xml)
|
@@ -474,13 +467,6 @@ module ActiveMerchant #:nodoc:
|
|
474
467
|
xml.target!
|
475
468
|
end
|
476
469
|
|
477
|
-
def build_validate_pinless_debit_request(creditcard, options)
|
478
|
-
xml = Builder::XmlMarkup.new indent: 2
|
479
|
-
add_creditcard(xml, creditcard)
|
480
|
-
add_validate_pinless_debit_service(xml)
|
481
|
-
xml.target!
|
482
|
-
end
|
483
|
-
|
484
470
|
def add_business_rules_data(xml, payment_method, options)
|
485
471
|
prioritized_options = [options, @options]
|
486
472
|
|
@@ -794,15 +780,9 @@ module ActiveMerchant #:nodoc:
|
|
794
780
|
end
|
795
781
|
|
796
782
|
def add_purchase_service(xml, payment_method, options)
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
end
|
801
|
-
else
|
802
|
-
add_auth_service(xml, payment_method, options)
|
803
|
-
xml.tag! 'ccCaptureService', { 'run' => 'true' } do
|
804
|
-
xml.tag!('reconciliationID', options[:reconciliation_id]) if options[:reconciliation_id]
|
805
|
-
end
|
783
|
+
add_auth_service(xml, payment_method, options)
|
784
|
+
xml.tag! 'ccCaptureService', { 'run' => 'true' } do
|
785
|
+
xml.tag!('reconciliationID', options[:reconciliation_id]) if options[:reconciliation_id]
|
806
786
|
end
|
807
787
|
end
|
808
788
|
|
@@ -911,10 +891,6 @@ module ActiveMerchant #:nodoc:
|
|
911
891
|
end
|
912
892
|
end
|
913
893
|
|
914
|
-
def add_validate_pinless_debit_service(xml)
|
915
|
-
xml.tag! 'pinlessDebitValidateService', { 'run' => 'true' }
|
916
|
-
end
|
917
|
-
|
918
894
|
def add_threeds_services(xml, options)
|
919
895
|
xml.tag! 'payerAuthEnrollService', { 'run' => 'true' } if options[:payer_auth_enroll_service]
|
920
896
|
if options[:payer_auth_validate_service]
|
@@ -1077,6 +1053,10 @@ module ActiveMerchant #:nodoc:
|
|
1077
1053
|
response[:message]
|
1078
1054
|
end
|
1079
1055
|
end
|
1056
|
+
|
1057
|
+
def eligible_for_zero_auth?(payment_method, options = {})
|
1058
|
+
payment_method.is_a?(CreditCard) && options[:zero_amount_auth]
|
1059
|
+
end
|
1080
1060
|
end
|
1081
1061
|
end
|
1082
1062
|
end
|
@@ -19,6 +19,7 @@ module ActiveMerchant #:nodoc:
|
|
19
19
|
def purchase(money, payment, options = {})
|
20
20
|
post = {}
|
21
21
|
add_auth_purchase_params(post, money, payment, 'purchase', options)
|
22
|
+
add_three_ds(post, options)
|
22
23
|
|
23
24
|
commit('purchase', post, options)
|
24
25
|
end
|
@@ -26,6 +27,7 @@ module ActiveMerchant #:nodoc:
|
|
26
27
|
def authorize(money, payment, options = {})
|
27
28
|
post = {}
|
28
29
|
add_auth_purchase_params(post, money, payment, 'authorize', options)
|
30
|
+
add_three_ds(post, options)
|
29
31
|
post[:card][:verify] = true if options[:verify].to_s == 'true'
|
30
32
|
|
31
33
|
commit('authorize', post, options)
|
@@ -154,6 +156,7 @@ module ActiveMerchant #:nodoc:
|
|
154
156
|
post[:card][:capture] = (action == 'purchase')
|
155
157
|
post[:card][:installments] = options[:installments] if options[:installments]
|
156
158
|
post[:card][:installments_id] = options[:installments_id] if options[:installments_id]
|
159
|
+
post[:card][:force_type] = options[:force_type].to_s.upcase if options[:force_type]
|
157
160
|
end
|
158
161
|
|
159
162
|
def parse(body)
|
@@ -161,6 +164,9 @@ module ActiveMerchant #:nodoc:
|
|
161
164
|
end
|
162
165
|
|
163
166
|
def commit(action, parameters, options = {})
|
167
|
+
three_ds_errors = validate_three_ds_params(parameters[:three_dsecure]) if parameters[:three_dsecure].present?
|
168
|
+
return three_ds_errors if three_ds_errors
|
169
|
+
|
164
170
|
url = url(action, parameters, options)
|
165
171
|
post = post_data(action, parameters)
|
166
172
|
begin
|
@@ -249,6 +255,49 @@ module ActiveMerchant #:nodoc:
|
|
249
255
|
def post_data(action, parameters = {})
|
250
256
|
parameters.to_json
|
251
257
|
end
|
258
|
+
|
259
|
+
def xid_or_ds_trans_id(three_d_secure)
|
260
|
+
if three_d_secure[:version].to_f >= 2
|
261
|
+
{ ds_transaction_id: three_d_secure[:ds_transaction_id] }
|
262
|
+
else
|
263
|
+
{ xid: three_d_secure[:xid] }
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def add_three_ds(post, options)
|
268
|
+
return unless three_d_secure = options[:three_d_secure]
|
269
|
+
|
270
|
+
post[:three_dsecure] = {
|
271
|
+
mpi: true,
|
272
|
+
three_dsecure_version: three_d_secure[:version],
|
273
|
+
cavv: three_d_secure[:cavv],
|
274
|
+
eci: three_d_secure[:eci],
|
275
|
+
enrollment_response: formatted_enrollment(three_d_secure[:enrolled]),
|
276
|
+
authentication_response: three_d_secure[:authentication_response_status]
|
277
|
+
}.merge(xid_or_ds_trans_id(three_d_secure))
|
278
|
+
end
|
279
|
+
|
280
|
+
def validate_three_ds_params(three_ds)
|
281
|
+
errors = {}
|
282
|
+
supported_version = %w{1.0 2.0 2.1.0 2.2.0}.include?(three_ds[:three_dsecure_version])
|
283
|
+
supported_enrollment = ['Y', 'N', 'U', nil].include?(three_ds[:enrollment_response])
|
284
|
+
supported_auth_response = ['Y', 'N', 'U', nil].include?(three_ds[:authentication_response])
|
285
|
+
|
286
|
+
errors[:three_ds_version] = 'ThreeDs version not supported' unless supported_version
|
287
|
+
errors[:enrollment] = 'Enrollment value not supported' unless supported_enrollment
|
288
|
+
errors[:auth_response] = 'Authentication response value not supported' unless supported_auth_response
|
289
|
+
errors.compact!
|
290
|
+
|
291
|
+
errors.present? ? Response.new(false, 'ThreeDs data is invalid', errors) : nil
|
292
|
+
end
|
293
|
+
|
294
|
+
def formatted_enrollment(val)
|
295
|
+
case val
|
296
|
+
when 'Y', 'N', 'U' then val
|
297
|
+
when true, 'true' then 'Y'
|
298
|
+
when false, 'false' then 'N'
|
299
|
+
end
|
300
|
+
end
|
252
301
|
end
|
253
302
|
end
|
254
303
|
end
|