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
@@ -58,7 +58,7 @@ module ActiveMerchant #:nodoc:
|
|
58
58
|
xml = Builder::XmlMarkup.new indent: 2
|
59
59
|
xml.tag! transaction_type + 'Req', 'xmlns' => PAYPAL_NAMESPACE do
|
60
60
|
xml.tag! transaction_type + 'Request', 'xmlns:n2' => EBAY_NAMESPACE do
|
61
|
-
xml.tag! 'n2:Version',
|
61
|
+
xml.tag! 'n2:Version', api_version(options)
|
62
62
|
xml.tag! 'n2:' + transaction_type + 'RequestDetails' do
|
63
63
|
xml.tag! 'n2:ReferenceID', reference_id if transaction_type == 'DoReferenceTransaction'
|
64
64
|
xml.tag! 'n2:PaymentAction', action
|
@@ -73,6 +73,12 @@ module ActiveMerchant #:nodoc:
|
|
73
73
|
xml.target!
|
74
74
|
end
|
75
75
|
|
76
|
+
def api_version(options)
|
77
|
+
return API_VERSION_3DS2 if options.dig(:three_d_secure, :version) =~ /^2/
|
78
|
+
|
79
|
+
API_VERSION
|
80
|
+
end
|
81
|
+
|
76
82
|
def add_credit_card(xml, credit_card, address, options)
|
77
83
|
xml.tag! 'n2:CreditCard' do
|
78
84
|
xml.tag! 'n2:CreditCardType', credit_card_type(card_brand(credit_card))
|
@@ -104,10 +110,12 @@ module ActiveMerchant #:nodoc:
|
|
104
110
|
three_d_secure = options[:three_d_secure]
|
105
111
|
xml.tag! 'ThreeDSecureRequest' do
|
106
112
|
xml.tag! 'MpiVendor3ds', 'Y'
|
107
|
-
xml.tag! 'AuthStatus3ds', three_d_secure[:trans_status]
|
113
|
+
xml.tag! 'AuthStatus3ds', three_d_secure[:authentication_response_status] || three_d_secure[:trans_status] if three_d_secure[:authentication_response_status] || three_d_secure[:trans_status]
|
108
114
|
xml.tag! 'Cavv', three_d_secure[:cavv] unless three_d_secure[:cavv].blank?
|
109
115
|
xml.tag! 'Eci3ds', three_d_secure[:eci] unless three_d_secure[:eci].blank?
|
110
116
|
xml.tag! 'Xid', three_d_secure[:xid] unless three_d_secure[:xid].blank?
|
117
|
+
xml.tag! 'ThreeDSVersion', three_d_secure[:version] unless three_d_secure[:version].blank?
|
118
|
+
xml.tag! 'DSTransactionId', three_d_secure[:ds_transaction_id] unless three_d_secure[:ds_transaction_id].blank?
|
111
119
|
end
|
112
120
|
end
|
113
121
|
|
@@ -250,6 +250,7 @@ module ActiveMerchant #:nodoc:
|
|
250
250
|
xml.tag! 'n2:PaymentType', options[:payment_type] || 'Any'
|
251
251
|
add_payment_details(xml, money, currency_code, options)
|
252
252
|
xml.tag! 'n2:IPAddress', options[:ip]
|
253
|
+
xml.tag! 'n2:MerchantSessionId', options[:merchant_session_id] if options[:merchant_session_id].present?
|
253
254
|
end
|
254
255
|
end
|
255
256
|
end
|
@@ -0,0 +1,291 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
class PaysafeGateway < Gateway
|
4
|
+
self.test_url = 'https://api.test.paysafe.com'
|
5
|
+
self.live_url = 'https://api.paysafe.com'
|
6
|
+
|
7
|
+
self.supported_countries = %w(FR)
|
8
|
+
self.default_currency = 'EUR'
|
9
|
+
self.supported_cardtypes = %i[visa master american_express discover]
|
10
|
+
|
11
|
+
self.homepage_url = 'https://www.paysafe.com/'
|
12
|
+
self.display_name = 'Paysafe'
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
requires!(options, :username, :password, :account_id)
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
def purchase(money, payment, options = {})
|
20
|
+
post = {}
|
21
|
+
add_invoice(post, money, options)
|
22
|
+
add_payment(post, payment)
|
23
|
+
add_billing_address(post, options)
|
24
|
+
add_merchant_details(post, options)
|
25
|
+
add_customer_data(post, payment, options) unless payment.is_a?(String)
|
26
|
+
add_three_d_secure(post, payment, options) if options[:three_d_secure]
|
27
|
+
post[:settleWithAuth] = true
|
28
|
+
|
29
|
+
commit(:post, 'auths', post, options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def authorize(money, payment, options = {})
|
33
|
+
post = {}
|
34
|
+
add_invoice(post, money, options)
|
35
|
+
add_payment(post, payment)
|
36
|
+
add_billing_address(post, options)
|
37
|
+
add_merchant_details(post, options)
|
38
|
+
add_customer_data(post, payment, options) unless payment.is_a?(String)
|
39
|
+
add_three_d_secure(post, payment, options) if options[:three_d_secure]
|
40
|
+
|
41
|
+
commit(:post, 'auths', post, options)
|
42
|
+
end
|
43
|
+
|
44
|
+
def capture(money, authorization, options = {})
|
45
|
+
post = {}
|
46
|
+
add_invoice(post, money, options)
|
47
|
+
|
48
|
+
commit(:post, "auths/#{authorization}/settlements", post, options)
|
49
|
+
end
|
50
|
+
|
51
|
+
def refund(money, authorization, options = {})
|
52
|
+
post = {}
|
53
|
+
add_invoice(post, money, options)
|
54
|
+
|
55
|
+
commit(:post, "settlements/#{authorization}/refunds", post, options)
|
56
|
+
end
|
57
|
+
|
58
|
+
def void(authorization, options = {})
|
59
|
+
post = {}
|
60
|
+
money = options[:amount]
|
61
|
+
add_invoice(post, money, options)
|
62
|
+
|
63
|
+
commit(:post, "auths/#{authorization}/voidauths", post, options)
|
64
|
+
end
|
65
|
+
|
66
|
+
def credit(money, payment, options = {})
|
67
|
+
post = {}
|
68
|
+
add_invoice(post, money, options)
|
69
|
+
add_payment(post, payment)
|
70
|
+
|
71
|
+
commit(:post, 'standalonecredits', post, options)
|
72
|
+
end
|
73
|
+
|
74
|
+
# This is a '$0 auth' done at a specific verification endpoint at the gateway
|
75
|
+
def verify(payment, options = {})
|
76
|
+
post = {}
|
77
|
+
add_payment(post, payment)
|
78
|
+
add_billing_address(post, options)
|
79
|
+
add_customer_data(post, payment, options) unless payment.is_a?(String)
|
80
|
+
|
81
|
+
commit(:post, 'verifications', post, options)
|
82
|
+
end
|
83
|
+
|
84
|
+
def store(payment, options = {})
|
85
|
+
post = {}
|
86
|
+
add_payment(post, payment)
|
87
|
+
add_address_for_vaulting(post, options)
|
88
|
+
add_profile_data(post, payment, options)
|
89
|
+
add_store_data(post, payment, options)
|
90
|
+
|
91
|
+
commit(:post, 'profiles', post, options)
|
92
|
+
end
|
93
|
+
|
94
|
+
def redact(pm_profile_id)
|
95
|
+
commit_for_redact(:delete, "profiles/#{pm_profile_id}", nil, nil)
|
96
|
+
end
|
97
|
+
|
98
|
+
def supports_scrubbing?
|
99
|
+
true
|
100
|
+
end
|
101
|
+
|
102
|
+
def scrub(transcript)
|
103
|
+
transcript.
|
104
|
+
gsub(%r((Authorization: Basic )[a-zA-Z0-9:_]+), '\1[FILTERED]').
|
105
|
+
gsub(%r(("cardNum\\?":\\?")\d+), '\1[FILTERED]').
|
106
|
+
gsub(%r(("cvv\\?":\\?")\d+), '\1[FILTERED]')
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
# Customer data can be included in transactions where the payment method is a credit card
|
112
|
+
# but should not be sent when the payment method is a token
|
113
|
+
def add_customer_data(post, creditcard, options)
|
114
|
+
post[:profile] = {}
|
115
|
+
post[:profile][:firstName] = creditcard.first_name
|
116
|
+
post[:profile][:lastName] = creditcard.last_name
|
117
|
+
post[:profile][:email] = options[:email] if options[:email]
|
118
|
+
post[:customerIp] = options[:ip] if options[:ip]
|
119
|
+
end
|
120
|
+
|
121
|
+
def add_billing_address(post, options)
|
122
|
+
return unless options[:billing_address] || options[:address]
|
123
|
+
|
124
|
+
address = options[:billing_address] || options[:address]
|
125
|
+
post[:billingDetails] = {}
|
126
|
+
post[:billingDetails][:street] = address[:address1]
|
127
|
+
post[:billingDetails][:city] = address[:city]
|
128
|
+
post[:billingDetails][:state] = address[:state]
|
129
|
+
post[:billingDetails][:country] = address[:country]
|
130
|
+
post[:billingDetails][:zip] = address[:zip]
|
131
|
+
post[:billingDetails][:phone] = address[:phone]
|
132
|
+
end
|
133
|
+
|
134
|
+
# The add_address_for_vaulting method is applicable to the store method, as the APIs address
|
135
|
+
# object is formatted differently from the standard transaction billing address
|
136
|
+
def add_address_for_vaulting(post, options)
|
137
|
+
return unless options[:billing_address || options[:address]]
|
138
|
+
|
139
|
+
address = options[:billing_address] || options[:address]
|
140
|
+
post[:billingAddress] = {}
|
141
|
+
post[:billingAddress][:street] = address[:address1]
|
142
|
+
post[:billingAddress][:city] = address[:city]
|
143
|
+
post[:billingAddress][:zip] = address[:zip]
|
144
|
+
post[:billingAddress][:country] = address[:country]
|
145
|
+
post[:billingAddress][:state] = address[:state] if address[:state]
|
146
|
+
end
|
147
|
+
|
148
|
+
# This data is specific to creating a profile at the gateway's vault level
|
149
|
+
def add_profile_data(post, payment, options)
|
150
|
+
address = options[:billing_address] || options[:address]
|
151
|
+
|
152
|
+
post[:firstName] = payment.first_name
|
153
|
+
post[:lastName] = payment.last_name
|
154
|
+
post[:dateOfBirth] = {}
|
155
|
+
post[:dateOfBirth][:year] = options[:date_of_birth][:year]
|
156
|
+
post[:dateOfBirth][:month] = options[:date_of_birth][:month]
|
157
|
+
post[:dateOfBirth][:day] = options[:date_of_birth][:day]
|
158
|
+
post[:email] = options[:email] if options[:email]
|
159
|
+
post[:phone] = (address[:phone] || options[:phone]) if address[:phone] || options[:phone]
|
160
|
+
post[:ip] = options[:ip] if options[:ip]
|
161
|
+
end
|
162
|
+
|
163
|
+
def add_store_data(post, payment, options)
|
164
|
+
post[:merchantCustomerId] = options[:customer_id] || SecureRandom.hex(12)
|
165
|
+
post[:locale] = options[:locale] || 'en_US'
|
166
|
+
post[:card][:holderName] = payment.name
|
167
|
+
end
|
168
|
+
|
169
|
+
# Paysafe expects minor units so we are not calling amount method on money parameter
|
170
|
+
def add_invoice(post, money, options)
|
171
|
+
post[:amount] = money
|
172
|
+
end
|
173
|
+
|
174
|
+
def add_payment(post, payment)
|
175
|
+
if payment.is_a?(String)
|
176
|
+
post[:card] = {}
|
177
|
+
post[:card][:paymentToken] = payment
|
178
|
+
else
|
179
|
+
post[:card] = { cardExpiry: {} }
|
180
|
+
post[:card][:cardNum] = payment.number
|
181
|
+
post[:card][:cardExpiry][:month] = payment.month
|
182
|
+
post[:card][:cardExpiry][:year] = payment.year
|
183
|
+
post[:card][:cvv] = payment.verification_value
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def add_merchant_details(post, options)
|
188
|
+
return unless options[:merchant_descriptor]
|
189
|
+
|
190
|
+
post[:merchantDescriptor] = {}
|
191
|
+
post[:merchantDescriptor][:dynamicDescriptor] = options[:merchant_descriptor][:dynamic_descriptor] if options[:merchant_descriptor][:dynamic_descriptor]
|
192
|
+
post[:merchantDescriptor][:phone] = options[:merchant_descriptor][:phone] if options[:merchant_descriptor][:phone]
|
193
|
+
end
|
194
|
+
|
195
|
+
def add_three_d_secure(post, payment, options)
|
196
|
+
three_d_secure = options[:three_d_secure]
|
197
|
+
|
198
|
+
post[:authentication] = {}
|
199
|
+
post[:authentication][:eci] = three_d_secure[:eci]
|
200
|
+
post[:authentication][:cavv] = three_d_secure[:cavv]
|
201
|
+
post[:authentication][:xid] = three_d_secure[:xid] if three_d_secure[:xid]
|
202
|
+
post[:authentication][:threeDSecureVersion] = three_d_secure[:version]
|
203
|
+
post[:authentication][:directoryServerTransactionId] = three_d_secure[:ds_transaction_id] unless payment.is_a?(String) || payment.brand != 'mastercard'
|
204
|
+
end
|
205
|
+
|
206
|
+
def parse(body)
|
207
|
+
JSON.parse(body)
|
208
|
+
end
|
209
|
+
|
210
|
+
def commit(method, action, parameters, options)
|
211
|
+
url = url(action)
|
212
|
+
raw_response = ssl_request(method, url, post_data(parameters, options), headers)
|
213
|
+
response = parse(raw_response)
|
214
|
+
success = success_from(response)
|
215
|
+
|
216
|
+
Response.new(
|
217
|
+
success,
|
218
|
+
message_from(success, response),
|
219
|
+
response,
|
220
|
+
authorization: authorization_from(response),
|
221
|
+
avs_result: AVSResult.new(code: response['avsResponse']),
|
222
|
+
cvv_result: CVVResult.new(response['cvvVerification']),
|
223
|
+
test: test?,
|
224
|
+
error_code: success ? nil : error_code_from(response)
|
225
|
+
)
|
226
|
+
end
|
227
|
+
|
228
|
+
def commit_for_redact(method, action, parameters, options)
|
229
|
+
url = url(action)
|
230
|
+
response = raw_ssl_request(method, url, post_data(parameters, options), headers)
|
231
|
+
success = true if response.code == '200'
|
232
|
+
|
233
|
+
Response.new(
|
234
|
+
success,
|
235
|
+
message: response.message
|
236
|
+
)
|
237
|
+
end
|
238
|
+
|
239
|
+
def headers
|
240
|
+
{
|
241
|
+
'Content-Type' => 'application/json',
|
242
|
+
'Authorization' => "Basic #{Base64.strict_encode64(@options[:api_key].to_s)}"
|
243
|
+
}
|
244
|
+
end
|
245
|
+
|
246
|
+
def url(action, options = {})
|
247
|
+
base_url = (test? ? test_url : live_url)
|
248
|
+
|
249
|
+
if action.include? 'profiles'
|
250
|
+
"#{base_url}/customervault/v1/#{action}"
|
251
|
+
else
|
252
|
+
"#{base_url}/cardpayments/v1/accounts/#{@options[:account_id]}/#{action}"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def success_from(response)
|
257
|
+
return false if response['status'] == 'FAILED' || response['error']
|
258
|
+
|
259
|
+
true
|
260
|
+
end
|
261
|
+
|
262
|
+
def message_from(success, response)
|
263
|
+
return response['status'] unless response['error']
|
264
|
+
|
265
|
+
"Error(s)- code:#{response['error']['code']}, message:#{response['error']['message']}"
|
266
|
+
end
|
267
|
+
|
268
|
+
def authorization_from(response)
|
269
|
+
response['id']
|
270
|
+
end
|
271
|
+
|
272
|
+
def post_data(parameters = {}, options = {})
|
273
|
+
return unless parameters.present?
|
274
|
+
|
275
|
+
parameters[:merchantRefNum] = options[:merchant_ref_num] || SecureRandom.hex(16).to_s
|
276
|
+
|
277
|
+
parameters.to_json
|
278
|
+
end
|
279
|
+
|
280
|
+
def error_code_from(response)
|
281
|
+
return unless response['error']
|
282
|
+
|
283
|
+
response['error']['code']
|
284
|
+
end
|
285
|
+
|
286
|
+
def handle_response(response)
|
287
|
+
response.body
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
@@ -208,7 +208,7 @@ module ActiveMerchant #:nodoc:
|
|
208
208
|
buyer[:merchantBuyerId] = buyer_hash[:merchant_buyer_id]
|
209
209
|
buyer[:cnpj] = buyer_hash[:cnpj] if @options[:payment_country] == 'BR'
|
210
210
|
buyer[:emailAddress] = buyer_hash[:email]
|
211
|
-
buyer[:contactPhone] = (options[:billing_address][:phone] if options[:billing_address]) || (options[:shipping_address][:
|
211
|
+
buyer[:contactPhone] = (options[:billing_address][:phone] if options[:billing_address]) || (options[:shipping_address][:phone_number] if options[:shipping_address]) || ''
|
212
212
|
buyer[:shippingAddress] = shipping_address_fields(options) if options[:shipping_address]
|
213
213
|
else
|
214
214
|
buyer[:fullName] = payment_method.name.strip
|
@@ -217,7 +217,7 @@ module ActiveMerchant #:nodoc:
|
|
217
217
|
buyer[:merchantBuyerId] = options[:merchant_buyer_id]
|
218
218
|
buyer[:cnpj] = options[:cnpj] if @options[:payment_country] == 'BR'
|
219
219
|
buyer[:emailAddress] = options[:email]
|
220
|
-
buyer[:contactPhone] = (options[:billing_address][:phone] if options[:billing_address]) || (options[:shipping_address][:
|
220
|
+
buyer[:contactPhone] = (options[:billing_address][:phone] if options[:billing_address]) || (options[:shipping_address][:phone_number] if options[:shipping_address]) || ''
|
221
221
|
buyer[:shippingAddress] = shipping_address_fields(options) if options[:shipping_address]
|
222
222
|
end
|
223
223
|
post[:transaction][:order][:buyer] = buyer
|
@@ -233,7 +233,7 @@ module ActiveMerchant #:nodoc:
|
|
233
233
|
shipping_address[:state] = address[:state]
|
234
234
|
shipping_address[:country] = address[:country]
|
235
235
|
shipping_address[:postalCode] = address[:zip]
|
236
|
-
shipping_address[:phone] = address[:
|
236
|
+
shipping_address[:phone] = address[:phone_number]
|
237
237
|
shipping_address
|
238
238
|
end
|
239
239
|
|
@@ -0,0 +1,253 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
class PaywayDotComGateway < Gateway
|
4
|
+
self.test_url = 'https://devedgilpayway.net/PaywayWS/Payment/CreditCard'
|
5
|
+
self.live_url = 'https://edgilpayway.com/PaywayWS/Payment/CreditCard'
|
6
|
+
|
7
|
+
self.supported_countries = %w[US CA]
|
8
|
+
self.default_currency = 'USD'
|
9
|
+
self.supported_cardtypes = %i[visa master american_express discover]
|
10
|
+
|
11
|
+
self.money_format = :cents
|
12
|
+
|
13
|
+
self.homepage_url = 'http://www.payway.com'
|
14
|
+
self.display_name = 'Payway Gateway'
|
15
|
+
|
16
|
+
STANDARD_ERROR_CODE_MAPPING = {
|
17
|
+
'5012' => STANDARD_ERROR_CODE[:card_declined],
|
18
|
+
'5035' => STANDARD_ERROR_CODE[:invalid_number],
|
19
|
+
'5037' => STANDARD_ERROR_CODE[:invalid_expiry_date],
|
20
|
+
'5045' => STANDARD_ERROR_CODE[:incorrect_zip]
|
21
|
+
}
|
22
|
+
|
23
|
+
# Payway to standard AVSResult codes.
|
24
|
+
AVS_MAPPING = {
|
25
|
+
'N1' => 'I', # No address given with order
|
26
|
+
'N2' => 'I', # Bill-to address did not pass
|
27
|
+
'““' => 'R', # AVS not performed (Blanks returned)
|
28
|
+
'IU' => 'G', # AVS not performed by Issuer
|
29
|
+
'ID' => 'S', # Issuer does not participate in AVS
|
30
|
+
'IE' => 'E', # Edit Error - AVS data is invalid
|
31
|
+
'IS' => 'R', # System unavailable or time-out
|
32
|
+
'IB' => 'B', # Street address match. Postal code not verified due to incompatible formats (both were sent).
|
33
|
+
'IC' => 'C', # Street address and postal code not verified due to incompatible format (both were sent).
|
34
|
+
'IP' => 'P', # Postal code match. Street address not verified due to incompatible formats (both were sent).
|
35
|
+
'A1' => 'K', # Accountholder name matches
|
36
|
+
'A3' => 'V', # Accountholder name, billing address and postal code.
|
37
|
+
'A4' => 'L', # Accountholder name and billing postal code match
|
38
|
+
'A7' => 'O', # Accountholder name and billing address match
|
39
|
+
'B3' => 'H', # Accountholder name incorrect, billing address and postal code match
|
40
|
+
'B4' => 'F', # Accountholder name incorrect, billing postal code matches
|
41
|
+
'B7' => 'T', # Accountholder name incorrect, billing address matches
|
42
|
+
'B8' => 'N', # Accountholder name, billing address and postal code are all incorrect
|
43
|
+
'??' => 'R', # A double question mark symbol indicates an unrecognized response from association
|
44
|
+
'I1' => 'X', # Zip code +4 and Address Match
|
45
|
+
'I2' => 'W', # Zip code +4 Match
|
46
|
+
'I3' => 'Y', # Zip code and Address Match
|
47
|
+
'I4' => 'Z', # Zip code Match
|
48
|
+
'I5' => 'M', # +4 and Address Match
|
49
|
+
'I6' => 'W', # +4 Match
|
50
|
+
'I7' => 'A', # Address Match
|
51
|
+
'I8' => 'C', # No Match
|
52
|
+
}
|
53
|
+
|
54
|
+
PAYWAY_WS_SUCCESS = '5000'
|
55
|
+
|
56
|
+
SCRUB_PATTERNS = [
|
57
|
+
%r(("password\\?":\\?")[^\\]+),
|
58
|
+
%r(("fsv\\?":\\?")\d+),
|
59
|
+
%r(("fsv\\?": \\?")\d+),
|
60
|
+
%r(("accountNumber\\?":\\?")\d+),
|
61
|
+
%r(("accountNumber\\?": \\?")[^\\]+),
|
62
|
+
%r(("Invalid account number: )\d+)
|
63
|
+
].freeze
|
64
|
+
|
65
|
+
SCRUB_REPLACEMENT = '\1[FILTERED]'
|
66
|
+
|
67
|
+
def initialize(options = {})
|
68
|
+
requires!(options, :login, :password, :company_id, :source_id)
|
69
|
+
super
|
70
|
+
end
|
71
|
+
|
72
|
+
def purchase(money, payment, options = {})
|
73
|
+
post = {}
|
74
|
+
add_common(post, options)
|
75
|
+
add_card_payment(post, payment, options)
|
76
|
+
add_card_transaction_details(post, money, options)
|
77
|
+
add_address(post, payment, options)
|
78
|
+
|
79
|
+
commit('sale', post)
|
80
|
+
end
|
81
|
+
|
82
|
+
def authorize(money, payment, options = {})
|
83
|
+
post = {}
|
84
|
+
add_common(post, options)
|
85
|
+
add_card_payment(post, payment, options)
|
86
|
+
add_card_transaction_details(post, money, options)
|
87
|
+
add_address(post, payment, options)
|
88
|
+
|
89
|
+
commit('authorize', post)
|
90
|
+
end
|
91
|
+
|
92
|
+
def capture(money, authorization, options = {})
|
93
|
+
post = {}
|
94
|
+
add_common(post, options)
|
95
|
+
add_card_transaction_name(post, authorization, options)
|
96
|
+
|
97
|
+
commit('capture', post)
|
98
|
+
end
|
99
|
+
|
100
|
+
def credit(money, payment, options = {})
|
101
|
+
post = {}
|
102
|
+
add_common(post, options)
|
103
|
+
add_card_payment(post, payment, options)
|
104
|
+
add_card_transaction_details(post, money, options)
|
105
|
+
add_address(post, payment, options)
|
106
|
+
|
107
|
+
commit('credit', post)
|
108
|
+
end
|
109
|
+
|
110
|
+
def void(authorization, options = {})
|
111
|
+
post = {}
|
112
|
+
add_common(post, options)
|
113
|
+
add_card_transaction_name(post, authorization, options)
|
114
|
+
|
115
|
+
commit('void', post)
|
116
|
+
end
|
117
|
+
|
118
|
+
def supports_scrubbing?
|
119
|
+
true
|
120
|
+
end
|
121
|
+
|
122
|
+
def scrub(transcript)
|
123
|
+
SCRUB_PATTERNS.inject(transcript) do |text, pattern|
|
124
|
+
text.gsub(pattern, SCRUB_REPLACEMENT)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def add_common(post, options)
|
131
|
+
post[:userName] = @options[:login]
|
132
|
+
post[:password] = @options[:password]
|
133
|
+
post[:companyId] = @options[:company_id]
|
134
|
+
post[:cardTransaction] = {}
|
135
|
+
post[:cardTransaction][:sourceId] = @options[:source_id]
|
136
|
+
end
|
137
|
+
|
138
|
+
def add_address(post, payment, options)
|
139
|
+
post[:cardAccount] ||= {}
|
140
|
+
address = options[:billing_address] || options[:address] || {}
|
141
|
+
first_name, last_name = split_names(address[:name])
|
142
|
+
full_address = "#{address[:address1]} #{address[:address2]}".strip
|
143
|
+
phone = address[:phone] || address[:phone_number]
|
144
|
+
|
145
|
+
post[:cardAccount][:firstName] = first_name if first_name
|
146
|
+
post[:cardAccount][:lastName] = last_name if last_name
|
147
|
+
post[:cardAccount][:address] = full_address if full_address
|
148
|
+
post[:cardAccount][:city] = address[:city] if address[:city]
|
149
|
+
post[:cardAccount][:state] = address[:state] if address[:state]
|
150
|
+
post[:cardAccount][:zip] = address[:zip] if address[:zip]
|
151
|
+
post[:cardAccount][:phone] = phone if phone
|
152
|
+
end
|
153
|
+
|
154
|
+
def add_card_transaction_details(post, money, options)
|
155
|
+
post[:cardTransaction][:amount] = amount(money)
|
156
|
+
eci_type = options[:eci_type].nil? ? '1' : options[:eci_type]
|
157
|
+
post[:cardTransaction][:eciType] = eci_type
|
158
|
+
post[:cardTransaction][:processorSoftDescriptor] = options[:processor_soft_descriptor] if options[:processor_soft_descriptor]
|
159
|
+
post[:cardTransaction][:tax] = options[:tax] if options[:tax]
|
160
|
+
end
|
161
|
+
|
162
|
+
def add_card_transaction_name(post, identifier, options)
|
163
|
+
post[:cardTransaction][:name] = identifier
|
164
|
+
end
|
165
|
+
|
166
|
+
def add_card_payment(post, payment, options)
|
167
|
+
# credit_card
|
168
|
+
post[:accountInputMode] = 'primaryAccountNumber'
|
169
|
+
|
170
|
+
post[:cardAccount] ||= {}
|
171
|
+
post[:cardAccount][:accountNumber] = payment.number
|
172
|
+
post[:cardAccount][:fsv] = payment.verification_value
|
173
|
+
post[:cardAccount][:expirationDate] = expdate(payment)
|
174
|
+
post[:cardAccount][:email] = options[:email] if options[:email]
|
175
|
+
end
|
176
|
+
|
177
|
+
def expdate(credit_card)
|
178
|
+
year = format(credit_card.year, :four_digits)
|
179
|
+
month = format(credit_card.month, :two_digits)
|
180
|
+
|
181
|
+
month + year
|
182
|
+
end
|
183
|
+
|
184
|
+
def parse(body)
|
185
|
+
body.blank? ? {} : JSON.parse(body)
|
186
|
+
end
|
187
|
+
|
188
|
+
def commit(action, parameters)
|
189
|
+
parameters[:request] = action
|
190
|
+
|
191
|
+
url = (test? ? test_url : live_url)
|
192
|
+
payload = parameters.to_json unless parameters.nil?
|
193
|
+
|
194
|
+
response =
|
195
|
+
begin
|
196
|
+
parse(ssl_request(:post, url, payload, headers))
|
197
|
+
rescue ResponseError => e
|
198
|
+
return Response.new(false, 'Invalid Login') if e.response.code == '401'
|
199
|
+
|
200
|
+
parse(e.response.body)
|
201
|
+
end
|
202
|
+
|
203
|
+
success = success_from(response)
|
204
|
+
avs_result_code = response['cardTransaction'].nil? || response['cardTransaction']['addressVerificationResults'].nil? ? '' : response['cardTransaction']['addressVerificationResults']
|
205
|
+
avs_result = AVSResult.new(code: AVS_MAPPING[avs_result_code])
|
206
|
+
cvv_result = CVVResult.new(response['cardTransaction']['fraudSecurityResults']) if response['cardTransaction'] && response['cardTransaction']['fraudSecurityResults']
|
207
|
+
|
208
|
+
Response.new(
|
209
|
+
success,
|
210
|
+
message_from(success, response),
|
211
|
+
response,
|
212
|
+
test: test?,
|
213
|
+
error_code: error_code_from(response),
|
214
|
+
authorization: authorization_from(response),
|
215
|
+
avs_result: avs_result,
|
216
|
+
cvv_result: cvv_result
|
217
|
+
)
|
218
|
+
end
|
219
|
+
|
220
|
+
def success_from(response)
|
221
|
+
response['paywayCode'] == PAYWAY_WS_SUCCESS
|
222
|
+
end
|
223
|
+
|
224
|
+
def error_code_from(response)
|
225
|
+
return '' if success_from(response)
|
226
|
+
|
227
|
+
error = !STANDARD_ERROR_CODE_MAPPING[response['paywayCode']].nil? ? STANDARD_ERROR_CODE_MAPPING[response['paywayCode']] : STANDARD_ERROR_CODE[:processing_error]
|
228
|
+
return error
|
229
|
+
end
|
230
|
+
|
231
|
+
def message_from(success, response)
|
232
|
+
return '' if response['paywayCode'].nil?
|
233
|
+
|
234
|
+
return response['paywayCode'] + '-' + 'success' if success
|
235
|
+
|
236
|
+
response['paywayCode'] + '-' + response['paywayMessage']
|
237
|
+
end
|
238
|
+
|
239
|
+
def authorization_from(response)
|
240
|
+
return '' if !success_from(response) || response['cardTransaction'].nil?
|
241
|
+
|
242
|
+
response['cardTransaction']['name']
|
243
|
+
end
|
244
|
+
|
245
|
+
def headers
|
246
|
+
{
|
247
|
+
'Accept' => 'application/json',
|
248
|
+
'Content-type' => 'application/json'
|
249
|
+
}
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
@@ -30,6 +30,7 @@ module ActiveMerchant #:nodoc:
|
|
30
30
|
add_address(post, creditcard, options)
|
31
31
|
add_capture(post, options)
|
32
32
|
add_metadata(post, options)
|
33
|
+
add_3ds(post, options)
|
33
34
|
|
34
35
|
commit(:post, 'charges', post, options)
|
35
36
|
end
|
@@ -158,6 +159,16 @@ module ActiveMerchant #:nodoc:
|
|
158
159
|
post[:metadata] = options[:metadata] if options[:metadata]
|
159
160
|
end
|
160
161
|
|
162
|
+
def add_3ds(post, options)
|
163
|
+
if options[:three_d_secure]
|
164
|
+
post[:three_d_secure] = {}
|
165
|
+
post[:three_d_secure][:version] = options[:three_d_secure][:version] if options[:three_d_secure][:version]
|
166
|
+
post[:three_d_secure][:eci] = options[:three_d_secure][:eci] if options[:three_d_secure][:eci]
|
167
|
+
post[:three_d_secure][:cavv] = options[:three_d_secure][:cavv] if options[:three_d_secure][:cavv]
|
168
|
+
post[:three_d_secure][:transaction_id] = options[:three_d_secure][:ds_transaction_id] || options[:three_d_secure][:xid]
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
161
172
|
def headers(params = {})
|
162
173
|
result = {
|
163
174
|
'Content-Type' => 'application/json',
|