activemerchant 1.66.0 → 1.67.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)