activemerchant 1.117.0 → 1.123.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 +217 -0
- data/README.md +5 -3
- data/lib/active_merchant/billing/check.rb +19 -12
- data/lib/active_merchant/billing/credit_card.rb +6 -0
- data/lib/active_merchant/billing/credit_card_formatting.rb +1 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +96 -22
- data/lib/active_merchant/billing/gateways/adyen.rb +38 -21
- data/lib/active_merchant/billing/gateways/authorize_net.rb +19 -11
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +4 -0
- data/lib/active_merchant/billing/gateways/blue_pay.rb +29 -0
- data/lib/active_merchant/billing/gateways/blue_snap.rb +5 -3
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +58 -8
- data/lib/active_merchant/billing/gateways/cashnet.rb +7 -2
- data/lib/active_merchant/billing/gateways/checkout_v2.rb +31 -0
- data/lib/active_merchant/billing/gateways/credorax.rb +16 -9
- data/lib/active_merchant/billing/gateways/cyber_source.rb +67 -9
- data/lib/active_merchant/billing/gateways/d_local.rb +1 -1
- data/lib/active_merchant/billing/gateways/decidir.rb +29 -3
- data/lib/active_merchant/billing/gateways/elavon.rb +110 -26
- data/lib/active_merchant/billing/gateways/element.rb +2 -0
- data/lib/active_merchant/billing/gateways/eway_rapid.rb +13 -0
- data/lib/active_merchant/billing/gateways/firstdata_e4_v27.rb +17 -6
- data/lib/active_merchant/billing/gateways/forte.rb +12 -0
- data/lib/active_merchant/billing/gateways/global_collect.rb +25 -16
- data/lib/active_merchant/billing/gateways/hps.rb +65 -2
- data/lib/active_merchant/billing/gateways/kushki.rb +23 -0
- data/lib/active_merchant/billing/gateways/litle.rb +9 -4
- data/lib/active_merchant/billing/gateways/mercado_pago.rb +5 -4
- data/lib/active_merchant/billing/gateways/merchant_warrior.rb +2 -0
- data/lib/active_merchant/billing/gateways/moka.rb +277 -0
- data/lib/active_merchant/billing/gateways/monei.rb +228 -144
- data/lib/active_merchant/billing/gateways/mundipagg.rb +14 -5
- data/lib/active_merchant/billing/gateways/netbanx.rb +37 -2
- data/lib/active_merchant/billing/gateways/nmi.rb +14 -9
- data/lib/active_merchant/billing/gateways/orbital.rb +202 -47
- data/lib/active_merchant/billing/gateways/pay_arc.rb +390 -0
- data/lib/active_merchant/billing/gateways/pay_trace.rb +404 -0
- data/lib/active_merchant/billing/gateways/payeezy.rb +57 -11
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -0
- data/lib/active_merchant/billing/gateways/payflow.rb +9 -0
- data/lib/active_merchant/billing/gateways/payment_express.rb +10 -5
- data/lib/active_merchant/billing/gateways/paymentez.rb +26 -1
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +1 -0
- data/lib/active_merchant/billing/gateways/paypal.rb +10 -2
- data/lib/active_merchant/billing/gateways/paypal_express.rb +1 -0
- data/lib/active_merchant/billing/gateways/paysafe.rb +291 -0
- data/lib/active_merchant/billing/gateways/payu_latam.rb +3 -3
- data/lib/active_merchant/billing/gateways/payway_dot_com.rb +253 -0
- data/lib/active_merchant/billing/gateways/pin.rb +11 -0
- data/lib/active_merchant/billing/gateways/qvalent.rb +23 -9
- data/lib/active_merchant/billing/gateways/redsys.rb +78 -30
- data/lib/active_merchant/billing/gateways/safe_charge.rb +19 -8
- data/lib/active_merchant/billing/gateways/spreedly_core.rb +13 -4
- data/lib/active_merchant/billing/gateways/stripe.rb +8 -8
- data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +86 -25
- data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +1 -1
- data/lib/active_merchant/billing/gateways/vpos.rb +220 -0
- data/lib/active_merchant/billing/gateways/worldpay.rb +68 -20
- data/lib/active_merchant/billing/response.rb +2 -1
- data/lib/active_merchant/billing/three_d_secure_eci_mapper.rb +27 -0
- data/lib/active_merchant/billing.rb +1 -0
- data/lib/active_merchant/version.rb +1 -1
- data/lib/certs/cacert.pem +1582 -2431
- metadata +10 -3
@@ -343,7 +343,7 @@ module ActiveMerchant #:nodoc:
|
|
343
343
|
parameters[:software] = 'Active Merchant'
|
344
344
|
parameters[:testmode] = (@options[:test] ? 1 : 0) unless parameters.has_key?(:testmode)
|
345
345
|
seed = SecureRandom.hex(32).upcase
|
346
|
-
hash = Digest::SHA1.hexdigest("#{parameters[:command]}:#{@options[:password]}:#{parameters[:amount]}:#{parameters[:invoice]}:#{seed}")
|
346
|
+
hash = Digest::SHA1.hexdigest("#{parameters[:command]}:#{@options[:pin] || @options[:password]}:#{parameters[:amount]}:#{parameters[:invoice]}:#{seed}")
|
347
347
|
parameters[:hash] = "s/#{seed}/#{hash}/n"
|
348
348
|
|
349
349
|
parameters.collect { |key, value| "UM#{key}=#{CGI.escape(value.to_s)}" }.join('&')
|
@@ -0,0 +1,220 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require 'jwe'
|
3
|
+
|
4
|
+
module ActiveMerchant #:nodoc:
|
5
|
+
module Billing #:nodoc:
|
6
|
+
class VposGateway < Gateway
|
7
|
+
self.test_url = 'https://vpos.infonet.com.py:8888'
|
8
|
+
self.live_url = 'https://vpos.infonet.com.py'
|
9
|
+
|
10
|
+
self.supported_countries = ['PY']
|
11
|
+
self.default_currency = 'PYG'
|
12
|
+
self.supported_cardtypes = %i[visa master]
|
13
|
+
|
14
|
+
self.homepage_url = 'https://comercios.bancard.com.py'
|
15
|
+
self.display_name = 'vPOS'
|
16
|
+
|
17
|
+
self.money_format = :dollars
|
18
|
+
|
19
|
+
ENDPOINTS = {
|
20
|
+
pci_encryption_key: '/vpos/api/0.3/application/encryption-key',
|
21
|
+
pay_pci_buy_encrypted: '/vpos/api/0.3/pci/encrypted',
|
22
|
+
pci_buy_rollback: '/vpos/api/0.3/pci_buy/rollback',
|
23
|
+
refund: '/vpos/api/0.3/refunds'
|
24
|
+
}
|
25
|
+
|
26
|
+
def initialize(options = {})
|
27
|
+
requires!(options, :private_key, :public_key)
|
28
|
+
@private_key = options[:private_key]
|
29
|
+
@public_key = options[:public_key]
|
30
|
+
@shop_process_id = options[:shop_process_id] || SecureRandom.random_number(10**15)
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
def purchase(money, payment, options = {})
|
35
|
+
commerce = options[:commerce] || @options[:commerce]
|
36
|
+
commerce_branch = options[:commerce_branch] || @options[:commerce_branch]
|
37
|
+
shop_process_id = options[:shop_process_id] || @shop_process_id
|
38
|
+
|
39
|
+
token = generate_token(shop_process_id, 'pay_pci', commerce, commerce_branch, amount(money), currency(money))
|
40
|
+
|
41
|
+
post = {}
|
42
|
+
post[:token] = token
|
43
|
+
post[:commerce] = commerce.to_s
|
44
|
+
post[:commerce_branch] = commerce_branch.to_s
|
45
|
+
post[:shop_process_id] = shop_process_id
|
46
|
+
post[:number_of_payments] = options[:number_of_payments] || 1
|
47
|
+
post[:recursive] = options[:recursive] || false
|
48
|
+
|
49
|
+
add_invoice(post, money, options)
|
50
|
+
add_card_data(post, payment)
|
51
|
+
add_customer_data(post, options)
|
52
|
+
|
53
|
+
commit(:pay_pci_buy_encrypted, post)
|
54
|
+
end
|
55
|
+
|
56
|
+
def void(authorization, options = {})
|
57
|
+
_, shop_process_id = authorization.to_s.split('#')
|
58
|
+
token = generate_token(shop_process_id, 'rollback', '0.00')
|
59
|
+
post = {
|
60
|
+
token: token,
|
61
|
+
shop_process_id: shop_process_id
|
62
|
+
}
|
63
|
+
commit(:pci_buy_rollback, post)
|
64
|
+
end
|
65
|
+
|
66
|
+
def credit(money, payment, options = {})
|
67
|
+
# Not permitted for foreign cards.
|
68
|
+
commerce = options[:commerce] || @options[:commerce]
|
69
|
+
commerce_branch = options[:commerce_branch] || @options[:commerce_branch]
|
70
|
+
|
71
|
+
token = generate_token(@shop_process_id, 'refund', commerce, commerce_branch, amount(money), currency(money))
|
72
|
+
post = {}
|
73
|
+
post[:token] = token
|
74
|
+
post[:commerce] = commerce.to_i
|
75
|
+
post[:commerce_branch] = commerce_branch.to_i
|
76
|
+
post[:shop_process_id] = @shop_process_id
|
77
|
+
add_invoice(post, money, options)
|
78
|
+
add_card_data(post, payment)
|
79
|
+
add_customer_data(post, options)
|
80
|
+
post[:origin_shop_process_id] = options[:original_shop_process_id] if options[:original_shop_process_id]
|
81
|
+
commit(:refund, post)
|
82
|
+
end
|
83
|
+
|
84
|
+
def refund(money, authorization, options = {})
|
85
|
+
commerce = options[:commerce] || @options[:commerce]
|
86
|
+
commerce_branch = options[:commerce_branch] || @options[:commerce_branch]
|
87
|
+
shop_process_id = options[:shop_process_id] || @shop_process_id
|
88
|
+
_, original_shop_process_id = authorization.to_s.split('#')
|
89
|
+
|
90
|
+
token = generate_token(shop_process_id, 'refund', commerce, commerce_branch, amount(money), currency(money))
|
91
|
+
post = {}
|
92
|
+
post[:token] = token
|
93
|
+
post[:commerce] = commerce.to_i
|
94
|
+
post[:commerce_branch] = commerce_branch.to_i
|
95
|
+
post[:shop_process_id] = shop_process_id
|
96
|
+
add_invoice(post, money, options)
|
97
|
+
add_customer_data(post, options)
|
98
|
+
post[:origin_shop_process_id] = original_shop_process_id || options[:original_shop_process_id]
|
99
|
+
commit(:refund, post)
|
100
|
+
end
|
101
|
+
|
102
|
+
def supports_scrubbing?
|
103
|
+
true
|
104
|
+
end
|
105
|
+
|
106
|
+
def scrub(transcript)
|
107
|
+
clean_transcript = remove_invalid_utf_8_byte_sequences(transcript)
|
108
|
+
clean_transcript.
|
109
|
+
gsub(/(token\\":\\")[.\-\w]+/, '\1[FILTERED]').
|
110
|
+
gsub(/(card_encrypted_data\\":\\")[.\-\w]+/, '\1[FILTERED]')
|
111
|
+
end
|
112
|
+
|
113
|
+
def remove_invalid_utf_8_byte_sequences(transcript)
|
114
|
+
transcript.encode('UTF-8', 'binary', undef: :replace, replace: '')
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
# Required to encrypt PAN data.
|
120
|
+
def one_time_public_key
|
121
|
+
token = generate_token('get_encription_public_key', @public_key)
|
122
|
+
response = commit(:pci_encryption_key, token: token)
|
123
|
+
OpenSSL::PKey::RSA.new(response.params['encryption_key'])
|
124
|
+
end
|
125
|
+
|
126
|
+
def generate_token(*elements)
|
127
|
+
Digest::MD5.hexdigest(@private_key + elements.join)
|
128
|
+
end
|
129
|
+
|
130
|
+
def add_invoice(post, money, options)
|
131
|
+
post[:amount] = amount(money)
|
132
|
+
post[:currency] = options[:currency] || currency(money)
|
133
|
+
end
|
134
|
+
|
135
|
+
def add_card_data(post, payment)
|
136
|
+
card_number = payment.number
|
137
|
+
cvv = payment.verification_value
|
138
|
+
|
139
|
+
payload = { card_number: card_number, 'cvv': cvv }.to_json
|
140
|
+
|
141
|
+
post[:card_encrypted_data] = JWE.encrypt(payload, one_time_public_key)
|
142
|
+
post[:card_month_expiration] = format(payment.month, :two_digits)
|
143
|
+
post[:card_year_expiration] = format(payment.year, :two_digits)
|
144
|
+
end
|
145
|
+
|
146
|
+
def add_customer_data(post, options)
|
147
|
+
post[:additional_data] = options[:additional_data] || '' # must be passed even if empty
|
148
|
+
end
|
149
|
+
|
150
|
+
def parse(body)
|
151
|
+
JSON.parse(body)
|
152
|
+
end
|
153
|
+
|
154
|
+
def commit(action, parameters)
|
155
|
+
url = build_request_url(action)
|
156
|
+
begin
|
157
|
+
response = parse(ssl_post(url, post_data(parameters)))
|
158
|
+
rescue ResponseError => response
|
159
|
+
# Errors are returned with helpful data,
|
160
|
+
# but get filtered out by `ssl_post` because of their HTTP status.
|
161
|
+
response = parse(response.response.body)
|
162
|
+
end
|
163
|
+
|
164
|
+
Response.new(
|
165
|
+
success_from(response),
|
166
|
+
message_from(response),
|
167
|
+
response,
|
168
|
+
authorization: authorization_from(response),
|
169
|
+
avs_result: nil,
|
170
|
+
cvv_result: nil,
|
171
|
+
test: test?,
|
172
|
+
error_code: error_code_from(response)
|
173
|
+
)
|
174
|
+
end
|
175
|
+
|
176
|
+
def success_from(response)
|
177
|
+
if code = response.dig('confirmation', 'response_code')
|
178
|
+
code == '00'
|
179
|
+
else
|
180
|
+
response['status'] == 'success'
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def message_from(response)
|
185
|
+
%w(confirmation refund).each do |m|
|
186
|
+
message =
|
187
|
+
response.dig(m, 'extended_response_description') ||
|
188
|
+
response.dig(m, 'response_description') ||
|
189
|
+
response.dig(m, 'response_details')
|
190
|
+
return message if message
|
191
|
+
end
|
192
|
+
[response.dig('messages', 0, 'key'), response.dig('messages', 0, 'dsc')].join(':')
|
193
|
+
end
|
194
|
+
|
195
|
+
def authorization_from(response)
|
196
|
+
response_body = response.dig('confirmation') || response.dig('refund')
|
197
|
+
return unless response_body
|
198
|
+
|
199
|
+
authorization_number = response_body.dig('authorization_number') || response_body.dig('authorization_code')
|
200
|
+
shop_process_id = response_body.dig('shop_process_id')
|
201
|
+
|
202
|
+
"#{authorization_number}##{shop_process_id}"
|
203
|
+
end
|
204
|
+
|
205
|
+
def error_code_from(response)
|
206
|
+
response.dig('confirmation', 'response_code') unless success_from(response)
|
207
|
+
end
|
208
|
+
|
209
|
+
def build_request_url(action)
|
210
|
+
base_url = (test? ? test_url : live_url)
|
211
|
+
base_url + ENDPOINTS[action]
|
212
|
+
end
|
213
|
+
|
214
|
+
def post_data(data)
|
215
|
+
{ public_key: @public_key,
|
216
|
+
operation: data }.compact.to_json
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -58,7 +58,7 @@ module ActiveMerchant #:nodoc:
|
|
58
58
|
def purchase(money, payment_method, options = {})
|
59
59
|
MultiResponse.run do |r|
|
60
60
|
r.process { authorize(money, payment_method, options) }
|
61
|
-
r.process { capture(money, r.authorization, options.merge(authorization_validated: true)) }
|
61
|
+
r.process { capture(money, r.authorization, options.merge(authorization_validated: true)) } unless options[:skip_capture]
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
@@ -71,7 +71,7 @@ module ActiveMerchant #:nodoc:
|
|
71
71
|
def capture(money, authorization, options = {})
|
72
72
|
authorization = order_id_from_authorization(authorization.to_s)
|
73
73
|
MultiResponse.run do |r|
|
74
|
-
r.process { inquire_request(authorization, options, 'AUTHORISED') } unless options[:authorization_validated]
|
74
|
+
r.process { inquire_request(authorization, options, 'AUTHORISED', 'CAPTURED') } unless options[:authorization_validated]
|
75
75
|
if r.params
|
76
76
|
authorization_currency = r.params['amount_currency_code']
|
77
77
|
options = options.merge(currency: authorization_currency) if authorization_currency.present?
|
@@ -90,8 +90,10 @@ module ActiveMerchant #:nodoc:
|
|
90
90
|
|
91
91
|
def refund(money, authorization, options = {})
|
92
92
|
authorization = order_id_from_authorization(authorization.to_s)
|
93
|
+
success_criteria = %w(CAPTURED SETTLED SETTLED_BY_MERCHANT SENT_FOR_REFUND)
|
94
|
+
success_criteria.push('AUTHORIZED') if options[:cancel_or_refund]
|
93
95
|
response = MultiResponse.run do |r|
|
94
|
-
r.process { inquire_request(authorization, options,
|
96
|
+
r.process { inquire_request(authorization, options, *success_criteria) } unless options[:authorization_validated]
|
95
97
|
r.process { refund_request(money, authorization, options) }
|
96
98
|
end
|
97
99
|
|
@@ -113,8 +115,9 @@ module ActiveMerchant #:nodoc:
|
|
113
115
|
end
|
114
116
|
|
115
117
|
def verify(payment_method, options = {})
|
118
|
+
amount = (eligible_for_0_auth?(payment_method, options) ? 0 : 100)
|
116
119
|
MultiResponse.run(:use_first_response) do |r|
|
117
|
-
r.process { authorize(
|
120
|
+
r.process { authorize(amount, payment_method, options) }
|
118
121
|
r.process(:ignore_result) { void(r.authorization, options.merge(authorization_validated: true)) }
|
119
122
|
end
|
120
123
|
end
|
@@ -128,6 +131,10 @@ module ActiveMerchant #:nodoc:
|
|
128
131
|
true
|
129
132
|
end
|
130
133
|
|
134
|
+
def supports_network_tokenization?
|
135
|
+
true
|
136
|
+
end
|
137
|
+
|
131
138
|
def scrub(transcript)
|
132
139
|
transcript.
|
133
140
|
gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
|
@@ -138,11 +145,11 @@ module ActiveMerchant #:nodoc:
|
|
138
145
|
private
|
139
146
|
|
140
147
|
def authorize_request(money, payment_method, options)
|
141
|
-
commit('authorize', build_authorization_request(money, payment_method, options), 'AUTHORISED', options)
|
148
|
+
commit('authorize', build_authorization_request(money, payment_method, options), 'AUTHORISED', 'CAPTURED', options)
|
142
149
|
end
|
143
150
|
|
144
151
|
def capture_request(money, authorization, options)
|
145
|
-
commit('capture', build_capture_request(money, authorization, options), :ok, options)
|
152
|
+
commit('capture', build_capture_request(money, authorization, options), 'CAPTURED', :ok, options)
|
146
153
|
end
|
147
154
|
|
148
155
|
def cancel_request(authorization, options)
|
@@ -154,7 +161,7 @@ module ActiveMerchant #:nodoc:
|
|
154
161
|
end
|
155
162
|
|
156
163
|
def refund_request(money, authorization, options)
|
157
|
-
commit('refund', build_refund_request(money, authorization, options), :ok, options)
|
164
|
+
commit('refund', build_refund_request(money, authorization, options), :ok, 'SENT_FOR_REFUND', options)
|
158
165
|
end
|
159
166
|
|
160
167
|
def credit_request(money, payment_method, options)
|
@@ -206,6 +213,7 @@ module ActiveMerchant #:nodoc:
|
|
206
213
|
end
|
207
214
|
add_payment_method(xml, money, payment_method, options)
|
208
215
|
add_shopper(xml, options)
|
216
|
+
add_statement_narrative(xml, options)
|
209
217
|
add_risk_data(xml, options[:risk_data]) if options[:risk_data]
|
210
218
|
add_hcg_additional_data(xml, options) if options[:hcg_additional_data]
|
211
219
|
add_instalments_data(xml, options) if options[:instalments]
|
@@ -232,13 +240,22 @@ module ActiveMerchant #:nodoc:
|
|
232
240
|
end
|
233
241
|
|
234
242
|
def build_void_request(authorization, options)
|
235
|
-
|
243
|
+
if options[:cancel_or_refund]
|
244
|
+
build_order_modify_request(authorization, &:cancelOrRefund)
|
245
|
+
else
|
246
|
+
build_order_modify_request(authorization, &:cancel)
|
247
|
+
end
|
236
248
|
end
|
237
249
|
|
238
250
|
def build_refund_request(money, authorization, options)
|
239
251
|
build_order_modify_request(authorization) do |xml|
|
240
|
-
|
241
|
-
|
252
|
+
if options[:cancel_or_refund]
|
253
|
+
# Worldpay docs claim amount must be passed. This causes an error.
|
254
|
+
xml.cancelOrRefund # { add_amount(xml, money, options.merge(debit_credit_indicator: 'credit')) }
|
255
|
+
else
|
256
|
+
xml.refund do
|
257
|
+
add_amount(xml, money, options.merge(debit_credit_indicator: 'credit'))
|
258
|
+
end
|
242
259
|
end
|
243
260
|
end
|
244
261
|
end
|
@@ -260,7 +277,10 @@ module ActiveMerchant #:nodoc:
|
|
260
277
|
end
|
261
278
|
|
262
279
|
def add_additional_3ds_data(xml, options)
|
263
|
-
|
280
|
+
additional_data = { 'dfReferenceId' => options[:session_id] }
|
281
|
+
additional_data['challengeWindowSize'] = options[:browser_size] if options[:browser_size]
|
282
|
+
|
283
|
+
xml.additional3DSData additional_data
|
264
284
|
end
|
265
285
|
|
266
286
|
def add_3ds_exemption(xml, options)
|
@@ -378,6 +398,8 @@ module ActiveMerchant #:nodoc:
|
|
378
398
|
add_amount(xml, amount, options)
|
379
399
|
end
|
380
400
|
end
|
401
|
+
elsif options[:payment_type] == :network_token
|
402
|
+
add_network_tokenization_card(xml, payment_method)
|
381
403
|
else
|
382
404
|
xml.paymentDetails credit_fund_transfer_attribute(options) do
|
383
405
|
if options[:payment_type] == :token
|
@@ -396,7 +418,6 @@ module ActiveMerchant #:nodoc:
|
|
396
418
|
xml.session 'shopperIPAddress' => options[:ip] if options[:ip]
|
397
419
|
xml.session 'id' => options[:session_id] if options[:session_id]
|
398
420
|
end
|
399
|
-
|
400
421
|
if three_d_secure = options[:three_d_secure]
|
401
422
|
add_three_d_secure(three_d_secure, xml)
|
402
423
|
end
|
@@ -404,10 +425,26 @@ module ActiveMerchant #:nodoc:
|
|
404
425
|
end
|
405
426
|
end
|
406
427
|
|
428
|
+
def add_network_tokenization_card(xml, payment_method)
|
429
|
+
xml.paymentDetails do
|
430
|
+
xml.tag! 'EMVCO_TOKEN-SSL', 'type' => 'NETWORKTOKEN' do
|
431
|
+
xml.tokenNumber payment_method.number
|
432
|
+
xml.expiryDate do
|
433
|
+
xml.date(
|
434
|
+
'month' => format(payment_method.month, :two_digits),
|
435
|
+
'year' => format(payment_method.year, :four_digits_year)
|
436
|
+
)
|
437
|
+
end
|
438
|
+
xml.cryptogram payment_method.payment_cryptogram
|
439
|
+
xml.eciIndicator format(payment_method.eci, :two_digits)
|
440
|
+
end
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
407
444
|
def add_three_d_secure(three_d_secure, xml)
|
408
445
|
xml.info3DSecure do
|
409
446
|
xml.threeDSVersion three_d_secure[:version]
|
410
|
-
if
|
447
|
+
if three_d_secure[:version] && three_d_secure[:ds_transaction_id]
|
411
448
|
xml.dsTransactionId three_d_secure[:ds_transaction_id]
|
412
449
|
else
|
413
450
|
xml.xid three_d_secure[:xid]
|
@@ -422,11 +459,11 @@ module ActiveMerchant #:nodoc:
|
|
422
459
|
xml.expiryDate do
|
423
460
|
xml.date(
|
424
461
|
'month' => format(payment_method.month, :two_digits),
|
425
|
-
'year' => format(payment_method.year, :
|
462
|
+
'year' => format(payment_method.year, :four_digits_year)
|
426
463
|
)
|
427
464
|
end
|
428
465
|
|
429
|
-
card_holder_name = options[:execute_threed] && !options[:three_ds_version]&.start_with?('2') ? '3D' : payment_method.name
|
466
|
+
card_holder_name = test? && options[:execute_threed] && !options[:three_ds_version]&.start_with?('2') ? '3D' : payment_method.name
|
430
467
|
xml.cardHolderName card_holder_name
|
431
468
|
xml.cvc payment_method.verification_value
|
432
469
|
|
@@ -482,6 +519,10 @@ module ActiveMerchant #:nodoc:
|
|
482
519
|
end
|
483
520
|
end
|
484
521
|
|
522
|
+
def add_statement_narrative(xml, options)
|
523
|
+
xml.statementNarrative truncate(options[:statement_narrative], 50) if options[:statement_narrative]
|
524
|
+
end
|
525
|
+
|
485
526
|
def add_authenticated_shopper_id(xml, options)
|
486
527
|
xml.authenticatedShopperID options[:customer] if options[:customer]
|
487
528
|
end
|
@@ -535,11 +576,10 @@ module ActiveMerchant #:nodoc:
|
|
535
576
|
|
536
577
|
def default_address
|
537
578
|
{
|
538
|
-
address1: 'N/A',
|
539
579
|
zip: '0000',
|
580
|
+
country: 'US',
|
540
581
|
city: 'N/A',
|
541
|
-
|
542
|
-
country: 'US'
|
582
|
+
address1: 'N/A'
|
543
583
|
}
|
544
584
|
end
|
545
585
|
|
@@ -651,7 +691,9 @@ module ActiveMerchant #:nodoc:
|
|
651
691
|
# - An array of strings if one of many responses could be considered a
|
652
692
|
# success.
|
653
693
|
def success_criteria_success?(raw, success_criteria)
|
654
|
-
|
694
|
+
return if raw[:error]
|
695
|
+
|
696
|
+
raw[:ok].present? || (success_criteria.include?(raw[:last_event]) if raw[:last_event])
|
655
697
|
end
|
656
698
|
|
657
699
|
def action_success?(action, raw)
|
@@ -714,7 +756,9 @@ module ActiveMerchant #:nodoc:
|
|
714
756
|
|
715
757
|
def payment_details_from(payment_method)
|
716
758
|
payment_details = {}
|
717
|
-
if payment_method.
|
759
|
+
if payment_method.is_a?(NetworkTokenizationCreditCard) && payment_method.source == :network_token
|
760
|
+
payment_details[:payment_type] = :network_token
|
761
|
+
elsif payment_method.respond_to?(:number)
|
718
762
|
payment_details[:payment_type] = :credit
|
719
763
|
else
|
720
764
|
token_details = token_details_from_authorization(payment_method)
|
@@ -750,6 +794,10 @@ module ActiveMerchant #:nodoc:
|
|
750
794
|
def card_code_for(payment_method)
|
751
795
|
CARD_CODES[card_brand(payment_method)] || CARD_CODES['unknown']
|
752
796
|
end
|
797
|
+
|
798
|
+
def eligible_for_0_auth?(payment_method, options = {})
|
799
|
+
payment_method.is_a?(CreditCard) && %w(visa master).include?(payment_method.brand) && options[:zero_dollar_auth]
|
800
|
+
end
|
753
801
|
end
|
754
802
|
end
|
755
803
|
end
|
@@ -4,7 +4,7 @@ module ActiveMerchant #:nodoc:
|
|
4
4
|
end
|
5
5
|
|
6
6
|
class Response
|
7
|
-
attr_reader :params, :message, :test, :authorization, :avs_result, :cvv_result, :error_code, :emv_authorization
|
7
|
+
attr_reader :params, :message, :test, :authorization, :avs_result, :cvv_result, :error_code, :emv_authorization, :network_transaction_id
|
8
8
|
|
9
9
|
def success?
|
10
10
|
@success
|
@@ -25,6 +25,7 @@ module ActiveMerchant #:nodoc:
|
|
25
25
|
@fraud_review = options[:fraud_review]
|
26
26
|
@error_code = options[:error_code]
|
27
27
|
@emv_authorization = options[:emv_authorization]
|
28
|
+
@network_transaction_id = options[:network_transaction_id]
|
28
29
|
|
29
30
|
@avs_result = if options[:avs_result].kind_of?(AVSResult)
|
30
31
|
options[:avs_result].to_hash
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ActiveMerchant
|
2
|
+
module Billing
|
3
|
+
module ThreeDSecureEciMapper
|
4
|
+
NON_THREE_D_SECURE_TRANSACTION = :non_three_d_secure_transaction
|
5
|
+
ATTEMPTED_AUTHENTICATION_TRANSACTION = :attempted_authentication_transaction
|
6
|
+
FULLY_AUTHENTICATED_TRANSACTION = :fully_authenticated_transaction
|
7
|
+
|
8
|
+
ECI_00_01_02_MAP = { '00' => NON_THREE_D_SECURE_TRANSACTION, '01' => ATTEMPTED_AUTHENTICATION_TRANSACTION, '02' => FULLY_AUTHENTICATED_TRANSACTION }.freeze
|
9
|
+
ECI_05_06_07_MAP = { '05' => FULLY_AUTHENTICATED_TRANSACTION, '06' => ATTEMPTED_AUTHENTICATION_TRANSACTION, '07' => NON_THREE_D_SECURE_TRANSACTION }.freeze
|
10
|
+
BRAND_TO_ECI_MAP = {
|
11
|
+
american_express: ECI_05_06_07_MAP,
|
12
|
+
dankort: ECI_05_06_07_MAP,
|
13
|
+
diners_club: ECI_05_06_07_MAP,
|
14
|
+
discover: ECI_05_06_07_MAP,
|
15
|
+
elo: ECI_05_06_07_MAP,
|
16
|
+
jcb: ECI_05_06_07_MAP,
|
17
|
+
maestro: ECI_00_01_02_MAP,
|
18
|
+
master: ECI_00_01_02_MAP,
|
19
|
+
visa: ECI_05_06_07_MAP
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
def self.map(brand, eci)
|
23
|
+
BRAND_TO_ECI_MAP.dig(brand, eci)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|