activemerchant 1.125.0 → 1.126.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|