activemerchant 1.66.0 → 1.67.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.
@@ -116,7 +116,12 @@ module ActiveMerchant #:nodoc:
116
116
  gsub(%r((ccnumber=)\d+), '\1[FILTERED]').
117
117
  gsub(%r((cvv=)\d+), '\1[FILTERED]').
118
118
  gsub(%r((checkaba=)\d+), '\1[FILTERED]').
119
- gsub(%r((checkaccount=)\d+), '\1[FILTERED]')
119
+ gsub(%r((checkaccount=)\d+), '\1[FILTERED]').
120
+ gsub(%r((cryptogram=)[^&]+(&?)), '\1[FILTERED]\2')
121
+ end
122
+
123
+ def supports_network_tokenization?
124
+ true
120
125
  end
121
126
 
122
127
  private
@@ -135,6 +140,10 @@ module ActiveMerchant #:nodoc:
135
140
  def add_payment_method(post, payment_method, options)
136
141
  if(payment_method.is_a?(String))
137
142
  post[:customer_vault_id] = payment_method
143
+ elsif (payment_method.is_a?(NetworkTokenizationCreditCard))
144
+ post[:ccnumber] = payment_method.number
145
+ post[:ccexp] = exp_date(payment_method)
146
+ post[:token_cryptogram] = payment_method.payment_cryptogram
138
147
  elsif(card_brand(payment_method) == 'check')
139
148
  post[:payment] = 'check'
140
149
  post[:checkname] = payment_method.name
@@ -1,26 +1,26 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  class OppGateway < Gateway
4
- # = Open Payment Platform
4
+ # = Open Payment Platform
5
5
  #
6
- # The Open Payment Platform includes a powerful omni-channel transaction processing API,
7
- # enabling you to quickly and flexibly build new applications and services on the platform.
8
- #
9
- # This plugin enables connectivity to the Open Payment Platform for activemerchant.
6
+ # The Open Payment Platform includes a powerful omni-channel transaction processing API,
7
+ # enabling you to quickly and flexibly build new applications and services on the platform.
8
+ #
9
+ # This plugin enables connectivity to the Open Payment Platform for activemerchant.
10
10
  #
11
11
  # For any questions or comments please contact support@payon.com
12
12
  #
13
13
  # == Usage
14
14
  #
15
15
  # gateway = ActiveMerchant::Billing::OppGateway.new(
16
- # user_id: 'merchant user id',
16
+ # user_id: 'merchant user id',
17
17
  # password: 'password',
18
- # entity_id: 'entity id',
18
+ # entity_id: 'entity id',
19
19
  # )
20
20
  #
21
21
  # # set up credit card object as in main ActiveMerchant example
22
22
  # creditcard = ActiveMerchant::Billing::CreditCard.new(
23
- # :type => 'visa',
23
+ # :type => 'visa',
24
24
  # :number => '4242424242424242',
25
25
  # :month => 8,
26
26
  # :year => 2009,
@@ -30,7 +30,7 @@ module ActiveMerchant #:nodoc:
30
30
  #
31
31
  # # Request: complete example, including address, billing address, shipping address
32
32
  # complete_request_options = {
33
- # order_id: "your merchant/shop order id", # alternative is to set merchantInvoiceId
33
+ # order_id: "your merchant/shop order id", # alternative is to set merchantInvoiceId
34
34
  # merchant_transaction_id: "your merchant/shop transaction id",
35
35
  # address: address,
36
36
  # description: 'Store Purchase - Books',
@@ -67,34 +67,34 @@ module ActiveMerchant #:nodoc:
67
67
  # ip: 101.102.103.104,
68
68
  # },
69
69
  # }
70
- #
70
+ #
71
71
  # # Request: minimal example
72
72
  # minimal_request_options = {
73
- # order_id: "your merchant/shop order id", # alternative is to set merchantInvoiceId
73
+ # order_id: "your merchant/shop order id", # alternative is to set merchantInvoiceId
74
74
  # description: 'Store Purchase - Books',
