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
@@ -0,0 +1,404 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
class PayTraceGateway < Gateway
|
4
|
+
self.test_url = 'https://api.paytrace.com'
|
5
|
+
self.live_url = 'https://api.paytrace.com'
|
6
|
+
|
7
|
+
self.supported_countries = ['US']
|
8
|
+
self.default_currency = 'USD'
|
9
|
+
self.supported_cardtypes = %i[visa master american_express discover]
|
10
|
+
|
11
|
+
self.homepage_url = 'https://paytrace.com/'
|
12
|
+
self.display_name = 'PayTrace'
|
13
|
+
|
14
|
+
# Response codes based on API Response Codes found here: https://developers.paytrace.com/support/home#14000041297
|
15
|
+
STANDARD_ERROR_CODE_MAPPING = {
|
16
|
+
'1' => STANDARD_ERROR_CODE[:error_occurred],
|
17
|
+
'102' => STANDARD_ERROR_CODE[:declined],
|
18
|
+
'103' => STANDARD_ERROR_CODE[:auto_voided],
|
19
|
+
'107' => STANDARD_ERROR_CODE[:unsuccessful_refund],
|
20
|
+
'108' => STANDARD_ERROR_CODE[:test_refund],
|
21
|
+
'110' => STANDARD_ERROR_CODE[:unsuccessful_void],
|
22
|
+
'113' => STANDARD_ERROR_CODE[:unsuccessful_capture]
|
23
|
+
}
|
24
|
+
|
25
|
+
ENDPOINTS = {
|
26
|
+
customer_id_sale: 'transactions/sale/by_customer',
|
27
|
+
keyed_sale: 'transactions/sale/keyed',
|
28
|
+
customer_id_auth: 'transactions/authorization/by_customer',
|
29
|
+
keyed_auth: 'transactions/authorization/keyed',
|
30
|
+
capture: 'transactions/authorization/capture',
|
31
|
+
transaction_refund: 'transactions/refund/for_transaction',
|
32
|
+
transaction_void: 'transactions/void',
|
33
|
+
store: 'customer/create',
|
34
|
+
redact: 'customer/delete',
|
35
|
+
level_3_visa: 'level_three/visa',
|
36
|
+
level_3_mastercard: 'level_three/mastercard'
|
37
|
+
}
|
38
|
+
|
39
|
+
def initialize(options = {})
|
40
|
+
requires!(options, :username, :password, :integrator_id)
|
41
|
+
super
|
42
|
+
acquire_access_token
|
43
|
+
end
|
44
|
+
|
45
|
+
def purchase(money, payment_or_customer_id, options = {})
|
46
|
+
if visa_or_mastercard?(options)
|
47
|
+
MultiResponse.run(:use_first_response) do |r|
|
48
|
+
endpoint = customer_id?(payment_or_customer_id) ? ENDPOINTS[:customer_id_sale] : ENDPOINTS[:keyed_sale]
|
49
|
+
|
50
|
+
r.process { commit(endpoint, build_purchase_request(money, payment_or_customer_id, options)) }
|
51
|
+
r.process { commit(ENDPOINTS[:"level_3_#{options[:visa_or_mastercard]}"], send_level_3_data(r, options)) }
|
52
|
+
end
|
53
|
+
else
|
54
|
+
post = build_purchase_request(money, payment_or_customer_id, options)
|
55
|
+
post[:customer_id] ? endpoint = ENDPOINTS[:customer_id_sale] : endpoint = ENDPOINTS[:keyed_sale]
|
56
|
+
response = commit(endpoint, post)
|
57
|
+
check_token_response(response, endpoint, post, options)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def authorize(money, payment_or_customer_id, options = {})
|
62
|
+
post = {}
|
63
|
+
add_amount(post, money, options)
|
64
|
+
if customer_id?(payment_or_customer_id)
|
65
|
+
post[:customer_id] = payment_or_customer_id
|
66
|
+
endpoint = ENDPOINTS[:customer_id_auth]
|
67
|
+
else
|
68
|
+
add_payment(post, payment_or_customer_id)
|
69
|
+
add_address(post, payment_or_customer_id, options)
|
70
|
+
add_customer_data(post, options)
|
71
|
+
endpoint = ENDPOINTS[:keyed_auth]
|
72
|
+
end
|
73
|
+
response = commit(endpoint, post)
|
74
|
+
check_token_response(response, endpoint, post, options)
|
75
|
+
end
|
76
|
+
|
77
|
+
def capture(money, authorization, options = {})
|
78
|
+
if visa_or_mastercard?(options)
|
79
|
+
MultiResponse.run do |r|
|
80
|
+
r.process { commit(ENDPOINTS[:capture], build_capture_request(money, authorization, options)) }
|
81
|
+
r.process { commit(ENDPOINTS[:"level_3_#{options[:visa_or_mastercard]}"], send_level_3_data(r, options)) }
|
82
|
+
end
|
83
|
+
else
|
84
|
+
post = build_capture_request(money, authorization, options)
|
85
|
+
response = commit(ENDPOINTS[:capture], post)
|
86
|
+
check_token_response(response, ENDPOINTS[:capture], post, options)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def refund(money, authorization, options = {})
|
91
|
+
# currently only support full and partial refunds of settled transactions via a transaction ID
|
92
|
+
post = {}
|
93
|
+
add_amount(post, money, options)
|
94
|
+
post[:transaction_id] = authorization
|
95
|
+
response = commit(ENDPOINTS[:transaction_refund], post)
|
96
|
+
check_token_response(response, ENDPOINTS[:transaction_refund], post, options)
|
97
|
+
end
|
98
|
+
|
99
|
+
def void(authorization, options = {})
|
100
|
+
post = {}
|
101
|
+
post[:transaction_id] = authorization
|
102
|
+
|
103
|
+
response = commit(ENDPOINTS[:transaction_void], post)
|
104
|
+
check_token_response(response, ENDPOINTS[:transaction_void], post, options)
|
105
|
+
end
|
106
|
+
|
107
|
+
def verify(credit_card, options = {})
|
108
|
+
authorize(0, credit_card, options)
|
109
|
+
end
|
110
|
+
|
111
|
+
# The customer_IDs that come from storing cards can be used for auth and purchase transaction types
|
112
|
+
def store(credit_card, options = {})
|
113
|
+
post = {}
|
114
|
+
post[:customer_id] = options[:customer_id] || SecureRandom.hex(12)
|
115
|
+
add_payment(post, credit_card)
|
116
|
+
add_address(post, credit_card, options)
|
117
|
+
response = commit(ENDPOINTS[:store], post)
|
118
|
+
check_token_response(response, ENDPOINTS[:store], post, options)
|
119
|
+
end
|
120
|
+
|
121
|
+
def redact(customer_id)
|
122
|
+
post = {}
|
123
|
+
post[:customer_id] = customer_id
|
124
|
+
response = commit(ENDPOINTS[:redact], post)
|
125
|
+
check_token_response(response, ENDPOINTS[:redact], post, options)
|
126
|
+
end
|
127
|
+
|
128
|
+
def supports_scrubbing?
|
129
|
+
true
|
130
|
+
end
|
131
|
+
|
132
|
+
def scrub(transcript)
|
133
|
+
transcript.
|
134
|
+
gsub(%r((Authorization: Bearer )[a-zA-Z0-9:_]+), '\1[FILTERED]').
|
135
|
+
gsub(%r(("credit_card\\?":{\\?"number\\?":\\?")\d+), '\1[FILTERED]').
|
136
|
+
gsub(%r(("cvv\\?":\\?")\d+), '\1[FILTERED]').
|
137
|
+
gsub(%r(("username\\?":\\?")\w+@+\w+.+\w+), '\1[FILTERED]').
|
138
|
+
gsub(%r(("password\\?":\\?")\w+), '\1[FILTERED]').
|
139
|
+
gsub(%r(("integrator_id\\?":\\?")\w+), '\1[FILTERED]')
|
140
|
+
end
|
141
|
+
|
142
|
+
def acquire_access_token
|
143
|
+
post = {}
|
144
|
+
post[:grant_type] = 'password'
|
145
|
+
post[:username] = @options[:username]
|
146
|
+
post[:password] = @options[:password]
|
147
|
+
data = post.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&')
|
148
|
+
url = live_url + '/oauth/token'
|
149
|
+
oauth_headers = {
|
150
|
+
'Accept' => '*/*',
|
151
|
+
'Content-Type' => 'application/x-www-form-urlencoded'
|
152
|
+
}
|
153
|
+
response = ssl_post(url, data, oauth_headers)
|
154
|
+
json_response = JSON.parse(response)
|
155
|
+
|
156
|
+
@options[:access_token] = json_response['access_token'] if json_response['access_token']
|
157
|
+
response
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
def build_purchase_request(money, payment_or_customer_id, options)
|
163
|
+
post = {}
|
164
|
+
add_amount(post, money, options)
|
165
|
+
if customer_id?(payment_or_customer_id)
|
166
|
+
post[:customer_id] = payment_or_customer_id
|
167
|
+
else
|
168
|
+
add_payment(post, payment_or_customer_id)
|
169
|
+
add_address(post, payment_or_customer_id, options)
|
170
|
+
add_customer_data(post, options)
|
171
|
+
end
|
172
|
+
|
173
|
+
post
|
174
|
+
end
|
175
|
+
|
176
|
+
def build_capture_request(money, authorization, options)
|
177
|
+
post = {}
|
178
|
+
post[:transaction_id] = authorization
|
179
|
+
add_amount(post, money, options)
|
180
|
+
|
181
|
+
post
|
182
|
+
end
|
183
|
+
|
184
|
+
# method can only be used to add level 3 data to any approved and unsettled sale transaction so it is built into the standard purchase workflow above
|
185
|
+
def send_level_3_data(response, options)
|
186
|
+
post = {}
|
187
|
+
post[:transaction_id] = response.authorization
|
188
|
+
add_level_3_data(post, options)
|
189
|
+
|
190
|
+
post
|
191
|
+
end
|
192
|
+
|
193
|
+
def visa_or_mastercard?(options)
|
194
|
+
return false unless options[:visa_or_mastercard]
|
195
|
+
|
196
|
+
options[:visa_or_mastercard] == 'visa' || options[:visa_or_mastercard] == 'mastercard'
|
197
|
+
end
|
198
|
+
|
199
|
+
def customer_id?(payment_or_customer_id)
|
200
|
+
payment_or_customer_id.class == String
|
201
|
+
end
|
202
|
+
|
203
|
+
def string_literal_to_boolean(value)
|
204
|
+
return value unless value.class == String
|
205
|
+
|
206
|
+
if value.casecmp('true').zero?
|
207
|
+
true
|
208
|
+
elsif value.casecmp('false').zero?
|
209
|
+
false
|
210
|
+
else return nil
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def add_customer_data(post, options)
|
215
|
+
return unless options[:email]
|
216
|
+
|
217
|
+
post[:email] = options[:email]
|
218
|
+
end
|
219
|
+
|
220
|
+
def add_address(post, creditcard, options)
|
221
|
+
return unless options[:billing_address] || options[:address]
|
222
|
+
|
223
|
+
address = options[:billing_address] || options[:address]
|
224
|
+
post[:billing_address] = {}
|
225
|
+
post[:billing_address][:name] = creditcard.name
|
226
|
+
post[:billing_address][:street_address] = address[:address1]
|
227
|
+
post[:billing_address][:city] = address[:city]
|
228
|
+
post[:billing_address][:state] = address[:state]
|
229
|
+
post[:billing_address][:zip] = address[:zip]
|
230
|
+
end
|
231
|
+
|
232
|
+
def add_amount(post, money, options)
|
233
|
+
post[:amount] = amount(money)
|
234
|
+
end
|
235
|
+
|
236
|
+
def add_payment(post, payment)
|
237
|
+
post[:credit_card] = {}
|
238
|
+
post[:credit_card][:number] = payment.number
|
239
|
+
post[:credit_card][:expiration_month] = payment.month
|
240
|
+
post[:credit_card][:expiration_year] = payment.year
|
241
|
+
end
|
242
|
+
|
243
|
+
def add_level_3_data(post, options)
|
244
|
+
post[:invoice_id] = options[:invoice_id] if options[:invoice_id]
|
245
|
+
post[:customer_reference_id] = options[:customer_reference_id] if options[:customer_reference_id]
|
246
|
+
post[:tax_amount] = options[:tax_amount].to_i if options[:tax_amount]
|
247
|
+
post[:national_tax_amount] = options[:national_tax_amount].to_i if options[:national_tax_amount]
|
248
|
+
post[:merchant_tax_id] = options[:merchant_tax_id] if options[:merchant_tax_id]
|
249
|
+
post[:customer_tax_id] = options[:customer_tax_id] if options[:customer_tax_id]
|
250
|
+
post[:commodity_code] = options[:commodity_code] if options[:commodity_code]
|
251
|
+
post[:discount_amount] = options[:discount_amount].to_i if options[:discount_amount]
|
252
|
+
post[:freight_amount] = options[:freight_amount].to_i if options[:freight_amount]
|
253
|
+
post[:duty_amount] = options[:duty_amount].to_i if options[:duty_amount]
|
254
|
+
post[:additional_tax_amount] = options[:additional_tax_amount].to_i if options[:additional_tax_amount]
|
255
|
+
post[:additional_tax_rate] = options[:additional_tax_rate].to_i if options[:additional_tax_rate]
|
256
|
+
|
257
|
+
add_source_address(post, options)
|
258
|
+
add_shipping_address(post, options)
|
259
|
+
add_line_items(post, options)
|
260
|
+
end
|
261
|
+
|
262
|
+
def add_source_address(post, options)
|
263
|
+
return unless source_address = options[:source_address] ||
|
264
|
+
options[:billing_address] ||
|
265
|
+
options[:address]
|
266
|
+
|
267
|
+
post[:source_address] = {}
|
268
|
+
post[:source_address][:zip] = source_address[:zip] if source_address[:zip]
|
269
|
+
end
|
270
|
+
|
271
|
+
def add_shipping_address(post, options)
|
272
|
+
return unless shipping_address = options[:shipping_address]
|
273
|
+
|
274
|
+
post[:shipping_address] = {}
|
275
|
+
post[:shipping_address][:name] = shipping_address[:name] if shipping_address[:name]
|
276
|
+
post[:shipping_address][:street_address] = shipping_address[:address1] if shipping_address[:address1]
|
277
|
+
post[:shipping_address][:street_address2] = shipping_address[:address2] if shipping_address[:address2]
|
278
|
+
post[:shipping_address][:city] = shipping_address[:city] if shipping_address[:city]
|
279
|
+
post[:shipping_address][:state] = shipping_address[:state] if shipping_address[:state]
|
280
|
+
post[:shipping_address][:zip] = shipping_address[:zip] if shipping_address[:zip]
|
281
|
+
post[:shipping_address][:country] = shipping_address[:country] if shipping_address[:country]
|
282
|
+
end
|
283
|
+
|
284
|
+
def add_line_items(post, options)
|
285
|
+
return unless options[:line_items]
|
286
|
+
|
287
|
+
line_items = []
|
288
|
+
options[:line_items].each do |li|
|
289
|
+
obj = {}
|
290
|
+
|
291
|
+
obj[:additional_tax_amount] = li[:additional_tax_amount].to_i if li[:additional_tax_amount]
|
292
|
+
obj[:additional_tax_included] = string_literal_to_boolean(li[:additional_tax_included]) if li[:additional_tax_included]
|
293
|
+
obj[:additional_tax_rate] = li[:additional_tax_rate].to_i if li[:additional_tax_rate]
|
294
|
+
obj[:amount] = li[:amount].to_i if li[:amount]
|
295
|
+
obj[:commodity_code] = li[:commodity_code] if li[:commodity_code]
|
296
|
+
obj[:debit_or_credit] = li[:debit_or_credit] if li[:debit_or_credit]
|
297
|
+
obj[:description] = li[:description] if li[:description]
|
298
|
+
obj[:discount_amount] = li[:discount_amount].to_i if li[:discount_amount]
|
299
|
+
obj[:discount_rate] = li[:discount_rate].to_i if li[:discount_rate]
|
300
|
+
obj[:discount_included] = string_literal_to_boolean(li[:discount_included]) if li[:discount_included]
|
301
|
+
obj[:merchant_tax_id] = li[:merchant_tax_id] if li[:merchant_tax_id]
|
302
|
+
obj[:product_id] = li[:product_id] if li[:product_id]
|
303
|
+
obj[:quantity] = li[:quantity] if li[:quantity]
|
304
|
+
obj[:transaction_id] = li[:transaction_id] if li[:transaction_id]
|
305
|
+
obj[:tax_included] = string_literal_to_boolean(li[:tax_included]) if li[:tax_included]
|
306
|
+
obj[:unit_of_measure] = li[:unit_of_measure] if li[:unit_of_measure]
|
307
|
+
obj[:unit_cost] = li[:unit_cost].to_i if li[:unit_cost]
|
308
|
+
|
309
|
+
line_items << obj
|
310
|
+
end
|
311
|
+
post[:line_items] = line_items
|
312
|
+
end
|
313
|
+
|
314
|
+
def check_token_response(response, endpoint, body = {}, options = {})
|
315
|
+
return response unless response.params['error'] == 'invalid_token'
|
316
|
+
|
317
|
+
acquire_access_token
|
318
|
+
commit(endpoint, body)
|
319
|
+
end
|
320
|
+
|
321
|
+
def parse(body)
|
322
|
+
JSON.parse(body)
|
323
|
+
end
|
324
|
+
|
325
|
+
def commit(action, parameters)
|
326
|
+
base_url = (test? ? test_url : live_url)
|
327
|
+
url = base_url + '/v1/' + action
|
328
|
+
raw_response = ssl_post(url, post_data(parameters), headers)
|
329
|
+
response = parse(raw_response)
|
330
|
+
success = success_from(response)
|
331
|
+
|
332
|
+
Response.new(
|
333
|
+
success,
|
334
|
+
message_from(success, response),
|
335
|
+
response,
|
336
|
+
authorization: authorization_from(action, response),
|
337
|
+
avs_result: AVSResult.new(code: response['avs_response']),
|
338
|
+
cvv_result: response['csc_response'],
|
339
|
+
test: test?,
|
340
|
+
error_code: success ? nil : error_code_from(response)
|
341
|
+
)
|
342
|
+
rescue JSON::ParserError
|
343
|
+
unparsable_response(raw_response)
|
344
|
+
end
|
345
|
+
|
346
|
+
def unparsable_response(raw_response)
|
347
|
+
message = 'Unparsable response received from PayTrace. Please contact PayTrace if you continue to receive this message.'
|
348
|
+
message += " (The raw response returned by the API was #{raw_response.inspect})"
|
349
|
+
return Response.new(false, message)
|
350
|
+
end
|
351
|
+
|
352
|
+
def headers
|
353
|
+
{
|
354
|
+
'Content-type' => 'application/json',
|
355
|
+
'Authorization' => 'Bearer ' + @options[:access_token]
|
356
|
+
}
|
357
|
+
end
|
358
|
+
|
359
|
+
def success_from(response)
|
360
|
+
response['success']
|
361
|
+
end
|
362
|
+
|
363
|
+
def message_from(success, response)
|
364
|
+
return response['status_message'] if success
|
365
|
+
|
366
|
+
if error = response['errors']
|
367
|
+
message = 'Errors-'
|
368
|
+
error.each do |k, v|
|
369
|
+
message.concat(" code:#{k}, message:#{v}")
|
370
|
+
end
|
371
|
+
else
|
372
|
+
message = response['status_message'].to_s + " #{response['approval_message']}"
|
373
|
+
end
|
374
|
+
|
375
|
+
message
|
376
|
+
end
|
377
|
+
|
378
|
+
# store transactions do not return a transaction_id, but they return a customer_id that will then be used as the third_party_token for the stored payment method
|
379
|
+
def authorization_from(action, response)
|
380
|
+
if action == ENDPOINTS[:store]
|
381
|
+
response['customer_id']
|
382
|
+
else
|
383
|
+
response['transaction_id']
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
def post_data(parameters = {})
|
388
|
+
parameters[:password] = @options[:password]
|
389
|
+
parameters[:username] = @options[:username]
|
390
|
+
parameters[:integrator_id] = @options[:integrator_id]
|
391
|
+
|
392
|
+
parameters.to_json
|
393
|
+
end
|
394
|
+
|
395
|
+
def error_code_from(response)
|
396
|
+
STANDARD_ERROR_CODE_MAPPING[response['response_code']]
|
397
|
+
end
|
398
|
+
|
399
|
+
def handle_response(response)
|
400
|
+
response.body
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
@@ -39,6 +39,7 @@ module ActiveMerchant
|
|
39
39
|
add_address(params, options)
|
40
40
|
add_amount(params, amount, options)
|
41
41
|
add_soft_descriptors(params, options)
|
42
|
+
add_level2_data(params, options)
|
42
43
|
add_stored_credentials(params, options)
|
43
44
|
|
44
45
|
commit(params, options)
|
@@ -53,6 +54,7 @@ module ActiveMerchant
|
|
53
54
|
add_address(params, options)
|
54
55
|
add_amount(params, amount, options)
|
55
56
|
add_soft_descriptors(params, options)
|
57
|
+
add_level2_data(params, options)
|
56
58
|
add_stored_credentials(params, options)
|
57
59
|
|
58
60
|
commit(params, options)
|
@@ -73,7 +75,19 @@ module ActiveMerchant
|
|
73
75
|
|
74
76
|
add_authorization_info(params, authorization)
|
75
77
|
add_amount(params, (amount || amount_from_authorization(authorization)), options)
|
78
|
+
add_soft_descriptors(params, options)
|
79
|
+
add_invoice(params, options)
|
80
|
+
|
81
|
+
commit(params, options)
|
82
|
+
end
|
83
|
+
|
84
|
+
def credit(amount, payment_method, options = {})
|
85
|
+
params = { transaction_type: 'refund' }
|
76
86
|
|
87
|
+
add_amount(params, amount, options)
|
88
|
+
add_payment_method(params, payment_method, options)
|
89
|
+
add_soft_descriptors(params, options)
|
90
|
+
add_invoice(params, options)
|
77
91
|
commit(params, options)
|
78
92
|
end
|
79
93
|
|
@@ -246,17 +260,42 @@ module ActiveMerchant
|
|
246
260
|
params[:soft_descriptors] = options[:soft_descriptors] if options[:soft_descriptors]
|
247
261
|
end
|
248
262
|
|
263
|
+
def add_level2_data(params, options)
|
264
|
+
return unless level2_data = options[:level2]
|
265
|
+
|
266
|
+
params[:level2] = {}
|
267
|
+
params[:level2][:customer_ref] = level2_data[:customer_ref]
|
268
|
+
end
|
269
|
+
|
249
270
|
def add_stored_credentials(params, options)
|
250
|
-
if options[:sequence]
|
271
|
+
if options[:sequence] || options[:stored_credential]
|
251
272
|
params[:stored_credentials] = {}
|
252
|
-
params[:stored_credentials][:cardbrand_original_transaction_id] = options
|
253
|
-
params[:stored_credentials][:
|
254
|
-
params[:stored_credentials][:
|
255
|
-
params[:stored_credentials][:is_scheduled] = options[:is_scheduled]
|
273
|
+
params[:stored_credentials][:cardbrand_original_transaction_id] = original_transaction_id(options) if original_transaction_id(options)
|
274
|
+
params[:stored_credentials][:initiator] = initiator(options) if initiator(options)
|
275
|
+
params[:stored_credentials][:sequence] = options[:sequence] || sequence(options[:stored_credential][:initial_transaction])
|
276
|
+
params[:stored_credentials][:is_scheduled] = options[:is_scheduled] || is_scheduled(options[:stored_credential][:reason_type])
|
256
277
|
params[:stored_credentials][:auth_type_override] = options[:auth_type_override] if options[:auth_type_override]
|
257
278
|
end
|
258
279
|
end
|
259
280
|
|
281
|
+
def original_transaction_id(options)
|
282
|
+
return options[:cardbrand_original_transaction_id] if options[:cardbrand_original_transaction_id]
|
283
|
+
return options[:stored_credential][:network_transaction_id] if options.dig(:stored_credential, :network_transaction_id)
|
284
|
+
end
|
285
|
+
|
286
|
+
def initiator(options)
|
287
|
+
return options[:initiator] if options[:initiator]
|
288
|
+
return options[:stored_credential][:initiator].upcase if options.dig(:stored_credential, :initiator)
|
289
|
+
end
|
290
|
+
|
291
|
+
def sequence(initial_transaction)
|
292
|
+
initial_transaction ? 'FIRST' : 'SUBSEQUENT'
|
293
|
+
end
|
294
|
+
|
295
|
+
def is_scheduled(reason_type)
|
296
|
+
reason_type == 'recurring' ? 'true' : 'false'
|
297
|
+
end
|
298
|
+
|
260
299
|
def commit(params, options)
|
261
300
|
url = base_url(options) + endpoint(params)
|
262
301
|
|
@@ -272,15 +311,16 @@ module ActiveMerchant
|
|
272
311
|
response = json_error(e.response.body)
|
273
312
|
end
|
274
313
|
|
314
|
+
success = success_from(response)
|
275
315
|
Response.new(
|
276
|
-
|
277
|
-
handle_message(response,
|
316
|
+
success,
|
317
|
+
handle_message(response, success),
|
278
318
|
response,
|
279
319
|
test: test?,
|
280
320
|
authorization: authorization_from(params, response),
|
281
321
|
avs_result: { code: response['avs'] },
|
282
322
|
cvv_result: response['cvv2'],
|
283
|
-
error_code:
|
323
|
+
error_code: success ? nil : error_code_from(response)
|
284
324
|
)
|
285
325
|
end
|
286
326
|
|
@@ -334,10 +374,16 @@ module ActiveMerchant
|
|
334
374
|
}
|
335
375
|
end
|
336
376
|
|
337
|
-
def
|
338
|
-
|
377
|
+
def error_code_from(response)
|
378
|
+
error_code = nil
|
379
|
+
if response['bank_resp_code'] == '100'
|
380
|
+
return
|
381
|
+
elsif response['bank_resp_code']
|
382
|
+
error_code = response['bank_resp_code']
|
383
|
+
elsif error_code = response['Error'].to_h['messages'].to_a.map { |e| e['code'] }.join(', ')
|
384
|
+
end
|
339
385
|
|
340
|
-
|
386
|
+
error_code
|
341
387
|
end
|
342
388
|
|
343
389
|
def success_from(response)
|
@@ -118,6 +118,7 @@ module ActiveMerchant #:nodoc:
|
|
118
118
|
xml.tag!('Description', options[:description]) unless options[:description].blank?
|
119
119
|
xml.tag!('Comment', options[:comment]) unless options[:comment].blank?
|
120
120
|
xml.tag!('ExtData', 'Name' => 'COMMENT2', 'Value' => options[:comment2]) unless options[:comment2].blank?
|
121
|
+
xml.tag!('MerchDescr', options[:merch_descr]) unless options[:merch_descr].blank?
|
121
122
|
xml.tag!(
|
122
123
|
'ExtData',
|
123
124
|
'Name' => 'CAPTURECOMPLETE',
|
@@ -56,6 +56,10 @@ module ActiveMerchant #:nodoc:
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
def store(payment, options = {})
|
60
|
+
raise ArgumentError, 'Store is not supported on Payflow gateways'
|
61
|
+
end
|
62
|
+
|
59
63
|
def verify_credentials
|
60
64
|
response = void('0')
|
61
65
|
response.params['result'] != '26'
|
@@ -141,6 +145,7 @@ module ActiveMerchant #:nodoc:
|
|
141
145
|
xml.tag! 'FreightAmt', options[:freightamt] unless options[:freightamt].blank?
|
142
146
|
xml.tag! 'DutyAmt', options[:dutyamt] unless options[:dutyamt].blank?
|
143
147
|
xml.tag! 'DiscountAmt', options[:discountamt] unless options[:discountamt].blank?
|
148
|
+
xml.tag! 'MerchDescr', options[:merch_descr] unless options[:merch_descr].blank?
|
144
149
|
|
145
150
|
billing_address = options[:billing_address] || options[:address]
|
146
151
|
add_address(xml, 'BillTo', billing_address, options) if billing_address
|
@@ -176,6 +181,7 @@ module ActiveMerchant #:nodoc:
|
|
176
181
|
xml.tag! 'DutyAmt', options[:dutyamt] unless options[:dutyamt].blank?
|
177
182
|
xml.tag! 'DiscountAmt', options[:discountamt] unless options[:discountamt].blank?
|
178
183
|
xml.tag! 'EMail', options[:email] unless options[:email].nil?
|
184
|
+
xml.tag! 'MerchDescr', options[:merch_descr] unless options[:merch_descr].blank?
|
179
185
|
|
180
186
|
billing_address = options[:billing_address] || options[:address]
|
181
187
|
add_address(xml, 'BillTo', billing_address, options) if billing_address
|
@@ -239,6 +245,7 @@ module ActiveMerchant #:nodoc:
|
|
239
245
|
xml.tag! 'InvNum', options[:order_id].to_s.gsub(/[^\w.]/, '') unless options[:order_id].blank?
|
240
246
|
xml.tag! 'Description', options[:description] unless options[:description].blank?
|
241
247
|
xml.tag! 'OrderDesc', options[:order_desc] unless options[:order_desc].blank?
|
248
|
+
xml.tag! 'MerchDescr', options[:merch_descr] unless options[:merch_descr].blank?
|
242
249
|
xml.tag! 'BillTo' do
|
243
250
|
xml.tag! 'Name', check.name
|
244
251
|
end
|
@@ -282,6 +289,8 @@ module ActiveMerchant #:nodoc:
|
|
282
289
|
xml.tag! 'ECI', three_d_secure[:eci] unless three_d_secure[:eci].blank?
|
283
290
|
xml.tag! 'CAVV', three_d_secure[:cavv] unless three_d_secure[:cavv].blank?
|
284
291
|
xml.tag! 'XID', three_d_secure[:xid] unless three_d_secure[:xid].blank?
|
292
|
+
xml.tag! 'THREEDSVERSION', three_d_secure[:version] unless three_d_secure[:version].blank?
|
293
|
+
xml.tag! 'DSTRANSACTIONID', three_d_secure[:ds_transaction_id] unless three_d_secure[:ds_transaction_id].blank?
|
285
294
|
end
|
286
295
|
end
|
287
296
|
end
|
@@ -86,6 +86,11 @@ module ActiveMerchant #:nodoc:
|
|
86
86
|
refund(money, identification, options)
|
87
87
|
end
|
88
88
|
|
89
|
+
def verify(payment_source, options = {})
|
90
|
+
request = build_purchase_or_authorization_request(100, payment_source, options)
|
91
|
+
commit(:validate, request)
|
92
|
+
end
|
93
|
+
|
89
94
|
# Token Based Billing
|
90
95
|
#
|
91
96
|
# Instead of storing the credit card details locally, you can store them inside the
|
@@ -149,7 +154,7 @@ module ActiveMerchant #:nodoc:
|
|
149
154
|
add_credit_card(result, payment_source)
|
150
155
|
end
|
151
156
|
|
152
|
-
add_amount(result, money, options)
|
157
|
+
add_amount(result, money, options) if money
|
153
158
|
add_invoice(result, options)
|
154
159
|
add_address_verification_data(result, options)
|
155
160
|
add_optional_elements(result, options)
|
@@ -229,8 +234,8 @@ module ActiveMerchant #:nodoc:
|
|
229
234
|
address = options[:billing_address] || options[:address]
|
230
235
|
return if address.nil?
|
231
236
|
|
232
|
-
xml.add_element('EnableAvsData').text = 1
|
233
|
-
xml.add_element('AvsAction').text = 1
|
237
|
+
xml.add_element('EnableAvsData').text = options[:enable_avs_data] || 1
|
238
|
+
xml.add_element('AvsAction').text = options[:avs_action] || 1
|
234
239
|
|
235
240
|
xml.add_element('AvsStreetAddress').text = address[:address1]
|
236
241
|
xml.add_element('AvsPostCode').text = address[:zip]
|
@@ -334,7 +339,7 @@ module ActiveMerchant #:nodoc:
|
|
334
339
|
def authorization_from(action, response)
|
335
340
|
case action
|
336
341
|
when :validate
|
337
|
-
(response[:billing_id] || response[:dps_billing_id])
|
342
|
+
(response[:billing_id] || response[:dps_billing_id] || response[:dps_txn_ref])
|
338
343
|
else
|
339
344
|
response[:dps_txn_ref]
|
340
345
|
end
|
@@ -361,7 +366,7 @@ module ActiveMerchant #:nodoc:
|
|
361
366
|
# add a method to response so we can easily get the token
|
362
367
|
# for Validate transactions
|
363
368
|
def token
|
364
|
-
@params['billing_id'] || @params['dps_billing_id']
|
369
|
+
@params['billing_id'] || @params['dps_billing_id'] || @params['dps_txn_ref']
|
365
370
|
end
|
366
371
|
end
|
367
372
|
end
|
@@ -9,7 +9,7 @@ module ActiveMerchant #:nodoc:
|
|
9
9
|
|
10
10
|
self.supported_countries = %w[MX EC CO BR CL PE]
|
11
11
|
self.default_currency = 'USD'
|
12
|
-
self.supported_cardtypes = %i[visa master american_express diners_club elo alia]
|
12
|
+
self.supported_cardtypes = %i[visa master american_express diners_club elo alia olimpica]
|
13
13
|
|
14
14
|
self.homepage_url = 'https://secure.paymentez.com/'
|
15
15
|
self.display_name = 'Paymentez'
|
@@ -82,6 +82,7 @@ module ActiveMerchant #:nodoc:
|
|
82
82
|
def refund(money, authorization, options = {})
|
83
83
|
post = { transaction: { id: authorization } }
|
84
84
|
post[:order] = { amount: amount(money).to_f } if money
|
85
|
+
add_more_info(post, options)
|
85
86
|
|
86
87
|
commit_transaction('refund', post)
|
87
88
|
end
|
@@ -175,9 +176,33 @@ module ActiveMerchant #:nodoc:
|
|
175
176
|
extra_params = {}
|
176
177
|
extra_params.merge!(options[:extra_params]) if options[:extra_params]
|
177
178
|
|
179
|
+
add_external_mpi_fields(extra_params, options)
|
180
|
+
|
178
181
|
post['extra_params'] = extra_params unless extra_params.empty?
|
179
182
|
end
|
180
183
|
|
184
|
+
def add_external_mpi_fields(extra_params, options)
|
185
|
+
three_d_secure_options = options[:three_d_secure]
|
186
|
+
return unless three_d_secure_options
|
187
|
+
|
188
|
+
auth_data = {
|
189
|
+
cavv: three_d_secure_options[:cavv],
|
190
|
+
xid: three_d_secure_options[:xid],
|
191
|
+
eci: three_d_secure_options[:eci],
|
192
|
+
version: three_d_secure_options[:version],
|
193
|
+
reference_id: three_d_secure_options[:three_ds_server_trans_id],
|
194
|
+
status: three_d_secure_options[:authentication_response_status] || three_d_secure_options[:directory_response_status]
|
195
|
+
}.compact
|
196
|
+
|
197
|
+
return if auth_data.empty?
|
198
|
+
|
199
|
+
extra_params[:auth_data] = auth_data
|
200
|
+
end
|
201
|
+
|
202
|
+
def add_more_info(post, options)
|
203
|
+
post[:more_info] = options[:more_info] if options[:more_info]
|
204
|
+
end
|
205
|
+
|
181
206
|
def parse(body)
|
182
207
|
JSON.parse(body)
|
183
208
|
end
|