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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +217 -0
  3. data/README.md +5 -3
  4. data/lib/active_merchant/billing/check.rb +19 -12
  5. data/lib/active_merchant/billing/credit_card.rb +6 -0
  6. data/lib/active_merchant/billing/credit_card_formatting.rb +1 -0
  7. data/lib/active_merchant/billing/credit_card_methods.rb +96 -22
  8. data/lib/active_merchant/billing/gateways/adyen.rb +38 -21
  9. data/lib/active_merchant/billing/gateways/authorize_net.rb +19 -11
  10. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +4 -0
  11. data/lib/active_merchant/billing/gateways/blue_pay.rb +29 -0
  12. data/lib/active_merchant/billing/gateways/blue_snap.rb +5 -3
  13. data/lib/active_merchant/billing/gateways/braintree_blue.rb +58 -8
  14. data/lib/active_merchant/billing/gateways/cashnet.rb +7 -2
  15. data/lib/active_merchant/billing/gateways/checkout_v2.rb +31 -0
  16. data/lib/active_merchant/billing/gateways/credorax.rb +16 -9
  17. data/lib/active_merchant/billing/gateways/cyber_source.rb +67 -9
  18. data/lib/active_merchant/billing/gateways/d_local.rb +1 -1
  19. data/lib/active_merchant/billing/gateways/decidir.rb +29 -3
  20. data/lib/active_merchant/billing/gateways/elavon.rb +110 -26
  21. data/lib/active_merchant/billing/gateways/element.rb +2 -0
  22. data/lib/active_merchant/billing/gateways/eway_rapid.rb +13 -0
  23. data/lib/active_merchant/billing/gateways/firstdata_e4_v27.rb +17 -6
  24. data/lib/active_merchant/billing/gateways/forte.rb +12 -0
  25. data/lib/active_merchant/billing/gateways/global_collect.rb +25 -16
  26. data/lib/active_merchant/billing/gateways/hps.rb +65 -2
  27. data/lib/active_merchant/billing/gateways/kushki.rb +23 -0
  28. data/lib/active_merchant/billing/gateways/litle.rb +9 -4
  29. data/lib/active_merchant/billing/gateways/mercado_pago.rb +5 -4
  30. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +2 -0
  31. data/lib/active_merchant/billing/gateways/moka.rb +277 -0
  32. data/lib/active_merchant/billing/gateways/monei.rb +228 -144
  33. data/lib/active_merchant/billing/gateways/mundipagg.rb +14 -5
  34. data/lib/active_merchant/billing/gateways/netbanx.rb +37 -2
  35. data/lib/active_merchant/billing/gateways/nmi.rb +14 -9
  36. data/lib/active_merchant/billing/gateways/orbital.rb +202 -47
  37. data/lib/active_merchant/billing/gateways/pay_arc.rb +390 -0
  38. data/lib/active_merchant/billing/gateways/pay_trace.rb +404 -0
  39. data/lib/active_merchant/billing/gateways/payeezy.rb +57 -11
  40. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -0
  41. data/lib/active_merchant/billing/gateways/payflow.rb +9 -0
  42. data/lib/active_merchant/billing/gateways/payment_express.rb +10 -5
  43. data/lib/active_merchant/billing/gateways/paymentez.rb +26 -1
  44. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +1 -0
  45. data/lib/active_merchant/billing/gateways/paypal.rb +10 -2
  46. data/lib/active_merchant/billing/gateways/paypal_express.rb +1 -0
  47. data/lib/active_merchant/billing/gateways/paysafe.rb +291 -0
  48. data/lib/active_merchant/billing/gateways/payu_latam.rb +3 -3
  49. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +253 -0
  50. data/lib/active_merchant/billing/gateways/pin.rb +11 -0
  51. data/lib/active_merchant/billing/gateways/qvalent.rb +23 -9
  52. data/lib/active_merchant/billing/gateways/redsys.rb +78 -30
  53. data/lib/active_merchant/billing/gateways/safe_charge.rb +19 -8
  54. data/lib/active_merchant/billing/gateways/spreedly_core.rb +13 -4
  55. data/lib/active_merchant/billing/gateways/stripe.rb +8 -8
  56. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +86 -25
  57. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +1 -1
  58. data/lib/active_merchant/billing/gateways/vpos.rb +220 -0
  59. data/lib/active_merchant/billing/gateways/worldpay.rb +68 -20
  60. data/lib/active_merchant/billing/response.rb +2 -1
  61. data/lib/active_merchant/billing/three_d_secure_eci_mapper.rb +27 -0
  62. data/lib/active_merchant/billing.rb +1 -0
  63. data/lib/active_merchant/version.rb +1 -1
  64. data/lib/certs/cacert.pem +1582 -2431
  65. metadata +10 -3
