activemerchant 1.60.0 → 1.61.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +64 -0
  3. data/lib/active_merchant/billing/gateways/authorize_net.rb +1 -1
  4. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +12 -1
  5. data/lib/active_merchant/billing/gateways/blue_pay.rb +1 -1
  6. data/lib/active_merchant/billing/gateways/blue_snap.rb +1 -1
  7. data/lib/active_merchant/billing/gateways/braintree_blue.rb +8 -0
  8. data/lib/active_merchant/billing/gateways/card_stream.rb +2 -0
  9. data/lib/active_merchant/billing/gateways/citrus_pay.rb +24 -0
  10. data/lib/active_merchant/billing/gateways/clearhaus.rb +12 -4
  11. data/lib/active_merchant/billing/gateways/conekta.rb +6 -1
  12. data/lib/active_merchant/billing/gateways/credorax.rb +234 -0
  13. data/lib/active_merchant/billing/gateways/cyber_source.rb +39 -52
  14. data/lib/active_merchant/billing/gateways/element.rb +13 -2
  15. data/lib/active_merchant/billing/gateways/fat_zebra.rb +12 -3
  16. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +5 -0
  17. data/lib/active_merchant/billing/gateways/global_collect.rb +3 -1
  18. data/lib/active_merchant/billing/gateways/jetpay.rb +11 -3
  19. data/lib/active_merchant/billing/gateways/linkpoint.rb +2 -0
  20. data/lib/active_merchant/billing/gateways/litle.rb +28 -12
  21. data/lib/active_merchant/billing/gateways/mastercard.rb +261 -0
  22. data/lib/active_merchant/billing/gateways/migs.rb +23 -1
  23. data/lib/active_merchant/billing/gateways/monei.rb +1 -1
  24. data/lib/active_merchant/billing/gateways/moneris.rb +13 -0
  25. data/lib/active_merchant/billing/gateways/netbanx.rb +245 -0
  26. data/lib/active_merchant/billing/gateways/nmi.rb +5 -0
  27. data/lib/active_merchant/billing/gateways/openpay.rb +7 -0
  28. data/lib/active_merchant/billing/gateways/opp.rb +362 -0
  29. data/lib/active_merchant/billing/gateways/orbital.rb +13 -0
  30. data/lib/active_merchant/billing/gateways/pay_junction_v2.rb +190 -0
  31. data/lib/active_merchant/billing/gateways/payflow.rb +6 -0
  32. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -1
  33. data/lib/active_merchant/billing/gateways/paymill.rb +10 -0
  34. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +1 -1
  35. data/lib/active_merchant/billing/gateways/payu_latam.rb +386 -0
  36. data/lib/active_merchant/billing/gateways/pin.rb +1 -3
  37. data/lib/active_merchant/billing/gateways/redsys.rb +2 -0
  38. data/lib/active_merchant/billing/gateways/sage.rb +22 -0
  39. data/lib/active_merchant/billing/gateways/sage_pay.rb +12 -0
  40. data/lib/active_merchant/billing/gateways/securion_pay.rb +2 -2
  41. data/lib/active_merchant/billing/gateways/stripe.rb +29 -8
  42. data/lib/active_merchant/billing/gateways/telr.rb +275 -0
  43. data/lib/active_merchant/billing/gateways/tns.rb +12 -230
  44. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +1 -1
  45. data/lib/active_merchant/billing/gateways/vanco.rb +12 -8
  46. data/lib/active_merchant/billing/gateways/worldpay.rb +18 -0
  47. data/lib/active_merchant/country.rb +6 -2
  48. data/lib/active_merchant/version.rb +1 -1
  49. metadata +11 -3
@@ -101,6 +101,11 @@ module ActiveMerchant #:nodoc:
101
101
  commit("add_customer", post)
102
102
  end
103
103
 
104
+ def verify_credentials
105
+ response = void("0")
106
+ response.message != "Authentication Failed"
107
+ end
108
+
104
109
  def supports_scrubbing?
105
110
  true
106
111
  end
@@ -52,6 +52,13 @@ module ActiveMerchant #:nodoc:
52
52
  commit(:post, "charges/#{CGI.escape(identification)}/refund", post, options)
53
53
  end
54
54
 
55
+ def verify(credit_card, options = {})
56
+ MultiResponse.run(:use_first_response) do |r|
57
+ r.process { authorize(100, credit_card, options) }
58
+ r.process(:ignore_result) { void(r.authorization, options) }
59
+ end
60
+ end
61
+
55
62
  def store(creditcard, options = {})
