activemerchant 1.126.0 → 1.129.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 +241 -0
- data/lib/active_merchant/billing/check.rb +40 -8
- data/lib/active_merchant/billing/credit_card.rb +28 -1
- data/lib/active_merchant/billing/credit_card_methods.rb +79 -23
- data/lib/active_merchant/billing/gateways/adyen.rb +67 -8
- data/lib/active_merchant/billing/gateways/airwallex.rb +40 -11
- data/lib/active_merchant/billing/gateways/alelo.rb +256 -0
- data/lib/active_merchant/billing/gateways/authorize_net.rb +21 -4
- data/lib/active_merchant/billing/gateways/beanstream.rb +18 -0
- data/lib/active_merchant/billing/gateways/blue_snap.rb +22 -1
- data/lib/active_merchant/billing/gateways/bogus.rb +4 -0
- data/lib/active_merchant/billing/gateways/borgun.rb +56 -16
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +64 -17
- data/lib/active_merchant/billing/gateways/card_connect.rb +27 -9
- data/lib/active_merchant/billing/gateways/card_stream.rb +23 -0
- data/lib/active_merchant/billing/gateways/checkout_v2.rb +228 -57
- data/lib/active_merchant/billing/gateways/commerce_hub.rb +361 -0
- data/lib/active_merchant/billing/gateways/credorax.rb +47 -27
- data/lib/active_merchant/billing/gateways/cyber_source/cyber_source_common.rb +36 -0
- data/lib/active_merchant/billing/gateways/cyber_source.rb +100 -26
- data/lib/active_merchant/billing/gateways/cyber_source_rest.rb +456 -0
- data/lib/active_merchant/billing/gateways/d_local.rb +44 -5
- data/lib/active_merchant/billing/gateways/decidir.rb +15 -4
- data/lib/active_merchant/billing/gateways/ebanx.rb +36 -24
- data/lib/active_merchant/billing/gateways/element.rb +21 -1
- data/lib/active_merchant/billing/gateways/global_collect.rb +73 -22
- data/lib/active_merchant/billing/gateways/ipg.rb +13 -8
- data/lib/active_merchant/billing/gateways/iveri.rb +39 -3
- data/lib/active_merchant/billing/gateways/kushki.rb +21 -1
- data/lib/active_merchant/billing/gateways/litle.rb +25 -5
- data/lib/active_merchant/billing/gateways/mastercard.rb +1 -8
- data/lib/active_merchant/billing/gateways/mercado_pago.rb +17 -0
- data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +44 -10
- data/lib/active_merchant/billing/gateways/monei.rb +2 -0
- data/lib/active_merchant/billing/gateways/moneris.rb +20 -5
- data/lib/active_merchant/billing/gateways/mundipagg.rb +3 -0
- data/lib/active_merchant/billing/gateways/ogone.rb +35 -7
- data/lib/active_merchant/billing/gateways/openpay.rb +20 -3
- data/lib/active_merchant/billing/gateways/orbital.rb +43 -22
- data/lib/active_merchant/billing/gateways/pay_trace.rb +64 -18
- data/lib/active_merchant/billing/gateways/payeezy.rb +59 -4
- data/lib/active_merchant/billing/gateways/paymentez.rb +18 -6
- data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +4 -0
- data/lib/active_merchant/billing/gateways/paysafe.rb +22 -14
- data/lib/active_merchant/billing/gateways/payu_latam.rb +3 -0
- data/lib/active_merchant/billing/gateways/plexo.rb +308 -0
- data/lib/active_merchant/billing/gateways/priority.rb +29 -6
- data/lib/active_merchant/billing/gateways/rapyd.rb +110 -49
- data/lib/active_merchant/billing/gateways/reach.rb +277 -0
- data/lib/active_merchant/billing/gateways/redsys.rb +9 -5
- data/lib/active_merchant/billing/gateways/sage_pay.rb +1 -1
- data/lib/active_merchant/billing/gateways/securion_pay.rb +40 -0
- data/lib/active_merchant/billing/gateways/shift4.rb +342 -0
- data/lib/active_merchant/billing/gateways/simetrik.rb +28 -22
- data/lib/active_merchant/billing/gateways/stripe.rb +21 -1
- data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +62 -22
- data/lib/active_merchant/billing/gateways/tns.rb +2 -5
- data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +1 -1
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +14 -3
- data/lib/active_merchant/billing/gateways/vanco.rb +12 -3
- data/lib/active_merchant/billing/gateways/visanet_peru.rb +1 -1
- data/lib/active_merchant/billing/gateways/vpos.rb +7 -4
- data/lib/active_merchant/billing/gateways/wompi.rb +8 -4
- data/lib/active_merchant/billing/gateways/worldpay.rb +117 -9
- data/lib/active_merchant/billing/response.rb +15 -1
- data/lib/active_merchant/connection.rb +0 -2
- data/lib/active_merchant/country.rb +1 -0
- data/lib/active_merchant/errors.rb +4 -1
- data/lib/active_merchant/version.rb +1 -1
- metadata +24 -3
@@ -0,0 +1,277 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
class ReachGateway < Gateway
|
4
|
+
self.test_url = 'https://checkout.rch.how/'
|
5
|
+
self.live_url = 'https://checkout.rch.io/'
|
6
|
+
|
7
|
+
self.supported_countries = ['US']
|
8
|
+
self.default_currency = 'USD'
|
9
|
+
self.supported_cardtypes = %i[visa diners_club american_express jcb master discover maestro]
|
10
|
+
|
11
|
+
self.homepage_url = 'https://www.withreach.com/'
|
12
|
+
self.display_name = 'Reach'
|
13
|
+
self.currencies_without_fractions = %w(BIF BYR CLF CLP CVE DJF GNF ISK JPY KMF KRW PYG RWF UGX UYI VND VUV XAF XOF XPF IDR MGA MRO)
|
14
|
+
|
15
|
+
API_VERSION = 'v2.22'.freeze
|
16
|
+
STANDARD_ERROR_CODE_MAPPING = {}
|
17
|
+
PAYMENT_METHOD_MAP = {
|
18
|
+
american_express: 'AMEX',
|
19
|
+
cabal: 'CABAL',
|
20
|
+
check: 'ACH',
|
21
|
+
dankort: 'DANKORT',
|
22
|
+
diners_club: 'DINERS',
|
23
|
+
discover: 'DISC',
|
24
|
+
elo: 'ELO',
|
25
|
+
jcb: 'JCB',
|
26
|
+
maestro: 'MAESTRO',
|
27
|
+
master: 'MC',
|
28
|
+
naranja: 'NARANJA',
|
29
|
+
union_pay: 'UNIONPAY',
|
30
|
+
visa: 'VISA'
|
31
|
+
}
|
32
|
+
|
33
|
+
def initialize(options = {})
|
34
|
+
requires!(options, :merchant_id, :secret)
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
def authorize(money, payment, options = {})
|
39
|
+
request = build_checkout_request(money, payment, options)
|
40
|
+
add_custom_fields_data(request, options)
|
41
|
+
add_customer_data(request, options, payment)
|
42
|
+
add_stored_credentials(request, options)
|
43
|
+
post = { request: request, card: add_payment(payment, options) }
|
44
|
+
if options[:stored_credential]
|
45
|
+
MultiResponse.run(:use_first_response) do |r|
|
46
|
+
r.process { commit('checkout', post) }
|
47
|
+
r.process do
|
48
|
+
r2 = get_network_payment_reference(r.responses[0])
|
49
|
+
r.params[:network_transaction_id] = r2.message
|
50
|
+
r2
|
51
|
+
end
|
52
|
+
end
|
53
|
+
else
|
54
|
+
commit('checkout', post)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def purchase(money, payment, options = {})
|
59
|
+
options[:capture] = true
|
60
|
+
authorize(money, payment, options)
|
61
|
+
end
|
62
|
+
|
63
|
+
def capture(money, authorization, options = {})
|
64
|
+
post = { request: { MerchantId: @options[:merchant_id], OrderId: authorization } }
|
65
|
+
commit('capture', post)
|
66
|
+
end
|
67
|
+
|
68
|
+
def supports_scrubbing?
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
def scrub(transcript)
|
73
|
+
transcript.
|
74
|
+
gsub(%r(((MerchantId)[% \w]+[%]\d{2})[\w -]+), '\1[FILTERED]').
|
75
|
+
gsub(%r((signature=)[\w%]+), '\1[FILTERED]\2').
|
76
|
+
gsub(%r((Number%22%3A%22)[\d]+), '\1[FILTERED]\2').
|
77
|
+
gsub(%r((VerificationCode%22%3A)[\d]+), '\1[FILTERED]\2')
|
78
|
+
end
|
79
|
+
|
80
|
+
def refund(amount, authorization, options = {})
|
81
|
+
currency = options[:currency] || currency(options[:amount])
|
82
|
+
post = {
|
83
|
+
request: {
|
84
|
+
MerchantId: @options[:merchant_id],
|
85
|
+
OrderId: authorization,
|
86
|
+
ReferenceId: options[:order_id] || options[:reference_id],
|
87
|
+
Amount: localized_amount(amount, currency)
|
88
|
+
}
|
89
|
+
}
|
90
|
+
commit('refund', post)
|
91
|
+
end
|
92
|
+
|
93
|
+
def void(authorization, options = {})
|
94
|
+
post = {
|
95
|
+
request: {
|
96
|
+
MerchantId: @options[:merchant_id],
|
97
|
+
OrderId: authorization
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
commit('cancel', post)
|
102
|
+
end
|
103
|
+
|
104
|
+
def verify(credit_card, options = {})
|
105
|
+
MultiResponse.run(:use_first_response) do |r|
|
106
|
+
r.process { authorize(100, credit_card, options) }
|
107
|
+
r.process(:ignore_result) { void(r.authorization, options) }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def build_checkout_request(amount, payment, options)
|
114
|
+
currency = options[:currency] || currency(options[:amount])
|
115
|
+
{
|
116
|
+
MerchantId: @options[:merchant_id],
|
117
|
+
ReferenceId: options[:order_id],
|
118
|
+
ConsumerCurrency: currency,
|
119
|
+
Capture: options[:capture] || false,
|
120
|
+
PaymentMethod: PAYMENT_METHOD_MAP.fetch(payment.brand.to_sym, 'unsupported'),
|
121
|
+
Items: [
|
122
|
+
Sku: options[:item_sku] || SecureRandom.alphanumeric,
|
123
|
+
ConsumerPrice: localized_amount(amount, currency),
|
124
|
+
Quantity: (options[:item_quantity] || 1)
|
125
|
+
]
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
def add_payment(payment, options)
|
130
|
+
ntid = options.dig(:stored_credential, :network_transaction_id)
|
131
|
+
cvv_or_previos_reference = (ntid ? { PreviousNetworkPaymentReference: ntid } : { VerificationCode: payment.verification_value })
|
132
|
+
{
|
133
|
+
Name: payment.name,
|
134
|
+
Number: payment.number,
|
135
|
+
Expiry: { Month: payment.month, Year: payment.year }
|
136
|
+
}.merge!(cvv_or_previos_reference)
|
137
|
+
end
|
138
|
+
|
139
|
+
def add_customer_data(request, options, payment)
|
140
|
+
address = options[:billing_address] || options[:address]
|
141
|
+
|
142
|
+
return if address.blank?
|
143
|
+
|
144
|
+
request[:Consumer] = {
|
145
|
+
Name: payment.respond_to?(:name) ? payment.name : address[:name],
|
146
|
+
Email: options[:email],
|
147
|
+
Address: address[:address1],
|
148
|
+
City: address[:city],
|
149
|
+
Country: address[:country]
|
150
|
+
}.compact
|
151
|
+
end
|
152
|
+
|
153
|
+
def add_stored_credentials(request, options)
|
154
|
+
request[:PaymentModel] = payment_model(options) || ''
|
155
|
+
request[:DeviceFingerprint] = options[:device_fingerprint] if options[:device_fingerprint]
|
156
|
+
end
|
157
|
+
|
158
|
+
def payment_model(options)
|
159
|
+
stored_credential = options[:stored_credential]
|
160
|
+
return options[:payment_model] if options[:payment_model]
|
161
|
+
return 'CIT-One-Time' unless stored_credential
|
162
|
+
|
163
|
+
payment_model_options = {
|
164
|
+
initial_transaction: {
|
165
|
+
'cardholder' => {
|
166
|
+
'installment' => 'CIT-Setup-Scheduled',
|
167
|
+
'unscheduled' => 'CIT-Setup-Unscheduled-MIT',
|
168
|
+
'recurring' => 'CIT-Setup-Unscheduled'
|
169
|
+
}
|
170
|
+
},
|
171
|
+
no_initial_transaction: {
|
172
|
+
'cardholder' => {
|
173
|
+
'unscheduled' => 'CIT-Subsequent-Unscheduled'
|
174
|
+
},
|
175
|
+
'merchant' => {
|
176
|
+
'recurring' => 'MIT-Subsequent-Scheduled',
|
177
|
+
'unscheduled' => 'MIT-Subsequent-Unscheduled'
|
178
|
+
}
|
179
|
+
}
|
180
|
+
}
|
181
|
+
initial = stored_credential[:initial_transaction] ? :initial_transaction : :no_initial_transaction
|
182
|
+
payment_model_options[initial].dig(stored_credential[:initiator], stored_credential[:reason_type])
|
183
|
+
end
|
184
|
+
|
185
|
+
def add_custom_fields_data(request, options)
|
186
|
+
add_shipping_data(request, options) if options[:taxes].present?
|
187
|
+
request[:RateOfferId] = options[:rate_offer_id] if options[:rate_offer_id].present?
|
188
|
+
request[:Items] = options[:items] if options[:items].present?
|
189
|
+
end
|
190
|
+
|
191
|
+
def add_shipping_data(request, options)
|
192
|
+
request[:Shipping] = {
|
193
|
+
ConsumerPrice: options[:price],
|
194
|
+
ConsumerTaxes: options[:taxes],
|
195
|
+
ConsumerDuty: options[:duty]
|
196
|
+
}
|
197
|
+
request[:Consignee] = {
|
198
|
+
Name: options[:consignee_name],
|
199
|
+
Address: options[:consignee_address],
|
200
|
+
City: options[:consignee_city],
|
201
|
+
Country: options[:consignee_country]
|
202
|
+
}
|
203
|
+
end
|
204
|
+
|
205
|
+
def sign_body(body)
|
206
|
+
Base64.strict_encode64(OpenSSL::HMAC.digest('sha256', @options[:secret].encode('utf-8'), body.encode('utf-8')))
|
207
|
+
end
|
208
|
+
|
209
|
+
def parse(body)
|
210
|
+
hash_response = URI.decode_www_form(body).to_h
|
211
|
+
hash_response['response'] = JSON.parse(hash_response['response'])
|
212
|
+
|
213
|
+
hash_response
|
214
|
+
end
|
215
|
+
|
216
|
+
def format_and_sign(post)
|
217
|
+
post[:request] = post[:request].to_json
|
218
|
+
post[:card] = post[:card].to_json if post[:card].present?
|
219
|
+
post[:signature] = sign_body(post[:request])
|
220
|
+
post
|
221
|
+
end
|
222
|
+
|
223
|
+
def get_network_payment_reference(response)
|
224
|
+
parameters = { request: { MerchantId: @options[:merchant_id], OrderId: response.params['response']['OrderId'] } }
|
225
|
+
body = post_data format_and_sign(parameters)
|
226
|
+
|
227
|
+
raw_response = ssl_request :post, url('query'), body, {}
|
228
|
+
response = parse(raw_response)
|
229
|
+
message = response.dig('response', 'Payment', 'NetworkPaymentReference')
|
230
|
+
Response.new(true, message, {})
|
231
|
+
end
|
232
|
+
|
233
|
+
def commit(action, parameters)
|
234
|
+
body = post_data format_and_sign(parameters)
|
235
|
+
raw_response = ssl_post url(action), body
|
236
|
+
response = parse(raw_response)
|
237
|
+
|
238
|
+
Response.new(
|
239
|
+
success_from(response),
|
240
|
+
message_from(response) || '',
|
241
|
+
response,
|
242
|
+
authorization: authorization_from(response['response']),
|
243
|
+
# avs_result: AVSResult.new(code: response['some_avs_response_key']),
|
244
|
+
# cvv_result: CVVResult.new(response['some_cvv_response_key']),
|
245
|
+
test: test?,
|
246
|
+
error_code: error_code_from(response)
|
247
|
+
)
|
248
|
+
rescue ActiveMerchant::ResponseError => e
|
249
|
+
Response.new(false, (e.response.body.present? ? e.response.body : e.response.msg), {}, test: test?)
|
250
|
+
end
|
251
|
+
|
252
|
+
def success_from(response)
|
253
|
+
response.dig('response', 'Error').blank?
|
254
|
+
end
|
255
|
+
|
256
|
+
def message_from(response)
|
257
|
+
success_from(response) ? '' : response.dig('response', 'Error', 'ReasonCode')
|
258
|
+
end
|
259
|
+
|
260
|
+
def authorization_from(response)
|
261
|
+
response['OrderId']
|
262
|
+
end
|
263
|
+
|
264
|
+
def post_data(params)
|
265
|
+
params.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&')
|
266
|
+
end
|
267
|
+
|
268
|
+
def error_code_from(response)
|
269
|
+
response['response']['Error']['Code'] unless success_from(response)
|
270
|
+
end
|
271
|
+
|
272
|
+
def url(action)
|
273
|
+
"#{test? ? test_url : live_url}#{API_VERSION}/#{action}"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
@@ -91,7 +91,7 @@ module ActiveMerchant #:nodoc:
|
|
91
91
|
# More operations are supported by the gateway itself, but
|
92
92
|
# are not supported in this library.
|
93
93
|
SUPPORTED_TRANSACTIONS = {
|
94
|
-
purchase: '
|
94
|
+
purchase: '0',
|
95
95
|
authorize: '1',
|
96
96
|
capture: '2',
|
97
97
|
refund: '3',
|
@@ -266,9 +266,13 @@ module ActiveMerchant #:nodoc:
|
|
266
266
|
end
|
267
267
|
|
268
268
|
def verify(creditcard, options = {})
|
269
|
-
|
270
|
-
|
271
|
-
|
269
|
+
if options[:sca_exemption_behavior_override] == 'endpoint_and_ntid'
|
270
|
+
purchase(0, creditcard, options)
|
271
|
+
else
|
272
|
+
MultiResponse.run(:use_first_response) do |r|
|
273
|
+
r.process { authorize(100, creditcard, options) }
|
274
|
+
r.process(:ignore_result) { void(r.authorization, options) }
|
275
|
+
end
|
272
276
|
end
|
273
277
|
end
|
274
278
|
|
@@ -685,7 +689,7 @@ module ActiveMerchant #:nodoc:
|
|
685
689
|
cipher = OpenSSL::Cipher.new('DES3')
|
686
690
|
cipher.encrypt
|
687
691
|
|
688
|
-
cipher.key = Base64.
|
692
|
+
cipher.key = Base64.urlsafe_decode64(key)
|
689
693
|
# The OpenSSL default of an all-zeroes ("\\0") IV is used.
|
690
694
|
cipher.padding = 0
|
691
695
|
|
@@ -427,7 +427,7 @@ module ActiveMerchant #:nodoc:
|
|
427
427
|
def past_purchase_reference?(payment_method)
|
428
428
|
return false unless payment_method.is_a?(String)
|
429
429
|
|
430
|
-
payment_method.split(';').last
|
430
|
+
%w(purchase repeat).include?(payment_method.split(';').last)
|
431
431
|
end
|
432
432
|
end
|
433
433
|
end
|
@@ -133,6 +133,7 @@ module ActiveMerchant #:nodoc:
|
|
133
133
|
add_creditcard(post, payment, options)
|
134
134
|
add_customer(post, payment, options)
|
135
135
|
add_customer_data(post, options)
|
136
|
+
add_external_three_ds(post, options)
|
136
137
|
if options[:email]
|
137
138
|
post[:metadata] = {}
|
138
139
|
post[:metadata][:email] = options[:email]
|
@@ -140,6 +141,40 @@ module ActiveMerchant #:nodoc:
|
|
140
141
|
post
|
141
142
|
end
|
142
143
|
|
144
|
+
def add_external_three_ds(post, options)
|
145
|
+
return if options[:three_d_secure].blank?
|
146
|
+
|
147
|
+
post[:threeDSecure] = {
|
148
|
+
external: {
|
149
|
+
version: options[:three_d_secure][:version],
|
150
|
+
authenticationValue: options[:three_d_secure][:cavv],
|
151
|
+
acsTransactionId: options[:three_d_secure][:acs_transaction_id],
|
152
|
+
status: options[:three_d_secure][:authentication_response_status],
|
153
|
+
eci: options[:three_d_secure][:eci]
|
154
|
+
}.merge(xid_or_ds_trans_id(options[:three_d_secure]))
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
158
|
+
def xid_or_ds_trans_id(three_ds)
|
159
|
+
if three_ds[:version].to_f >= 2.0
|
160
|
+
{ dsTransactionId: three_ds[:ds_transaction_id] }
|
161
|
+
else
|
162
|
+
{ xid: three_ds[:xid] }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def validate_three_ds_params(three_ds)
|
167
|
+
errors = {}
|
168
|
+
supported_version = %w{1.0.2 2.1.0 2.2.0}.include?(three_ds[:version])
|
169
|
+
supported_auth_response = ['Y', 'N', 'U', 'R', 'E', 'A', nil].include?(three_ds[:status])
|
170
|
+
|
171
|
+
errors[:three_ds_version] = 'ThreeDs version not supported' unless supported_version
|
172
|
+
errors[:auth_response] = 'Authentication response value not supported' unless supported_auth_response
|
173
|
+
errors.compact!
|
174
|
+
|
175
|
+
errors.present? ? Response.new(false, 'ThreeDs data is invalid', errors) : nil
|
176
|
+
end
|
177
|
+
|
143
178
|
def add_amount(post, money, options, include_currency = false)
|
144
179
|
currency = (options[:currency] || default_currency)
|
145
180
|
post[:amount] = localized_amount(money, currency)
|
@@ -182,6 +217,11 @@ module ActiveMerchant #:nodoc:
|
|
182
217
|
end
|
183
218
|
|
184
219
|
def commit(url, parameters = nil, options = {}, method = nil)
|
220
|
+
if parameters.present? && parameters[:threeDSecure].present?
|
221
|
+
three_ds_errors = validate_three_ds_params(parameters[:threeDSecure][:external])
|
222
|
+
return three_ds_errors if three_ds_errors
|
223
|
+
end
|
224
|
+
|
185
225
|
response = api_request(url, parameters, options, method)
|
186
226
|
success = !response.key?('error')
|
187
227
|
|