@@ -33,7 +33,7 @@ module ActiveMerchant #:nodoc:
33
33
  add_creator_info(post, options)
34
34
  add_fraud_fields(post, options)
35
35
  add_external_cardholder_authentication_data(post, options)
36
- commit(:authorize, post)
36
+ commit(:authorize, post, options: options)
37
37
  end
38
38
 
39
39
  def capture(money, authorization, options = {})
@@ -41,7 +41,7 @@ module ActiveMerchant #:nodoc:
41
41
  add_order(post, money, options, capture: true)
42
42
  add_customer_data(post, options)
43
43
  add_creator_info(post, options)
44
- commit(:capture, post, authorization)
44
+ commit(:capture, post, authorization: authorization)
45
45
  end
46
46
 
47
47
  def refund(money, authorization, options = {})
@@ -49,13 +49,13 @@ module ActiveMerchant #:nodoc:
49
49
  add_amount(post, money, options)
50
50
  add_refund_customer_data(post, options)
51
51
  add_creator_info(post, options)
52
- commit(:refund, post, authorization)
52
+ commit(:refund, post, authorization: authorization)
53
53
  end
54
54
 
55
55
  def void(authorization, options = {})
56
56
  post = nestable_hash
57
57
  add_creator_info(post, options)
58
- commit(:void, post, authorization)
58
+ commit(:void, post, authorization: authorization)
59
59
  end
60
60
 
61
61
  def verify(payment, options = {})
@@ -201,21 +201,21 @@ module ActiveMerchant #:nodoc:
201
201
  shipping_address = options[:shipping_address]
202
202
  if billing_address = options[:billing_address] || options[:address]
203
203
  post['order']['customer']['billingAddress'] = {
204
- 'street' => billing_address[:address1],
205
- 'additionalInfo' => billing_address[:address2],
204
+ 'street' => truncate(billing_address[:address1], 50),
205
+ 'additionalInfo' => truncate(billing_address[:address2], 50),
206
206
  'zip' => billing_address[:zip],
207
207
  'city' => billing_address[:city],
208
- 'state' => billing_address[:state],
208
+ 'state' => truncate(billing_address[:state], 35),
209
209
  'countryCode' => billing_address[:country]
210
210
  }
211
211
  end
212
212
  if shipping_address
213
213
  post['order']['customer']['shippingAddress'] = {
214
- 'street' => shipping_address[:address1],
215
- 'additionalInfo' => shipping_address[:address2],
214
+ 'street' => truncate(shipping_address[:address1], 50),
215
+ 'additionalInfo' => truncate(shipping_address[:address2], 50),
216
216
  'zip' => shipping_address[:zip],
217
217
  'city' => shipping_address[:city],
218
- 'state' => shipping_address[:state],
218
+ 'state' => truncate(shipping_address[:state], 35),
219
219
  'countryCode' => shipping_address[:country]
220
220
  }
221
221
  post['order']['customer']['shippingAddress']['name'] = {
@@ -277,9 +277,13 @@ module ActiveMerchant #:nodoc:
277
277
  end
278
278
  end
279
279
 
280
- def commit(action, post, authorization = nil)
280
+ def idempotency_key_for_signature(options)
281
+ "x-gcs-idempotence-key:#{options[:idempotency_key]}" if options[:idempotency_key]
282
+ end
283
+
284
+ def commit(action, post, authorization: nil, options: {})
281
285
  begin
282
- raw_response = ssl_post(url(action, authorization), post.to_json, headers(action, post, authorization))
286
+ raw_response = ssl_post(url(action, authorization), post.to_json, headers(action, post, authorization, options))
283
287
  response = parse(raw_response)
284
288
  rescue ResponseError => e
285
289
  response = parse(e.response.body) if e.response.code.to_i >= 400
@@ -306,21 +310,26 @@ module ActiveMerchant #:nodoc:
306
310
  }
307
311
  end
308
312
 