75
75
  # }
76
76
  #
77
- # options =
77
+ # options =
78
78
  # # run request
79
79
  # response = gateway.purchase(754, creditcard, options) # charge 7,54 EUR
80
80
  #
81
81
  # response.success? # Check whether the transaction was successful
82
- # response.error_code # Retrieve the error message - it's mapped to Gateway::STANDARD_ERROR_CODE
82
+ # response.error_code # Retrieve the error message - it's mapped to Gateway::STANDARD_ERROR_CODE
83
83
  # response.message # Retrieve the message returned by opp
84
84
  # response.authorization # Retrieve the unique transaction ID returned by opp
85
85
  # response.params['result']['code'] # Retrieve original return code returned by opp server
86
86
  #
87
87
  # == Errors
88
- # If transaction is not successful, response.error_code contains mapped to Gateway::STANDARD_ERROR_CODE error message.
89
- # Complete list of opp error codes can be viewed on https://docs.oppwa.com/
90
- # Because this list is much bigger than Gateway::STANDARD_ERROR_CODE, only fraction is mapped to Gateway::STANDARD_ERROR_CODE.
91
- # All other codes are mapped as Gateway::STANDARD_ERROR_CODE[:processing_error], so if this is the case,
88
+ # If transaction is not successful, response.error_code contains mapped to Gateway::STANDARD_ERROR_CODE error message.
89
+ # Complete list of opp error codes can be viewed on https://docs.oppwa.com/
90
+ # Because this list is much bigger than Gateway::STANDARD_ERROR_CODE, only fraction is mapped to Gateway::STANDARD_ERROR_CODE.
91
+ # All other codes are mapped as Gateway::STANDARD_ERROR_CODE[:processing_error], so if this is the case,
92
92
  # you may check the original result code from OPP that can be found in response.params['result']['code']
93
- #
93
+ #
94
94
  # == Special features
95
- # For purchase method risk check can be forced when options[:risk_workflow] = true
96
- # This will split (on OPP server side) the transaction into two separate transactions: authorize and capture,
97
- # but capture will be executed only if risk checks are successful.
95
+ # For purchase method risk check can be forced when options[:risk_workflow] = true
96
+ # This will split (on OPP server side) the transaction into two separate transactions: authorize and capture,
97
+ # but capture will be executed only if risk checks are successful.
98
98
  #
99
99
  # For testing you may use the test account details listed fixtures.yml under opp. It is important to note that there are two test modes available:
100
100
  # options[:test_mode]='EXTERNAL' causes test transactions to be forwarded to the processor's test system for 'end-to-end' testing
@@ -102,10 +102,10 @@ module ActiveMerchant #:nodoc:
102
102
  # If no test_mode parameter is sent, test_mode=INTERNAL is the default behaviour.
103
103
  #
104
104
  # Billing Address, Shipping Address, Custom Parameters are supported as described under https://docs.oppwa.com/parameters
105
- # See complete example above for details.
105
+ # See complete example above for details.
106
106
  #
107
107
  # == Tokenization
108
- # When create_registration is set to true, the payment details will be stored and a token will be returned in registrationId response field,
108
+ # When create_registration is set to true, the payment details will be stored and a token will be returned in registrationId response field,
109
109
  # which can subsequently be used to reference the stored payment.
110
110
 
111
111
  self.test_url = 'https://test.oppwa.com/v1/payments'
@@ -113,7 +113,7 @@ module ActiveMerchant #:nodoc:
113
113
 
114
114
  self.supported_countries = %w(AD AI AG AR AU AT BS BB BE BZ BM BR BN BG CA HR CY CZ DK DM EE FI FR DE GR GD GY HK HU IS IN IL IT JP LV LI LT LU MY MT MX MC MS NL PA PL PT KN LC MF VC SM SG SK SI ZA ES SR SE CH TR GB US UY)