56
63
  card_params = {}
57
64
  add_creditcard(card_params, creditcard, options)
@@ -0,0 +1,362 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class OppGateway < Gateway
4
+ # = Open Payment Platform
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.
10
+ #
11
+ # For any questions or comments please contact support@payon.com
12
+ #
13
+ # == Usage
14
+ #
15
+ # gateway = ActiveMerchant::Billing::OppGateway.new(
16
+ # user_id: 'merchant user id',
17
+ # password: 'password',
18
+ # entity_id: 'entity id',
19
+ # )
20
+ #
21
+ # # set up credit card object as in main ActiveMerchant example
22
+ # creditcard = ActiveMerchant::Billing::CreditCard.new(
23
+ # :type => 'visa',
24
+ # :number => '4242424242424242',
25
+ # :month => 8,
26
+ # :year => 2009,
27
+ # :first_name => 'Bob',
28
+ # :last_name => 'Bobsen'
29
+ # :verification_value: '123')
30
+ #
31
+ # # Request: complete example, including address, billing address, shipping address
32
+ # complete_request_options = {
33
+ # order_id: "your merchant/shop order id", # alternative is to set merchantInvoiceId
34
+ # merchant_transaction_id: "your merchant/shop transaction id",
35
+ # address: address,
36
+ # description: 'Store Purchase - Books',
37
+ # risk_workflow: false,
38
+ # test_mode: 'EXTERNAL' # or 'INTERNAL', valid only for test system
39
+ # create_registration: false, # payment details will be stored on the server an latter can be referenced
40
+ #
41
+ # billing_address: {
42
+ # address1: '123 Test Street',
43
+ # city: 'Test',
44
+ # state: 'TE',
45
+ # zip: 'AB12CD',
46
+ # country: 'GB',
47
+ # },
48
+ # shipping_address: {
49
+ # name: 'Muton DeMicelis',
50
+ # address1: 'My Street On Upiter, Apt 3.14/2.78',
51
+ # city: 'Munich',
52
+ # state: 'Bov',
53
+ # zip: '81675',
54
+ # country: 'DE',
55
+ # },
56
+ # customer: {
57
+ # merchant_customer_id: "your merchant/customer id",
58
+ # givenname: 'Jane',
59
+ # surname: 'Jones',
60
+ # birth_date: '1965-05-01',
61
+ # phone: '(?!?)555-5555',
62
+ # mobile: '(?!?)234-23423',
63
+ # email: 'jane@jones.com',
64
+ # company_name: 'JJ Ltd.',
65
+ # identification_doctype: 'PASSPORT',
66
+ # identification_docid: 'FakeID2342431234123',
67
+ # ip: 101.102.103.104,
68
+ # },
69
+ # }
70
+ #
71
+ # # Request: minimal example
72
+ # minimal_request_options = {
73
+ # order_id: "your merchant/shop order id", # alternative is to set merchantInvoiceId
74
+ # description: 'Store Purchase - Books',
75
+ # }
76
+ #
77
+ # options =
78
+ # # run request
79
+ # response = gateway.purchase(754, creditcard, options) # charge 7,54 EUR
80
+ #
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
83
+ # response.message # Retrieve the message returned by opp
84
+ # response.authorization # Retrieve the unique transaction ID returned by opp
85
+ # response.params['result']['code'] # Retrieve original return code returned by opp server
86
+ #
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,
92
+ # you may check the original result code from OPP that can be found in response.params['result']['code']
93
+ #
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.
98
+ #
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
+ # options[:test_mode]='EXTERNAL' causes test transactions to be forwarded to the processor's test system for 'end-to-end' testing
101
+ # options[:test_mode]='INTERNAL' causes transactions to be sent to opp simulators, which is useful when switching to the live endpoint for connectivity testing.
102
+ # If no test_mode parameter is sent, test_mode=INTERNAL is the default behaviour.
103
+ #
104
+ # Billing Address, Shipping Address, Custom Parameters are supported as described under https://docs.oppwa.com/parameters
105
+ # See complete example above for details.
106
+ #
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,
109
+ # which can subsequently be used to reference the stored payment.
110
+
111
+ self.test_url = 'https://test.oppwa.com/v1/payments'
112
+ self.live_url = 'https://oppwa.com/v1/payments'
113
+
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
+ self.default_currency = 'EUR'
116
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :discover, :jcb, :maestro, :dankort]
117
+
118
+ self.homepage_url = 'https://docs.oppwa.com'
119
+ self.display_name = 'Open Payment Platform'
120
+
121
+ def initialize(options={})
122
+ requires!(options, :user_id, :password, :entity_id)
123
+ super
124
+ end
125
+
126
+ def purchase(money, payment, options={})
127
+ # debit
128
+ execute_dbpa(options[:risk_workflow] ? 'PA.CP': 'DB',
129
+ money, payment, options)
130
+ end
131
+
132
+ def authorize(money, payment, options={})
133
+ # preauthorization PA
134
+ execute_dbpa('PA', money, payment, options)
135
+ end
136
+
137
+ def capture(money, authorization, options={})
138
+ # capture CP
139
+ execute_referencing('CP', money, authorization, options)
140
+ end
141
+
142
+ def refund(money, authorization, options={})
143
+ # refund RF
144
+ execute_referencing('RF', money, authorization, options)
145
+ end
146
+
147
+ def void(authorization, options={})
148
+ # reversal RV
149
+ execute_referencing('RV', nil, authorization, options)
150
+ end
151
+
152
+ def verify(credit_card, options={})
153
+ MultiResponse.run(:use_first_response) do |r|
154
+ r.process { authorize(100, credit_card, options) }
155
+ r.process(:ignore_result) { void(r.authorization, options) }
156
+ end
157
+ end
158
+
159
+ def supports_scrubbing?
160
+ true
161
+ end
162
+
163
+ def scrub(transcript)
164
+ transcript.
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
+ gsub(%r((card\.number=)\d+), '\1[FILTERED]').
169
+ gsub(%r((card\.cvv=)\d+), '\1[FILTERED]')
170
+ end
171
+
172
+ private
173
+
174
+ def execute_dbpa(txtype, money, payment, options)
175
+ post = {}
176
+ post[:paymentType] = txtype
177
+ add_invoice(post, money, options)
178
+ add_payment_method(post, payment, options)
179
+ add_address(post, options)
180
+ add_customer_data(post, options)
181
+ add_options(post, options)
182
+ commit(post, nil, options)
183
+ end
184
+
185
+ def execute_referencing(txtype, money, authorization, options)
186
+ post = {}
187
+ post[:paymentType] = txtype
188
+ add_invoice(post, money, options)
189
+ commit(post, authorization, options)
190
+ end
191
+
192
+ def add_authentication(post)
193
+ post[:authentication] = { entityId: @options[:entity_id], password: @options[:password], userId: @options[:user_id]}
194
+ end
195
+
196
+ def add_customer_data(post, options)
197
+ if options[:customer]
198
+ post[:customer] = {
199
+ merchantCustomerId: options[:customer][:merchant_customer_id],
200
+ givenName: options[:customer][:givenname],
201
+ surname: options[:customer][:surname],
202
+ birthDate: options[:customer][:birth_date],
203
+ phone: options[:customer][:phone],
204
+ mobile: options[:customer][:mobile],
205
+ email: options[:customer][:email],
206
+ companyName: options[:customer][:company_name],
207
+ identificationDocType: options[:customer][:identification_doctype],
208
+ identificationDocId: options[:customer][:identification_docid],
209
+ ip: options[:customer][:ip],
210
+ }
211
+ end
212
+ end
213
+
214
+ def add_address(post, options)
215
+ if billing_address = options[:billing_address]
216
+ address(post, billing_address, 'billing')
217
+ end
218
+ if shipping_address = options[:shipping_address]
219
+ address(post, billing_address, 'shipping')
220
+ if shipping_address[:name]
221
+ firstname, lastname = shipping_address[:name].split(' ')
222
+ post[:shipping] = { givenName: firstname, surname: lastname }
223
+ end
224
+ end
225
+ end
226
+
227
+ 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
+ }
236
+ end
237
+
238
+ 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]
244
+ end
245
+
246
+ 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
262
+ end
263
+
264
+ def add_options(post, options)
265
+ post[:createRegistration] = options[:create_registration] if options[:create_registration] && !options[:registrationId]
266
+ post[:testMode] = options[:test_mode] if test? && options[:test_mode]
267
+ options.each {|key, value| post[key] = value if key.to_s.match('customParameters\[[a-zA-Z0-9\._]{3,64}\]') }
268
+ post['customParameters[SHOPPER_pluginId]'] = 'activemerchant'
269
+ end
270
+
271
+ def build_url(url, authorization, options)
272
+ if options[:registrationId]
273
+ "#{url.gsub(/payments/, 'registrations')}/#{options[:registrationId]}/payments"
274
+ elsif authorization
275
+ "#{url}/#{authorization}"
276
+ else
277
+ url
278
+ end
279
+ end
280
+
281
+ def commit(post, authorization, options)
282
+ url = (test? ? test_url : live_url)
283
+ add_authentication(post)
284
+ post = flatten_hash(post)
285
+
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)
297
+ end
298
+
299
+ Response.new(
300
+ success,
301
+ message_from(response),
302
+ response,
303
+ authorization: authorization_from(response),
304
+ test: test?,
305
+ error_code: success ? nil : error_code_from(response),
306
+ )
307
+ end
308
+
309
+ def success_from(raw_response)
310
+ raw_response.code.to_i.between?(200,299)
311
+ end
312
+
313
+ def message_from(response)
314
+ response['result']['description']
315
+ end
316
+
317
+ def authorization_from(response)
318
+ response['id']
319
+ end
320
+
321
+ 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
342
+ 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
+
349
+ def flatten_hash(hash)
350
+ hash.each_with_object({}) do |(k, v), h|
351
+ if v.is_a? Hash
352
+ flatten_hash(v).map do |h_k, h_v|
353
+ h["#{k}.#{h_k}".to_sym] = h_v
354
+ end
355
+ else
356
+ h[k] = v
357
+ end
358
+ end
359
+ end
360
+ end
361
+ end
362
+ end
@@ -295,6 +295,19 @@ module ActiveMerchant #:nodoc:
295
295
  commit(order, :delete_customer_profile)