309
- def headers(action, post, authorization = nil)
310
- {
313
+ def headers(action, post, authorization = nil, options = {})
314
+ headers = {
311
315
  'Content-Type' => content_type,
312
- 'Authorization' => auth_digest(action, post, authorization),
316
+ 'Authorization' => auth_digest(action, post, authorization, options),
313
317
  'Date' => date
314
318
  }
319
+
320
+ headers['X-GCS-Idempotence-Key'] = options[:idempotency_key] if options[:idempotency_key]
321
+ headers
315
322
  end
316
323
 
317
- def auth_digest(action, post, authorization = nil)
324
+ def auth_digest(action, post, authorization = nil, options = {})
318
325
  data = <<~REQUEST
319
326
  POST
320
327
  #{content_type}
321
328
  #{date}
329
+ #{idempotency_key_for_signature(options)}
322
330
  #{uri(action, authorization)}
323
331
  REQUEST
332
+ data = data.each_line.reject { |line| line.strip == '' }.join
324
333
  digest = OpenSSL::Digest.new('sha256')
325
334
  key = @options[:secret_api_key]
326
335
  "GCS v1HMAC:#{@options[:api_key_id]}:#{Base64.strict_encode64(OpenSSL::HMAC.digest(digest, key, data))}"
@@ -39,6 +39,7 @@ module ActiveMerchant #:nodoc:
39
39
  add_descriptor_name(xml, options)
40
40
  add_card_or_token_payment(xml, card_or_token, options)
41
41
  add_three_d_secure(xml, card_or_token, options)
42
+ add_stored_credentials(xml, options)
42
43
  end
43
44
  end
44
45
 
@@ -52,6 +53,8 @@ module ActiveMerchant #:nodoc:
52
53
  def purchase(money, payment_method, options = {})
53
54
  if payment_method.is_a?(Check)
54
55
  commit_check_sale(money, payment_method, options)
56
+ elsif options.dig(:stored_credential, :reason_type) == 'recurring'
57
+ commit_recurring_billing_sale(money, payment_method, options)
55
58
  else
56
59
  commit_credit_sale(money, payment_method, options)
57
60
  end
@@ -67,6 +70,15 @@ module ActiveMerchant #:nodoc:
67
70
  end
68
71
  end
69
72
 
73
+ def credit(money, payment_method, options = {})
74
+ commit('CreditReturn') do |xml|
75
+ add_amount(xml, money)
76
+ add_allow_dup(xml)
77
+ add_card_or_token_payment(xml, payment_method, options)
78
+ add_details(xml, options)
79
+ end
80
+ end
81
+
70
82
  def verify(card_or_token, options = {})
71
83
  commit('CreditAccountVerify') do |xml|
72
84
  add_card_or_token_customer_data(xml, card_or_token, options)
@@ -122,6 +134,21 @@ module ActiveMerchant #:nodoc:
122
134
  add_descriptor_name(xml, options)
123
135
  add_card_or_token_payment(xml, card_or_token, options)
124
136
  add_three_d_secure(xml, card_or_token, options)
137
+ add_stored_credentials(xml, options)
138
+ end
139
+ end
140
+
141
+ def commit_recurring_billing_sale(money, card_or_token, options)
142
+ commit('RecurringBilling') do |xml|
143
+ add_amount(xml, money)
144
+ add_allow_dup(xml)
145
+ add_card_or_token_customer_data(xml, card_or_token, options)
146
+ add_details(xml, options)
147
+ add_descriptor_name(xml, options)
148
+ add_card_or_token_payment(xml, card_or_token, options)
149
+ add_three_d_secure(xml, card_or_token, options)
150
+ add_stored_credentials(xml, options)
151
+ add_stored_credentials_for_recurring_billing(xml, options)
125
152
  end
126
153
  end
127
154
 
@@ -148,7 +175,7 @@ module ActiveMerchant #:nodoc:
148
175
  xml.hps :CardHolderAddr, billing_address[:address1] if billing_address[:address1]
149
176
  xml.hps :CardHolderCity, billing_address[:city] if billing_address[:city]
150
177
  xml.hps :CardHolderState, billing_address[:state] if billing_address[:state]
151
- xml.hps :CardHolderZip, billing_address[:zip] if billing_address[:zip]
178
+ xml.hps :CardHolderZip, alphanumeric_zip(billing_address[:zip]) if billing_address[:zip]
152
179
  end
153
180
  end
154
181
  end
@@ -210,7 +237,7 @@ module ActiveMerchant #:nodoc:
210
237
  def add_details(xml, options)
211
238
  xml.hps :AdditionalTxnFields do
212
239
  xml.hps :Description, options[:description] if options[:description]
213
- xml.hps :InvoiceNbr, options[:order_id] if options[:order_id]
240
+ xml.hps :InvoiceNbr, options[:order_id][0..59] if options[:order_id]
214
241
  xml.hps :CustomerID, options[:customer_id] if options[:customer_id]
215
242
  end
216
243
  end
@@ -256,6 +283,38 @@ module ActiveMerchant #:nodoc:
256
283
  end
257
284
  end
258
285
 
286
+ # We do not currently support installments on this gateway.
287
+ # The HPS gateway treats recurring transactions as a seperate transaction type
288
+ def add_stored_credentials(xml, options)
289
+ return unless options[:stored_credential]
290
+
291
+ xml.hps :CardOnFileData do
292
+ if options[:stored_credential][:initiator] == 'customer'
293
+ xml.hps :CardOnFile, 'C'
294
+ elsif options[:stored_credential][:initiator] == 'merchant'
295
+ xml.hps :CardOnFile, 'M'
296
+ else
297
+ return
298
+ end
299
+
300
+ if options[:stored_credential][:network_transaction_id]
301
+ xml.hps :CardBrandTxnId, options[:stored_credential][:network_transaction_id]
302
+ else
303
+ return
304
+ end
305
+ end
306
+ end
307
+
308
+ def add_stored_credentials_for_recurring_billing(xml, options)
309
+ xml.hps :RecurringData do
310
+ if options[:stored_credential][:reason_type] = 'recurring'
311
+ xml.hps :OneTime, 'N'
312
+ else
313
+ xml.hps :OneTime, 'Y'
314
+ end
315
+ end
316
+ end
317
+
259
318
  def strip_leading_zero(value)
260
319
  return value unless value[0] == '0'
261
320
 
@@ -380,6 +439,10 @@ module ActiveMerchant #:nodoc:
380
439
  @options[:secret_api_key]&.include?('_cert_')
381
440
  end
382
441
 
442
+ def alphanumeric_zip(zip)
443
+ zip.gsub(/[^0-9a-z]/i, '')
444
+ end
445
+
383
446
  ISSUER_MESSAGES = {
384
447
  '13' => 'Must be greater than or equal 0.',
385
448
  '14' => 'The card number is incorrect.',
@@ -37,6 +37,7 @@ module ActiveMerchant #:nodoc:
37
37
  post = {}
38
38
  post[:ticketNumber] = authorization
39
39
  add_invoice(action, post, amount, options)
40
+ add_full_response(post, options)
40
41
 
41
42
  commit(action, post)
42
43
  end
@@ -46,6 +47,7 @@ module ActiveMerchant #:nodoc:
46
47
 
47
48
  post = {}
48
49
  post[:ticketNumber] = authorization
50
+ add_full_response(post, options)
49
51
 
50
52
  commit(action, post)
51
53
  end
@@ -55,6 +57,7 @@ module ActiveMerchant #:nodoc:
55
57
 
56
58
  post = {}
57
59
  post[:ticketNumber] = authorization
60
+ add_full_response(post, options)
58
61
 
59
62
  commit(action, post)
60
63
  end
@@ -78,6 +81,7 @@ module ActiveMerchant #:nodoc:
78
81
  post = {}
79
82
  add_invoice(action, post, amount, options)
80
83
  add_payment_method(post, payment_method, options)
84
+ add_full_response(post, options)
81
85
 
82
86
  commit(action, post)
83
87
  end
@@ -88,6 +92,8 @@ module ActiveMerchant #:nodoc:
88
92
  post = {}
89
93
  add_reference(post, authorization, options)
90
94
  add_invoice(action, post, amount, options)
95
+ add_contact_details(post, options[:contact_details]) if options[:contact_details]
96
+ add_full_response(post, options)
91
97
 
92
98
  commit(action, post)
93
99
  end
@@ -98,6 +104,7 @@ module ActiveMerchant #:nodoc:
98
104
  post = {}
99
105
  add_reference(post, authorization, options)
100
106
  add_invoice(action, post, amount, options)
107
+ add_full_response(post, options)
101
108
 
102
109
  commit(action, post)
103
110
  end
@@ -154,6 +161,22 @@ module ActiveMerchant #:nodoc:
154
161
  post[:token] = authorization
155
162
  end
156
163
 
164
+ def add_contact_details(post, contact_details_options)
165
+ contact_details = {}
166
+ contact_details[:documentType] = contact_details_options[:document_type] if contact_details_options[:document_type]
167
+ contact_details[:documentNumber] = contact_details_options[:document_number] if contact_details_options[:document_number]
168
+ contact_details[:email] = contact_details_options[:email] if contact_details_options[:email]
169
+ contact_details[:firstName] = contact_details_options[:first_name] if contact_details_options[:first_name]
170
+ contact_details[:lastName] = contact_details_options[:last_name] if contact_details_options[:last_name]
171
+ contact_details[:secondLastName] = contact_details_options[:second_last_name] if contact_details_options[:second_last_name]
172
+ contact_details[:phoneNumber] = contact_details_options[:phone_number] if contact_details_options[:phone_number]
173
+ post[:contactDetails] = contact_details
174
+ end
175
+
176
+ def add_full_response(post, options)
177
+ post[:fullResponse] = options[:full_response].to_s.casecmp('true').zero? if options[:full_response]
178
+ end
179
+
157
180
  ENDPOINT = {
158
181
  'tokenize' => 'tokens',
159
182
  'charge' => 'charges',
@@ -5,7 +5,10 @@ module ActiveMerchant #:nodoc:
5
5
  class LitleGateway < Gateway
6
6
  SCHEMA_VERSION = '9.14'
7
7
 
8
+ class_attribute :postlive_url
9
+
8
10
  self.test_url = 'https://www.testvantivcnp.com/sandbox/communicator/online'
11
+ self.postlive_url = 'https://payments.vantivpostlive.com/vap/communicator/online'
9
12
  self.live_url = 'https://payments.vantivcnp.com/vap/communicator/online'
10
13
 
11
14
  self.supported_countries = ['US']
@@ -368,9 +371,9 @@ module ActiveMerchant #:nodoc:
368
371
  return unless address
369
372
 
370
373
  doc.companyName(address[:company]) unless address[:company].blank?
371
- doc.addressLine1(address[:address1]) unless address[:address1].blank?
372
- doc.addressLine2(address[:address2]) unless address[:address2].blank?
373
- doc.city(address[:city]) unless address[:city].blank?
374
+ doc.addressLine1(truncate(address[:address1], 35)) unless address[:address1].blank?
375
+ doc.addressLine2(truncate(address[:address2], 35)) unless address[:address2].blank?
376
+ doc.city(truncate(address[:city], 35)) unless address[:city].blank?
374
377
  doc.state(address[:state]) unless address[:state].blank?
375
378
  doc.zip(address[:zip]) unless address[:zip].blank?
376
379
  doc.country(address[:country]) unless address[:country].blank?
@@ -491,7 +494,7 @@ module ActiveMerchant #:nodoc:
491
494
  attributes = {}
492
495
  attributes[:id] = truncate(options[:id] || options[:order_id], 24)
493
496
  attributes[:reportGroup] = options[:merchant] || 'Default Report Group'
494
- attributes[:customerId] = options[:customer]
497
+ attributes[:customerId] = options[:customer_id]
495
498
  attributes.delete_if { |_key, value| value == nil }
496
499
  attributes
497
500
  end
@@ -513,6 +516,8 @@ module ActiveMerchant #:nodoc:
513
516
  end
514
517
 
515
518
  def url
519
+ return postlive_url if @options[:url_override].to_s == 'postlive'
520
+
516
521
  test? ? test_url : live_url
517
522
  end
518
523
 
@@ -4,7 +4,7 @@ module ActiveMerchant #:nodoc:
4
4
  self.live_url = self.test_url = 'https://api.mercadopago.com/v1'
5
5
 
6
6
  self.supported_countries = %w[AR BR CL CO MX PE UY]
7
- self.supported_cardtypes = %i[visa master american_express elo cabal naranja]
7
+ self.supported_cardtypes = %i[visa master american_express elo cabal naranja creditel]
8
8
 
9
9
  self.homepage_url = 'https://www.mercadopago.com/'
10
10
  self.display_name = 'Mercado Pago'
@@ -105,7 +105,7 @@ module ActiveMerchant #:nodoc:
105
105
 
106
106
  def authorize_request(money, payment, options = {})
107
107
  post = purchase_request(money, payment, options)
108
- post[:capture] = false
108
+ post[:capture] = options[:capture] || false
109
109
  post
110
110
  end
111
111
 
@@ -129,6 +129,7 @@ module ActiveMerchant #:nodoc:
129
129
 
130
130
  def add_additional_data(post, options)
131
131
  post[:sponsor_id] = options[:sponsor_id]
132
+ post[:metadata] = options[:metadata] if options[:metadata]
132
133
  post[:device_id] = options[:device_id] if options[:device_id]
133
134
  post[:additional_info] = {
134
135
  ip_address: options[:ip_address]
@@ -143,7 +144,7 @@ module ActiveMerchant #:nodoc:
143
144
  email: options[:email],
144
145
  first_name: payment.first_name,
145
146
  last_name: payment.last_name
146
- }
147
+ }.merge(options[:payer] || {})
147
148
  end
148
149
 
149
150
  def add_address(post, options)
@@ -191,7 +192,7 @@ module ActiveMerchant #:nodoc:
191
192
  post[:description] = options[:description]
192
193
  post[:installments] = options[:installments] ? options[:installments].to_i : 1
193
194
  post[:statement_descriptor] = options[:statement_descriptor] if options[:statement_descriptor]
194
- post[:external_reference] = options[:order_id] || SecureRandom.hex(16)
195
+ post[:external_reference] = options[:order_id] || options[:external_reference] || SecureRandom.hex(16)
195
196
  end
196
197
 
197
198
  def add_payment(post, options)
@@ -187,6 +187,8 @@ module ActiveMerchant #:nodoc:
187
187
  def parse(body)
188
188
  xml = REXML::Document.new(body)
189
189
 
190
+ return { response_message: 'Invalid gateway response' } unless xml.root.present?
191
+
190
192
  response = {}
191
193
  xml.root.elements.to_a.each do |node|
192
194
  parse_element(response, node)
@@ -0,0 +1,277 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class MokaGateway < Gateway
4
+ self.test_url = 'https://service.testmoka.com'
5
+ self.live_url = 'https://service.moka.com'
6
+
7
+ self.supported_countries = %w[GB TR US]
8
+ self.default_currency = 'TRY'
9
+ self.money_format = :dollars
10
+ self.supported_cardtypes = %i[visa master american_express discover]
11
+
12
+ self.homepage_url = 'http://developer.moka.com/'
13
+ self.display_name = 'Moka'
14
+
15
+ ERROR_CODE_MAPPING = {
16
+ '000' => 'General error',
17
+ '001' => '3D Not authenticated',
18
+ '002' => 'Limit is insufficient',
19
+ '003' => 'Credit card number format is wrong',
20
+ '004' => 'General decline',
21
+ '005' => 'This process is invalid for the card owner',
22
+ '006' => 'Expiration date is wrong',
23
+ '007' => 'Invalid transaction',
24
+ '008' => 'Connection with the bank not established',
25
+ '009' => 'Undefined error code',
26
+ '010' => 'Bank SSL error',
27
+ '011' => 'Call the bank for the manual authentication',
28
+ '012' => 'Card info is wrong - Kart Number or CVV2',
29
+ '013' => '3D secure is not supported other than Visa MC cards',
30
+ '014' => 'Invalid account number',
31
+ '015' => 'CVV is wrong',
32
+ '016' => 'Authentication process is not present',
33
+ '017' => 'System error',
34
+ '018' => 'Stolen card',
35
+ '019' => 'Lost card',
36
+ '020' => 'Card with limited properties',
37
+ '021' => 'Timeout',
38
+ '022' => 'Invalid merchant',
39
+ '023' => 'False authentication',
40
+ '024' => '3D authorization is successful but the process cannot be completed',
41
+ '025' => '3D authorization failure',
42
+ '026' => 'Either the issuer bank or the card is not enrolled to the 3D process',
43
+ '027' => 'The bank did not allow the process',
44
+ '028' => 'Fraud suspect',
45
+ '029' => 'The card is closed to the e-commerce operations'
46
+ }
47
+
48
+ def initialize(options = {})
49
+ requires!(options, :dealer_code, :username, :password)
50
+ super
51
+ end
52
+
53
+ def purchase(money, payment, options = {})
54
+ post = {}
55
+ post[:PaymentDealerRequest] = {}
56
+ options[:pre_auth] = 0
57
+ add_auth_purchase(post, money, payment, options)
58
+
59
+ commit('purchase', post)
60
+ end
61
+
62
+ def authorize(money, payment, options = {})
63
+ post = {}
64
+ post[:PaymentDealerRequest] = {}
65
+ options[:pre_auth] = 1
66
+ add_auth_purchase(post, money, payment, options)
67
+
68
+ commit('authorize', post)
69
+ end
70
+
71
+ def capture(money, authorization, options = {})
72
+ post = {}
73
+ post[:PaymentDealerRequest] = {}
74
+ add_payment_dealer_authentication(post)
75
+ add_transaction_reference(post, authorization)
76
+ add_additional_transaction_data(post, options)
77
+
78
+ commit('capture', post)
79
+ end
80
+
81
+ def refund(money, authorization, options = {})
82
+ post = {}
83
+ post[:PaymentDealerRequest] = {}
84
+ add_payment_dealer_authentication(post)
85
+ add_transaction_reference(post, authorization)
86
+ add_additional_transaction_data(post, options)
87
+ add_void_refund_reason(post)
88
+ add_amount(post, money)
89
+
90
+ commit('refund', post)
91
+ end
92
+
93
+ def void(authorization, options = {})
94
+ post = {}
95
+ post[:PaymentDealerRequest] = {}
96
+ add_payment_dealer_authentication(post)
97
+ add_transaction_reference(post, authorization)
98
+ add_additional_transaction_data(post, options)
99
+ add_void_refund_reason(post)
100
+
101
+ commit('void', 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
+ def supports_scrubbing?
112
+ true
113
+ end
114
+
115
+ def scrub(transcript)
116
+ transcript.
117
+ gsub(%r(("CardNumber\\?":\\?")[^"]*)i, '\1[FILTERED]').
118
+ gsub(%r(("CvcNumber\\?":\\?")[^"]*)i, '\1[FILTERED]').
119
+ gsub(%r(("DealerCode\\?":\\?"?)[^"?]*)i, '\1[FILTERED]').
120
+ gsub(%r(("Username\\?":\\?")[^"]*)i, '\1[FILTERED]').
121
+ gsub(%r(("Password\\?":\\?")[^"]*)i, '\1[FILTERED]').
122
+ gsub(%r(("CheckKey\\?":\\?")[^"]*)i, '\1[FILTERED]')
123
+ end
124
+
125
+ private
126
+
127
+ def add_auth_purchase(post, money, payment, options)
128
+ add_payment_dealer_authentication(post)
129
+ add_invoice(post, money, options)
130
+ add_payment(post, payment)
131
+ add_additional_auth_purchase_data(post, options)
132
+ add_additional_transaction_data(post, options)
133
+ add_buyer_information(post, payment, options)
134
+ add_basket_product(post, options[:basket_product]) if options[:basket_product]
135
+ end
136
+
137
+ def add_payment_dealer_authentication(post)
138
+ post[:PaymentDealerAuthentication] = {
139
+ DealerCode: @options[:dealer_code],
140
+ Username: @options[:username],
141
+ Password: @options[:password],
142
+ CheckKey: check_key
143
+ }
144
+ end
145
+
146
+ def check_key
147
+ str = "#{@options[:dealer_code]}MK#{@options[:username]}PD#{@options[:password]}"
148
+ Digest::SHA256.hexdigest(str)
149
+ end
150
+
151
+ def add_invoice(post, money, options)
152
+ post[:PaymentDealerRequest][:Amount] = amount(money) || 0
153
+ post[:PaymentDealerRequest][:Currency] = options[:currency] || 'TL'
154
+ end
155
+
156
+ def add_payment(post, card)
157
+ post[:PaymentDealerRequest][:CardHolderFullName] = card.name
158
+ post[:PaymentDealerRequest][:CardNumber] = card.number
159
+ post[:PaymentDealerRequest][:ExpMonth] = card.month
160
+ post[:PaymentDealerRequest][:ExpYear] = card.year
161
+ post[:PaymentDealerRequest][:CvcNumber] = card.verification_value
162
+ end
163
+
164
+ def add_amount(post, money)
165
+ post[:PaymentDealerRequest][:Amount] = money || 0
166
+ end
167
+
168
+ def add_additional_auth_purchase_data(post, options)
169
+ post[:PaymentDealerRequest][:IsPreAuth] = options[:pre_auth]
170
+ post[:PaymentDealerRequest][:Description] = options[:order_id] if options[:order_id]
171
+ post[:SubMerchantName] = options[:sub_merchant_name] if options[:sub_merchant_name]
172
+ post[:IsPoolPayment] = options[:is_pool_payment] || 0
173
+ end
174
+
175
+ def add_buyer_information(post, card, options)
176
+ obj = {}
177
+
178
+ obj[:BuyerFullName] = card.name || ''
179
+ obj[:BuyerEmail] = options[:email] if options[:email]
180
+ obj[:BuyerAddress] = options[:billing_address][:address1] if options[:billing_address]
181
+ obj[:BuyerGsmNumber] = options[:billing_address][:phone] if options[:billing_address]
182
+
183
+ post[:PaymentDealerRequest][:BuyerInformation] = obj
184
+ end
185
+
186
+ def add_basket_product(post, basket_options)
187
+ basket = []
188
+
189
+ basket_options.each do |product|
190
+ obj = {}
191
+ obj[:ProductId] = product[:product_id] if product[:product_id]
192
+ obj[:ProductCode] = product[:product_code] if product[:product_code]
193
+ obj[:UnitPrice] = amount(product[:unit_price]) if product[:unit_price]
194
+ obj[:Quantity] = product[:quantity] if product[:quantity]
195
+ basket << obj
196
+ end
197
+
198
+ post[:PaymentDealerRequest][:BasketProduct] = basket
199
+ end
200
+
201
+ def add_additional_transaction_data(post, options)
202
+ post[:PaymentDealerRequest][:ClientIP] = options[:ip] if options[:ip]
203
+ post[:PaymentDealerRequest][:OtherTrxCode] = options[:order_id] if options[:order_id]
204
+ end
205
+
206
+ def add_transaction_reference(post, authorization)
207
+ post[:PaymentDealerRequest][:VirtualPosOrderId] = authorization
208
+ end
209
+
210
+ def add_void_refund_reason(post)
211
+ post[:PaymentDealerRequest][:VoidRefundReason] = 2
212
+ end
213
+
214
+ def commit(action, parameters)
215
+ response = parse(ssl_post(url(action), post_data(parameters), request_headers))
216
+ Response.new(
217
+ success_from(response),
218
+ message_from(response),
219
+ response,
220
+ authorization: authorization_from(response),
221
+ test: test?,
222
+ error_code: error_code_from(response)
223
+ )
224
+ end
225
+
226
+ def url(action)
227
+ host = (test? ? test_url : live_url)
228
+ endpoint = endpoint(action)
229
+
230
+ "#{host}/PaymentDealer/#{endpoint}"
231
+ end
232
+
233
+ def endpoint(action)
234
+ case action
235
+ when 'purchase', 'authorize'
236
+ 'DoDirectPayment'
237
+ when 'capture'
238
+ 'DoCapture'
239
+ when 'void'
240
+ 'DoVoid'
241
+ when 'refund'
242
+ 'DoCreateRefundRequest'
243
+ end
244
+ end
245
+
246
+ def request_headers
247
+ { 'Content-Type' => 'application/json' }
248
+ end
249
+
250
+ def post_data(parameters = {})
251
+ JSON.generate(parameters)
252
+ end
253
+
254
+ def parse(body)
255
+ JSON.parse(body)
256
+ end
257
+
258
+ def success_from(response)
259
+ response.dig('Data', 'IsSuccessful')
260
+ end
261
+
262
+ def message_from(response)
263
+ response.dig('Data', 'ResultMessage').presence || response['ResultCode']
264
+ end
265
+
266
+ def authorization_from(response)
267
+ response.dig('Data', 'VirtualPosOrderId')
268
+ end
269
+
270
+ def error_code_from(response)
271
+ codes = [response['ResultCode'], response.dig('Data', 'ResultCode')].flatten
272
+ codes.reject! { |code| code.blank? || code.casecmp('success').zero? }
273
+ codes.map { |code| ERROR_CODE_MAPPING[code] || code }.join(', ')
274
+ end
275
+ end
276
+ end
277
+ end