115
115
  self.default_currency = 'EUR'
116
- self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :discover, :jcb, :maestro, :dankort]
116
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :discover, :jcb, :maestro, :dankort]
117
117
 
118
118
  self.homepage_url = 'https://docs.oppwa.com'
119
119
  self.display_name = 'Open Payment Platform'
@@ -125,7 +125,7 @@ module ActiveMerchant #:nodoc:
125
125
 
126
126
  def purchase(money, payment, options={})
127
127
  # debit
128
- execute_dbpa(options[:risk_workflow] ? 'PA.CP': 'DB',
128
+ execute_dbpa(options[:risk_workflow] ? 'PA.CP': 'DB',
129
129
  money, payment, options)
130
130
  end
131
131
 
@@ -133,7 +133,7 @@ module ActiveMerchant #:nodoc:
133
133
  # preauthorization PA
134
134
  execute_dbpa('PA', money, payment, options)
135
135
  end
136
-
136
+
137
137
  def capture(money, authorization, options={})
138
138
  # capture CP
139
139
  execute_referencing('CP', money, authorization, options)
@@ -163,8 +163,6 @@ module ActiveMerchant #:nodoc:
163
163
  def scrub(transcript)
164
164
  transcript.
165
165
  gsub(%r((authentication\.password=)\w+), '\1[FILTERED]').
166
- gsub(%r((authentication\.userId=)\w+), '\1[FILTERED]').
167
- gsub(%r((authentication\.entityId=)\w+), '\1[FILTERED]').
168
166
  gsub(%r((card\.number=)\d+), '\1[FILTERED]').
169
167
  gsub(%r((card\.cvv=)\d+), '\1[FILTERED]')
170
168
  end
@@ -173,12 +171,13 @@ module ActiveMerchant #:nodoc:
173
171
 
174
172
  def execute_dbpa(txtype, money, payment, options)
175
173
  post = {}
176
- post[:paymentType] = txtype
174
+ post[:paymentType] = txtype
177
175
  add_invoice(post, money, options)
178
176
  add_payment_method(post, payment, options)
179
177
  add_address(post, options)
180
- add_customer_data(post, options)
178
+ add_customer_data(post, payment, options)
181
179
  add_options(post, options)
180
+ add_3d_secure(post, options)
182
181
  commit(post, nil, options)
183
182
  end
184
183
 
@@ -189,76 +188,86 @@ module ActiveMerchant #:nodoc:
189
188
  commit(post, authorization, options)
190
189
  end
191
190
 
192
- def add_authentication(post)
193
- post[:authentication] = { entityId: @options[:entity_id], password: @options[:password], userId: @options[:user_id]}
191
+ def add_authentication(post)
192
+ post[:authentication] = { entityId: @options[:entity_id], password: @options[:password], userId: @options[:user_id]}
194
193
  end
195
194
 
196
- def add_customer_data(post, options)
195
+ def add_customer_data(post, payment, options)
197
196
  if options[:customer]
198
197
  post[:customer] = {
199
198
  merchantCustomerId: options[:customer][:merchant_customer_id],
200
- givenName: options[:customer][:givenname],
201
- surname: options[:customer][:surname],
199
+ givenName: options[:customer][:givenname] || payment.first_name,
200
+ surname: options[:customer][:surname] || payment.last_name,
202
201
  birthDate: options[:customer][:birth_date],
203
202
  phone: options[:customer][:phone],
204
203
  mobile: options[:customer][:mobile],
205
- email: options[:customer][:email],
204
+ email: options[:customer][:email] || options[:email],
206
205
  companyName: options[:customer][:company_name],
207
206
  identificationDocType: options[:customer][:identification_doctype],
208
207
  identificationDocId: options[:customer][:identification_docid],
209
- ip: options[:customer][:ip],
208
+ ip: options[:customer][:ip] || options[:ip]
210
209
  }
211
210
  end
212
211
  end
213
212
 
214
213
  def add_address(post, options)
215
- if billing_address = options[:billing_address]
214
+ if billing_address = options[:billing_address] || options[:address]
216
215
  address(post, billing_address, 'billing')
217
216
  end
218
217
  if shipping_address = options[:shipping_address]
219
- address(post, billing_address, 'shipping')
218
+ address(post, shipping_address, 'shipping')
220
219
  if shipping_address[:name]
221
- firstname, lastname = shipping_address[:name].split(' ')
222
- post[:shipping] = { givenName: firstname, surname: lastname }
223
- end
220
+ firstname, lastname = shipping_address[:name].split(' ')
221
+ post[:shipping] = { givenName: firstname, surname: lastname }
222
+ end
224
223
  end
225
224
  end
226
225
 
227
226
  def address(post, address, prefix)
228
- post[prefix] = {
229
- street1: address[:address1],
230
- street2: address[:address2],
231
- city: address[:city],
232
- state: address[:state],
233
- postcode: address[:zip],
234
- country: address[:country],
235
- }
227
+ post[prefix] = {
228
+ street1: address[:address1],
229
+ street2: address[:address2],
230
+ city: address[:city],
231
+ state: address[:state],
232
+ postcode: address[:zip],
233
+ country: address[:country],
234
+ }
236
235
  end
237
236
 
238
237
  def add_invoice(post, money, options)
239
- post[:amount] = amount(money)
240
- post[:currency] = (currency(money) || @options[:currency]) if 'RV'!=(post[:paymentType])
241
- post[:descriptor] = options[:description] || options[:descriptor]
242
- post[:merchantInvoiceId] = options[:merchantInvoiceId] || options[:order_id]
243
- post[:merchantTransactionId] = options[:merchant_transaction_id]
238
+ post[:amount] = amount(money)
239
+ post[:currency] = options[:currency] || currency(money) unless post[:paymentType] == 'RV'
240
+ post[:descriptor] = options[:description] || options[:descriptor]
241
+ post[:merchantInvoiceId] = options[:merchantInvoiceId] || options[:order_id]
242
+ post[:merchantTransactionId] = options[:merchant_transaction_id] || generate_unique_id
244
243
  end
245
244
 
246
245
  def add_payment_method(post, payment, options)
247
- if options[:registrationId]
248
- #post[:recurringType] = 'REPEATED'
249
- post[:card] = {
250
- cvv: payment.verification_value,
251
- }
252
- else
253
- post[:paymentBrand] = payment.brand.upcase
254
- post[:card] = {
255
- holder: payment.name,
256
- number: payment.number,
257
- expiryMonth: "%02d" % payment.month,
258
- expiryYear: payment.year,
259
- cvv: payment.verification_value,
260
- }
261
- end
246
+ if options[:registrationId]
247
+ #post[:recurringType] = 'REPEATED'
248
+ post[:card] = {
249
+ cvv: payment.verification_value,
250
+ }
251
+ else
252
+ post[:paymentBrand] = payment.brand.upcase
253
+ post[:card] = {
254
+ holder: payment.name,
255
+ number: payment.number,
256
+ expiryMonth: "%02d" % payment.month,
257
+ expiryYear: payment.year,
258
+ cvv: payment.verification_value,
259
+ }
260
+ end
261
+ end
262
+
263
+ def add_3d_secure(post, options)
264
+ return unless options[:eci] && options[:cavv] && options[:xid]
265
+
266
+ post[:threeDSecure] = {
267
+ eci: options[:eci],
268
+ verificationId: options[:cavv],
269
+ xid: options[:xid]
270
+ }
262
271
  end
263
272
 
264
273
  def add_options(post, options)
@@ -266,36 +275,38 @@ module ActiveMerchant #:nodoc:
266
275
  post[:testMode] = options[:test_mode] if test? && options[:test_mode]
267
276
  options.each {|key, value| post[key] = value if key.to_s.match('customParameters\[[a-zA-Z0-9\._]{3,64}\]') }
268
277
  post['customParameters[SHOPPER_pluginId]'] = 'activemerchant'
278
+ post['customParameters[custom_disable3DSecure]'] = options[:disable_3d_secure] if options[:disable_3d_secure]
269
279
  end
270
280
 
271
281
  def build_url(url, authorization, options)
272
282
  if options[:registrationId]
273
283
  "#{url.gsub(/payments/, 'registrations')}/#{options[:registrationId]}/payments"
274
- elsif authorization
284
+ elsif authorization
275
285
  "#{url}/#{authorization}"
276
286
  else
277
287
  url
278
288
  end
279
289
  end
280
-
290
+
281
291
  def commit(post, authorization, options)
282
- url = (test? ? test_url : live_url)
292
+ url = build_url(test? ? test_url : live_url, authorization, options)
283
293
  add_authentication(post)
284
294
  post = flatten_hash(post)
285
295
 
286
- url = build_url(url, authorization, options)
287
- raw_response = raw_ssl_request(:post, url,
288
- post.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&"),
289
- "Content-Type" => "application/x-www-form-urlencoded;charset=UTF-8")
290
-
291
- success = success_from(raw_response)
292
- response = raw_response.body
293
- begin
294
- response = JSON.parse(response)
295
- rescue JSON::ParserError
296
- response = json_error(response)
296
+ response = begin
297
+ parse(
298
+ ssl_post(
299
+ url,
300
+ post.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&"),
301
+ "Content-Type" => "application/x-www-form-urlencoded;charset=UTF-8"
302
+ )
303
+ )
304
+ rescue ResponseError => e
305
+ parse(e.response.body)
297
306
  end
298
307
 
308
+ success = success_from(response)
309
+
299
310
  Response.new(
300
311
  success,
301
312
  message_from(response),
@@ -306,11 +317,34 @@ module ActiveMerchant #:nodoc:
306
317
  )
307
318
  end
308
319
 
309
- def success_from(raw_response)
310
- raw_response.code.to_i.between?(200,299)
320
+ def parse(body)
321
+ begin
322
+ JSON.parse(body)
323
+ rescue JSON::ParserError
324
+ json_error(body)
325
+ end
326
+ end
327
+
328
+ def json_error(body)
329
+ message = "Invalid response received #{body.inspect}"
330
+ { 'result' => {'description' => message, 'code' => 'unknown' } }
331
+ end
332
+
333
+ def success_from(response)
334
+ return false unless response['result']
335
+
336
+ success_regex = /^(000\.000\.|000\.100\.1|000\.[36])/
337
+
338
+ if success_regex =~ response['result']['code']
339
+ true
340
+ else
341
+ false
342
+ end
311
343
  end
312
344
 
313
345
  def message_from(response)
346
+ return 'Failed' unless response['result']
347
+
314
348
  response['result']['description']
315
349
  end
316
350
 
@@ -319,44 +353,20 @@ module ActiveMerchant #:nodoc:
319
353
  end
320
354
 
321
355
  def error_code_from(response)
322
- case response['result']['code']
323
- when '100.100.101'
324
- Gateway::STANDARD_ERROR_CODE[:incorrect_number]
325
- when '100.400.317'
326
- Gateway::STANDARD_ERROR_CODE[:invalid_number]
327
- when '100.100.600', '100.100.601', '800.100.153', '800.100.192'
328
- Gateway::STANDARD_ERROR_CODE[:invalid_cvc]
329
- when '100.100.303'
330
- Gateway::STANDARD_ERROR_CODE[:expired_card]
331
- when '100.800.200', '100.800.201', '100.800.202', '800.800.202'
332
- Gateway::STANDARD_ERROR_CODE[:incorrect_zip]
333
- when '100.400.000', '100.400.086', '100.400.305', '800.400.150'
334
- Gateway::STANDARD_ERROR_CODE[:incorrect_address]
335
- when '800.100.159'
336
- Gateway::STANDARD_ERROR_CODE[:pickup_card]
337
- when '800.100.151', '800.100.158', '800.100.160'
338
- Gateway::STANDARD_ERROR_CODE[:card_declined]
339
- else
340
- Gateway::STANDARD_ERROR_CODE[:processing_error]
341
- end
356
+ response['result']['code']
342
357
  end
343
-
344
- def json_error(raw_response)
345
- message = "Invalid response received #{raw_response.inspect}"
346
- { 'result' => {'description' => message, 'code' => 'unknown' } }
347
- end
348
-
358
+
349
359
  def flatten_hash(hash)
350
360
  hash.each_with_object({}) do |(k, v), h|
351
361
  if v.is_a? Hash
352
362
  flatten_hash(v).map do |h_k, h_v|
353
363
  h["#{k}.#{h_k}".to_sym] = h_v
354
364
  end
355
- else
365
+ else
356
366
  h[k] = v
357
367
  end
358
368
  end
359
- end
369
+ end
360
370
  end
361
371
  end
362
372
  end
@@ -65,11 +65,11 @@ module ActiveMerchant #:nodoc:
65
65
 
66
66
  class_attribute :secondary_test_url, :secondary_live_url
67
67
 
68
- self.test_url = "https://orbitalvar1.paymentech.net/authorize"
69
- self.secondary_test_url = "https://orbitalvar2.paymentech.net/authorize"
68
+ self.test_url = "https://orbitalvar1.chasepaymentech.com/authorize"
69
+ self.secondary_test_url = "https://orbitalvar2.chasepaymentech.com/authorize"
70
70
 
71
- self.live_url = "https://orbital1.paymentech.net/authorize"
72
- self.secondary_live_url = "https://orbital2.paymentech.net/authorize"
71
+ self.live_url = "https://orbital1.chasepaymentech.com/authorize"
72
+ self.secondary_live_url = "https://orbital2.chasepaymentech.com/authorize"
73
73
 
74
74
  self.supported_countries = ["US", "CA"]
75
75
  self.default_currency = "CAD"
@@ -345,6 +345,15 @@ module ActiveMerchant #:nodoc:
345
345
  xml.tag! :SDMerchantEmail, soft_desc.merchant_email if soft_desc.merchant_email
346
346
  end
347
347
 
348
+ def add_soft_descriptors_from_hash(xml, soft_desc)
349
+ xml.tag! :SDMerchantName, soft_desc[:merchant_name] || nil
350
+ xml.tag! :SDProductDescription, soft_desc[:product_description] || nil
351
+ xml.tag! :SDMerchantCity, soft_desc[:merchant_city] || nil
352
+ xml.tag! :SDMerchantPhone, soft_desc[:merchant_phone] || nil
353
+ xml.tag! :SDMerchantURL, soft_desc[:merchant_url] || nil
354
+ xml.tag! :SDMerchantEmail, soft_desc[:merchant_email] || nil
355
+ end
356
+
348
357
  def add_address(xml, creditcard, options)
349
358
  if(address = (options[:billing_address] || options[:address]))
350
359
  avs_supported = AVS_SUPPORTED_COUNTRIES.include?(address[:country].to_s) || empty?(address[:country])
@@ -560,6 +569,8 @@ module ActiveMerchant #:nodoc:
560
569
 
561
570
  if parameters[:soft_descriptors].is_a?(OrbitalSoftDescriptors)
562
571
  add_soft_descriptors(xml, parameters[:soft_descriptors])
572
+ elsif parameters[:soft_descriptors].is_a?(Hash)
573
+ add_soft_descriptors_from_hash(xml, parameters[:soft_descriptors])
563
574
  end
564
575
 
565
576
  set_recurring_ind(xml, parameters)