296
296
  end
297
297
 
298
+ def supports_scrubbing?
299
+ true
300
+ end
301
+
302
+ def scrub(transcript)
303
+ transcript.
304
+ gsub(%r((<OrbitalConnectionUsername>).+(</OrbitalConnectionUsername>)), '\1[FILTERED]\2').
305
+ gsub(%r((<OrbitalConnectionPassword>).+(</OrbitalConnectionPassword>)), '\1[FILTERED]\2').
306
+ gsub(%r((<AccountNum>).+(</AccountNum>)), '\1[FILTERED]\2').
307
+ gsub(%r((<CardSecVal>).+(</CardSecVal>)), '\1[FILTERED]\2').
308
+ gsub(%r((<MerchantID>).+(</MerchantID>)), '\1[FILTERED]\2')
309
+ end
310
+
298
311
  private
299
312
 
300
313
  def authorization_string(*args)
@@ -0,0 +1,190 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class PayJunctionV2Gateway < Gateway
4
+ self.display_name = "PayJunction"
5
+ self.homepage_url = "https://www.payjunction.com/"
6
+
7
+ self.test_url = "https://api.payjunctionlabs.com/transactions"
8
+ self.live_url = "https://api.payjunction.com/transactions"
9
+
10
+ self.supported_countries = ["US"]
11
+ self.default_currency = "USD"
12
+ self.money_format = :dollars
13
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
14
+
15
+ def initialize(options={})
16
+ requires!(options, :api_login, :api_password, :api_key)
17
+ super
18
+ end
19
+
20
+ def purchase(amount, payment_method, options={})
21
+ post = {}
22
+ add_invoice(post, amount, options)
23
+ add_payment_method(post, payment_method)
24
+
25
+ commit("purchase", post)
26
+ end
27
+
28
+ def authorize(amount, payment_method, options={})
29
+ post = {}
30
+ post[:status] = "HOLD"
31
+ add_invoice(post, amount, options)
32
+ add_payment_method(post, payment_method)
33
+
34
+ commit("authorize", post)
35
+ end
36
+
37
+ def capture(amount, authorization, options={})
38
+ post = {}
39
+ post[:status] = "CAPTURE"
40
+ post[:transactionId] = authorization
41
+ add_invoice(post, amount, options)
42
+
43
+ commit("capture", post)
44
+ end
45
+
46
+ def void(authorization, options={})
47
+ post = {}
48
+ post[:status] = "VOID"
49
+ post[:transactionId] = authorization
50
+
51
+ commit("void", post)
52
+ end
53
+
54
+ def refund(amount, authorization, options={})
55
+ post = {}
56
+ post[:action] = "REFUND"
57
+ post[:transactionId] = authorization
58
+ add_invoice(post, amount, options)
59
+
60
+ commit("refund", post)
61
+ end
62
+
63
+ def credit(amount, payment_method, options={})
64
+ post = {}
65
+ post[:action] = "REFUND"
66
+ add_invoice(post, amount, options)
67
+ add_payment_method(post, payment_method)
68
+
69
+ commit("credit", post)
70
+ end
71
+
72
+ def verify(credit_card, options={})
73
+ MultiResponse.run(:use_first_response) do |r|
74
+ r.process { authorize(100, credit_card, options) }
75
+ r.process(:ignore_result) { void(r.authorization, options) }
76
+ end
77
+ end
78
+
79
+ def store(payment_method, options = {})
80
+ verify(payment_method, options)
81
+ end
82
+
83
+ def supports_scrubbing?
84
+ true
85
+ end
86
+
87
+ def scrub(transcript)
88
+ transcript.
89
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
90
+ gsub(%r((X-Pj-Application-Key: )[\w-]+), '\1[FILTERED]').
91
+ gsub(%r((cardNumber=)\d+), '\1[FILTERED]').
92
+ gsub(%r((cardCvv=)\d+), '\1[FILTERED]')
93
+ end
94
+
95
+ private
96
+
97
+ def add_invoice(post, money, options)
98
+ post[:amountBase] = amount(money) if money
99
+ post[:invoiceNumber] = options[:order_id] if options[:order_id]
100
+ end
101
+
102
+ def add_payment_method(post, payment_method)
103
+ if payment_method.is_a? Integer
104
+ post[:transactionId] = payment_method
105
+ else
106
+ post[:cardNumber] = payment_method.number
107
+ post[:cardExpMonth] = format(payment_method.month, :two_digits)
108
+ post[:cardExpYear] = format(payment_method.year, :four_digits)
109
+ post[:cardCvv] = payment_method.verification_value
110
+ end
111
+ end
112
+
113
+ def commit(action, params)
114
+ response = begin
115
+ parse(ssl_invoke(action, params))
116
+ rescue ResponseError => e
117
+ parse(e.response.body)
118
+ end
119
+
120
+ success = success_from(response)
121
+ Response.new(
122
+ success,
123
+ message_from(response),
124
+ response,
125
+ authorization: success ? authorization_from(response) : nil,
126
+ error_code: success ? nil : error_from(response),
127
+ test: test?
128
+ )
129
+ end
130
+
131
+ def ssl_invoke(action, params)
132
+ if ["purchase", "authorize", "refund", "credit"].include?(action)
133
+ ssl_post(url(), post_data(params), headers)
134
+ else
135
+ ssl_request(:put, url(params), post_data(params), headers)
136
+ end
137
+ end
138
+
139
+ def headers
140
+ {
141
+ "Authorization" => "Basic " + Base64.encode64("#{@options[:api_login]}:#{@options[:api_password]}").strip,
142
+ "Content-Type" => "application/x-www-form-urlencoded;charset=UTF-8",
143
+ "Accept" => "application/json",
144
+ "X-PJ-Application-Key" => "#{@options[:api_key]}"
145
+ }
146
+ end
147
+
148
+ def post_data(params)
149
+ params.map {|k, v| "#{k}=#{CGI.escape(v.to_s)}"}.join('&')
150
+ end
151
+
152
+ def url(params={})
153
+ test? ? "#{test_url}/#{params[:transactionId]}" : "#{live_url}/#{params[:transactionId]}"
154
+ end
155
+
156
+ def parse(body)
157
+ begin
158
+ JSON.parse(body)
159
+ rescue JSON::ParserError
160
+ message = "Invalid JSON response received from PayJunctionV2Gateway. Please contact PayJunctionV2Gateway if you continue to receive this message."
161
+ message += " (The raw response returned by the API was #{body.inspect})"
162
+ {
163
+ "errors" => [{
164
+ "message" => message
165
+ }]
166
+ }
167
+ end
168
+ end
169
+
170
+ def success_from(response)
171
+ return response["response"]["approved"] if response["response"]
172
+ false
173
+ end
174
+
175
+ def message_from(response)
176
+ return response["response"]["message"] if response["response"]
177
+
178
+ response["errors"].inject(""){ |message,error| error["message"] + "|" + message } if response["errors"]
179
+ end
180
+
181
+ def authorization_from(response)
182
+ response["transactionId"]
183
+ end
184
+
185
+ def error_from(response)
186
+ response["response"]["code"] if response["response"]
187
+ end
188
+ end
189
+ end
190
+ end