activemerchant 1.45.0 → 1.46.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 (63) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +3 -1
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG +25 -0
  5. data/CONTRIBUTORS +9 -0
  6. data/README.md +6 -20
  7. data/lib/active_merchant.rb +9 -50
  8. data/lib/active_merchant/billing.rb +1 -0
  9. data/lib/active_merchant/billing/gateway.rb +10 -1
  10. data/lib/active_merchant/billing/gateways.rb +6 -11
  11. data/lib/active_merchant/billing/gateways/authorize_net.rb +58 -47
  12. data/lib/active_merchant/billing/gateways/beanstream.rb +1 -1
  13. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +8 -8
  14. data/lib/active_merchant/billing/gateways/braintree.rb +2 -2
  15. data/lib/active_merchant/billing/gateways/braintree_blue.rb +69 -22
  16. data/lib/active_merchant/billing/gateways/braintree_orange.rb +2 -2
  17. data/lib/active_merchant/billing/gateways/checkout.rb +7 -2
  18. data/lib/active_merchant/billing/gateways/cyber_source.rb +25 -9
  19. data/lib/active_merchant/billing/gateways/elavon.rb +1 -1
  20. data/lib/active_merchant/billing/gateways/eway_rapid.rb +6 -3
  21. data/lib/active_merchant/billing/gateways/finansbank.rb +1 -1
  22. data/lib/active_merchant/billing/gateways/hps.rb +0 -1
  23. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +1 -1
  24. data/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem +0 -0
  25. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +1 -1
  26. data/lib/active_merchant/billing/gateways/instapay.rb +0 -0
  27. data/lib/active_merchant/billing/gateways/ipp.rb +175 -0
  28. data/lib/active_merchant/billing/gateways/mercury.rb +4 -11
  29. data/lib/active_merchant/billing/gateways/migs.rb +1 -1
  30. data/lib/active_merchant/billing/gateways/modern_payments.rb +1 -1
  31. data/lib/active_merchant/billing/gateways/orbital.rb +2 -2
  32. data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +26 -0
  33. data/lib/active_merchant/billing/gateways/payflow.rb +3 -3
  34. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +16 -5
  35. data/lib/active_merchant/billing/gateways/payflow_express.rb +3 -3
  36. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +2 -2
  37. data/lib/active_merchant/billing/gateways/payflow_uk.rb +4 -4
  38. data/lib/active_merchant/billing/gateways/paypal.rb +15 -3
  39. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +10 -1
  40. data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +1 -1
  41. data/lib/active_merchant/billing/gateways/paypal_ca.rb +1 -1
  42. data/lib/active_merchant/billing/gateways/paypal_digital_goods.rb +3 -3
  43. data/lib/active_merchant/billing/gateways/paypal_express.rb +4 -4
  44. data/lib/active_merchant/billing/gateways/pin.rb +10 -1
  45. data/lib/active_merchant/billing/gateways/quickbooks.rb +278 -0
  46. data/lib/active_merchant/billing/gateways/redsys.rb +2 -2
  47. data/lib/active_merchant/billing/gateways/sage.rb +3 -3
  48. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +1 -1
  49. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +1 -1
  50. data/lib/active_merchant/billing/gateways/secure_pay.rb +1 -1
  51. data/lib/active_merchant/billing/gateways/skip_jack.rb +0 -1
  52. data/lib/active_merchant/billing/gateways/stripe.rb +13 -2
  53. data/lib/active_merchant/billing/gateways/wirecard.rb +0 -1
  54. data/lib/active_merchant/billing/payment_token.rb +1 -1
  55. data/lib/active_merchant/connection.rb +169 -0
  56. data/lib/active_merchant/network_connection_retries.rb +78 -0
  57. data/lib/active_merchant/post_data.rb +24 -0
  58. data/lib/active_merchant/posts_data.rb +78 -0
  59. data/lib/active_merchant/version.rb +1 -1
  60. data/lib/certs/cacert.pem +3866 -0
  61. metadata +22 -44
  62. metadata.gz.sig +0 -0
  63. data/lib/active_merchant/offsite_payments_shim.rb +0 -19
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/beanstream/beanstream_core'
1
+ require 'active_merchant/billing/gateways/beanstream/beanstream_core'
2
2
 
3
3
  module ActiveMerchant #:nodoc:
4
4
  module Billing #:nodoc:
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/beanstream/beanstream_core'
1
+ require 'active_merchant/billing/gateways/beanstream/beanstream_core'
2
2
 
3
3
  module ActiveMerchant #:nodoc:
4
4
  module Billing #:nodoc:
@@ -7,21 +7,21 @@ module ActiveMerchant #:nodoc:
7
7
  params['pageContents']
8
8
  end
9
9
  end
10
-
10
+
11
11
  class BeanstreamInteracGateway < Gateway
12
12
  include BeanstreamCore
13
13
 
14
14
  # Confirm a transaction posted back from the bank to Beanstream.
15
15
  # Confirming a transaction does not require any credentials,
16
16
  # and in an application with many merchants sharing a funded
17
- # URL the application may not yet know which merchant the
17
+ # URL the application may not yet know which merchant the
18
18
  # post back is for until the response of the confirmation is
19
19
  # received, which contains the order number.
20
20
  def self.confirm(transaction)
21
21
  gateway = new(:login => '')
22
22
  gateway.confirm(transaction)
23
23
  end
24
-
24
+
25
25
  def purchase(money, options = {})
26
26
  post = {}
27
27
  add_amount(post, money)
@@ -31,20 +31,20 @@ module ActiveMerchant #:nodoc:
31
31
  add_transaction_type(post, :purchase)
32
32
  commit(post)
33
33
  end
34
-
34
+
35
35
  # Confirm a transaction posted back from the bank to Beanstream.
36
36
  def confirm(transaction)
37
37
  post(transaction)
38
38
  end
39
-
39
+
40
40
  private
41
-
41
+
42
42
  def add_interac_details(post, options)
43
43
  address = options[:billing_address] || options[:address] || {}
44
44
  post[:trnCardOwner] = address[:name]
45
45
  post[:paymentMethod] = 'IO'
46
46
  end
47
-
47
+
48
48
  def build_response(*args)
49
49
  BeanstreamInteracResponse.new(*args)
50
50
  end
@@ -1,10 +1,10 @@
1
- require File.dirname(__FILE__) + '/braintree/braintree_common'
1
+ require 'active_merchant/billing/gateways/braintree/braintree_common'
2
2
 
3
3
  module ActiveMerchant #:nodoc:
4
4
  module Billing #:nodoc:
5
5
  class BraintreeGateway < Gateway
6
6
  include BraintreeCommon
7
-
7
+
8
8
  self.abstract_class = true
9
9
 
10
10
  def self.new(options={})
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/braintree/braintree_common'
1
+ require 'active_merchant/billing/gateways/braintree/braintree_common'
2
2
 
3
3
  begin
4
4
  require "braintree"
@@ -310,17 +310,15 @@ module ActiveMerchant #:nodoc:
310
310
 
311
311
  def response_from_result(result)
312
312
  Response.new(result.success?, message_from_result(result),
313
- { braintree_transaction: (transaction_hash(result.transaction) if result.success?) },
313
+ { braintree_transaction: transaction_hash(result) },
314
314
  { authorization: (result.transaction.id if result.success?) }
315
315
  )
316
316
  end
317
317
 
318
318
  def response_params(result)
319
319
  params = {}
320
- if result.success?
321
- params[:braintree_transaction] = transaction_hash(result.transaction)
322
- params[:customer_vault_id] = result.transaction.customer_details.id
323
- end
320
+ params[:customer_vault_id] = result.transaction.customer_details.id if result.success?
321
+ params[:braintree_transaction] = transaction_hash(result)
324
322
  params
325
323
  end
326
324
 
@@ -330,16 +328,50 @@ module ActiveMerchant #:nodoc:
330
328
  options[:authorization] = result.transaction.id
331
329
  end
332
330
  if result.transaction
333
- options[:avs_result] = {
334
- :code => nil, :message => nil,
335
- :street_match => result.transaction.avs_street_address_response_code,
336
- :postal_match => result.transaction.avs_postal_code_response_code
337
- }
331
+ options[:avs_result] = { code: avs_code_from(result.transaction) }
338
332
  options[:cvv_result] = result.transaction.cvv_response_code
339
333
  end
340
334
  options
341
335
  end
342
336
 
337
+ def avs_code_from(transaction)
338
+ avs_mapping["street: #{transaction.avs_street_address_response_code}, zip: #{transaction.avs_postal_code_response_code}"]
339
+ end
340
+
341
+ def avs_mapping
342
+ {
343
+ "street: M, zip: M" => "M",
344
+ "street: M, zip: N" => "A",
345
+ "street: M, zip: U" => "B",
346
+ "street: M, zip: I" => "B",
347
+ "street: M, zip: A" => "B",
348
+
349
+ "street: N, zip: M" => "Z",
350
+ "street: N, zip: N" => "C",
351
+ "street: N, zip: U" => "C",
352
+ "street: N, zip: I" => "C",
353
+ "street: N, zip: A" => "C",
354
+
355
+ "street: U, zip: M" => "P",
356
+ "street: U, zip: N" => "N",
357
+ "street: U, zip: U" => "I",
358
+ "street: U, zip: I" => "I",
359
+ "street: U, zip: A" => "I",
360
+
361
+ "street: I, zip: M" => "P",
362
+ "street: I, zip: N" => "C",
363
+ "street: I, zip: U" => "I",
364
+ "street: I, zip: I" => "I",
365
+ "street: I, zip: A" => "I",
366
+
367
+ "street: A, zip: M" => "P",
368
+ "street: A, zip: N" => "C",
369
+ "street: A, zip: U" => "I",
370
+ "street: A, zip: I" => "I",
371
+ "street: A, zip: A" => "I"
372
+ }
373
+ end
374
+
343
375
  def message_from_transaction_result(result)
344
376
  if result.transaction && result.transaction.status == "gateway_rejected"
345
377
  "Transaction declined - gateway rejected"
@@ -350,6 +382,16 @@ module ActiveMerchant #:nodoc:
350
382
  end
351
383
  end
352
384
 
385
+ def response_code_from_result(result)
386
+ if result.transaction
387
+ result.transaction.processor_response_code
388
+ elsif result.errors.size == 0 && result.credit_card_verification
389
+ result.credit_card_verification.processor_response_code
390
+ elsif result.errors.size > 0
391
+ result.errors.first.code
392
+ end
393
+ end
394
+
353
395
  def create_transaction(transaction_type, money, credit_card_or_vault_id, options)
354
396
  transaction_params = create_transaction_parameters(money, credit_card_or_vault_id, options)
355
397
  commit do
@@ -389,8 +431,7 @@ module ActiveMerchant #:nodoc:
389
431
  "token" => cc.token,
390
432
  "last_4" => cc.last_4,
391
433
  "card_type" => cc.card_type,
392
- "masked_number" => cc.masked_number,
393
- "token" => cc.token
434
+ "masked_number" => cc.masked_number
394
435
  }
395
436
  end
396
437
  end
@@ -398,7 +439,12 @@ module ActiveMerchant #:nodoc:
398
439
  hash
399
440
  end
400
441
 
401
- def transaction_hash(transaction)
442
+ def transaction_hash(result)
443
+ unless result.success?
444
+ return { "processor_response_code" => response_code_from_result(result) }
445
+ end
446
+
447
+ transaction = result.transaction
402
448
  if transaction.vault_customer
403
449
  vault_customer = {
404
450
  }
@@ -444,14 +490,15 @@ module ActiveMerchant #:nodoc:
444
490
  }
445
491
 
446
492
  {
447
- "order_id" => transaction.order_id,
448
- "status" => transaction.status,
449
- "credit_card_details" => credit_card_details,
450
- "customer_details" => customer_details,
451
- "billing_details" => billing_details,
452
- "shipping_details" => shipping_details,
453
- "vault_customer" => vault_customer,
454
- "merchant_account_id" => transaction.merchant_account_id
493
+ "order_id" => transaction.order_id,
494
+ "status" => transaction.status,
495
+ "credit_card_details" => credit_card_details,
496
+ "customer_details" => customer_details,
497
+ "billing_details" => billing_details,
498
+ "shipping_details" => shipping_details,
499
+ "vault_customer" => vault_customer,
500
+ "merchant_account_id" => transaction.merchant_account_id,
501
+ "processor_response_code" => response_code_from_result(result)
455
502
  }
456
503
  end
457
504
 
@@ -1,5 +1,5 @@
1
- require File.dirname(__FILE__) + '/smart_ps.rb'
2
- require File.dirname(__FILE__) + '/braintree/braintree_common'
1
+ require 'active_merchant/billing/gateways/smart_ps.rb'
2
+ require 'active_merchant/billing/gateways/braintree/braintree_common'
3
3
 
4
4
  module ActiveMerchant #:nodoc:
5
5
  module Billing #:nodoc:
@@ -38,6 +38,7 @@ module ActiveMerchant #:nodoc:
38
38
  commit('purchase', amount, options) do |xml|
39
39
  add_credentials(xml, options)
40
40
  add_invoice(xml, amount, options)
41
+ add_track_id(xml, options[:order_id])
41
42
  add_payment_method(xml, payment_method)
42
43
  add_billing_info(xml, options)
43
44
  add_shipping_info(xml, options)
@@ -52,6 +53,7 @@ module ActiveMerchant #:nodoc:
52
53
  commit('authorize', amount, options) do |xml|
53
54
  add_credentials(xml, options)
54
55
  add_invoice(xml, amount, options)
56
+ add_track_id(xml, options[:order_id])
55
57
  add_payment_method(xml, payment_method)
56
58
  add_billing_info(xml, options)
57
59
  add_shipping_info(xml, options)
@@ -104,7 +106,6 @@ module ActiveMerchant #:nodoc:
104
106
  def add_invoice(xml, amount, options)
105
107
  xml.bill_amount_ amount(amount)
106
108
  xml.bill_currencycode_ options[:currency] || currency(amount)
107
- xml.trackid_ options[:order_id] if options[:order_id]
108
109
  end
109
110
 
110
111
  def add_payment_method(xml, payment_method)
@@ -157,7 +158,11 @@ module ActiveMerchant #:nodoc:
157
158
  def add_reference(xml, authorization)
158
159
  transid, trackid, _, _, _ = split_authorization(authorization)
159
160
  xml.transid transid
160
- xml.trackid trackid if trackid
161
+ add_track_id(xml, trackid)
162
+ end
163
+
164
+ def add_track_id(xml, trackid)
165
+ xml.trackid(trackid) if trackid
161
166
  end
162
167
 
163
168
  def commit(action, amount=nil, options={}, &builder)
@@ -150,6 +150,13 @@ module ActiveMerchant #:nodoc:
150
150
  commit(build_refund_request(money, identification, options), options)
151
151
  end
152
152
 
153
+ def verify(payment, options = {})
154
+ MultiResponse.run(:use_first_response) do |r|
155
+ r.process { authorize(100, payment, options) }
156
+ r.process(:ignore_result) { void(r.authorization, options) }
157
+ end
158
+ end
159
+
153
160
  # Adds credit to a subscription (stand alone credit).
154
161
  def credit(money, reference, options = {})
155
162
  requires!(options, :order_id)
@@ -235,7 +242,7 @@ module ActiveMerchant #:nodoc:
235
242
  xml = Builder::XmlMarkup.new :indent => 2
236
243
  add_payment_method_or_subscription(xml, money, creditcard_or_reference, options)
237
244
  add_auth_service(xml)
238
- add_business_rules_data(xml)
245
+ add_business_rules_data(xml, options)
239
246
  xml.target!
240
247
  end
241
248
 
@@ -246,7 +253,7 @@ module ActiveMerchant #:nodoc:
246
253
  add_line_item_data(xml, options)
247
254
  add_purchase_data(xml, 0, false, options)
248
255
  add_tax_service(xml)
249
- add_business_rules_data(xml)
256
+ add_business_rules_data(xml, options)
250
257
  xml.target!
251
258
  end
252
259
 
@@ -257,7 +264,7 @@ module ActiveMerchant #:nodoc:
257
264
  xml = Builder::XmlMarkup.new :indent => 2
258
265
  add_purchase_data(xml, money, true, options)
259
266
  add_capture_service(xml, request_id, request_token)
260
- add_business_rules_data(xml)
267
+ add_business_rules_data(xml, options)
261
268
  xml.target!
262
269
  end
263
270
 
@@ -268,7 +275,7 @@ module ActiveMerchant #:nodoc:
268
275
  add_check_service(xml)
269
276
  else
270
277
  add_purchase_service(xml, options)
271
- add_business_rules_data(xml) unless options[:pinless_debit_card]
278
+ add_business_rules_data(xml, options) unless options[:pinless_debit_card]
272
279
  end
273
280
  xml.target!
274
281
  end
@@ -337,7 +344,7 @@ module ActiveMerchant #:nodoc:
337
344
  end
338
345
  end
339
346
  add_subscription_create_service(xml, options)
340
- add_business_rules_data(xml)
347
+ add_business_rules_data(xml, options)
341
348
  xml.target!
342
349
  end
343
350
 
@@ -349,7 +356,7 @@ module ActiveMerchant #:nodoc:
349
356
  add_creditcard_payment_method(xml) if creditcard
350
357
  add_subscription(xml, options, reference)
351
358
  add_subscription_update_service(xml, options)
352
- add_business_rules_data(xml)
359
+ add_business_rules_data(xml, options)
353
360
  xml.target!
354
361
  end
355
362
 
@@ -374,11 +381,20 @@ module ActiveMerchant #:nodoc:
374
381
  xml.target!
375
382
  end
376
383
 
377
- def add_business_rules_data(xml)
384
+ def add_business_rules_data(xml, options)
385
+ prioritized_options = [options, @options]
386
+
378
387
  xml.tag! 'businessRules' do
379
- xml.tag!('ignoreAVSResult', 'true') if @options[:ignore_avs]
380
- xml.tag!('ignoreCVResult', 'true') if @options[:ignore_cvv]
388
+ xml.tag!('ignoreAVSResult', 'true') if extract_option(prioritized_options, :ignore_avs)
389
+ xml.tag!('ignoreCVResult', 'true') if extract_option(prioritized_options, :ignore_cvv)
390
+ end
391
+ end
392
+
393
+ def extract_option prioritized_options, option_name
394
+ options_matching_key = prioritized_options.detect do |options|
395
+ options.has_key? option_name
381
396
  end
397
+ options_matching_key[option_name] if options_matching_key
382
398
  end
383
399
 
384
400
  def add_line_item_data(xml, options)
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/viaklix'
1
+ require 'active_merchant/billing/gateways/viaklix'
2
2
 
3
3
  module ActiveMerchant #:nodoc:
4
4
  module Billing #:nodoc:
@@ -7,8 +7,8 @@ module ActiveMerchant #:nodoc:
7
7
  self.live_url = "https://api.ewaypayments.com/"
8
8
 
9
9
  self.money_format = :cents
10
- self.supported_countries = ['AU', 'NZ', 'GB']
11
- self.supported_cardtypes = [:visa, :master, :american_express, :diners_club]
10
+ self.supported_countries = ['AU', 'NZ', 'GB', 'SG']
11
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb]
12
12
  self.homepage_url = "http://www.eway.com.au/"
13
13
  self.display_name = "eWAY Rapid 3.1"
14
14
  self.default_currency = "AUD"
@@ -187,6 +187,7 @@ module ActiveMerchant #:nodoc:
187
187
  params[key] = {
188
188
  'TotalAmount' => localized_amount(money, currency_code),
189
189
  'InvoiceReference' => truncate(options[:order_id]),
190
+ 'InvoiceNumber' => truncate(options[:order_id], 12),
190
191
  'InvoiceDescription' => truncate(options[:description], 64),
191
192
  'CurrencyCode' => currency_code,
192
193
  }
@@ -352,7 +353,7 @@ module ActiveMerchant #:nodoc:
352
353
  'D4403' => 'No Merchant Failed',
353
354
  'D4404' => 'Pick Up Card Failed',
354
355
  'D4405' => 'Do Not Honour Failed',
355
- 'D4406' => 'Error Failed',
356
+ 'D4406' => 'Error Failed',
356
357
  'D4407' => 'Pick Up Card, Special Failed',
357
358
  'D4409' => 'Request In Progress Failed',
358
359
  'D4412' => 'Invalid Transaction Failed',
@@ -406,6 +407,8 @@ module ActiveMerchant #:nodoc:
406
407
  'D4498' => 'PayPal Create Transaction Error Failed',
407
408
  'D4499' => 'Invalid Transaction for Auth/Void Failed',
408
409
  'S5000' => 'System Error',
410
+ 'S5011' => 'PayPal Connection Error',
411
+ 'S5012' => 'PayPal Settings Error',
409
412
  'S5085' => 'Started 3dSecure',
410
413
  'S5086' => 'Routed 3dSecure',
411
414
  'S5087' => 'Completed 3dSecure',
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/cc5'
1
+ require 'active_merchant/billing/gateways/cc5'
2
2
 
3
3
  module ActiveMerchant #:nodoc:
4
4
  module Billing #:nodoc:
@@ -266,7 +266,6 @@ module ActiveMerchant #:nodoc:
266
266
  "55" => "The 4-digit pin is invalid.",
267
267
  "75" => "Maximum number of pin retries exceeded.",
268
268
  "80" => "Card expiration date is invalid.",
269
- "80" => "Card expiration date is invalid.",
270
269
  "86" => "Can't verify card pin number."
271
270
  }
272
271
  def issuer_message(code)
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/ideal_response'
1
+ require 'active_merchant/billing/gateways/ideal/ideal_response'
2
2
 
3
3
  module ActiveMerchant #:nodoc:
4
4
  module Billing #:nodoc:
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/ideal/ideal_base'
1
+ require 'active_merchant/billing/gateways/ideal/ideal_base'
2
2
 
3
3
  module ActiveMerchant #:nodoc:
4
4
  module Billing #:nodoc:
@@ -0,0 +1,175 @@
1
+ require "nokogiri"
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class IppGateway < Gateway
6
+ self.live_url = 'https://www.ippayments.com.au/interface/api/dts.asmx'
7
+ self.test_url = 'https://demo.ippayments.com.au/interface/api/dts.asmx'
8
+
9
+ self.supported_countries = ['AU']
10
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb]
11
+
12
+ self.homepage_url = 'http://www.ippayments.com.au/'
13
+ self.display_name = 'IPP'
14
+
15
+ self.money_format = :cents
16
+
17
+ STANDARD_ERROR_CODE_MAPPING = {
18
+ "05" => STANDARD_ERROR_CODE[:card_declined],
19
+ "06" => STANDARD_ERROR_CODE[:processing_error],
20
+ "14" => STANDARD_ERROR_CODE[:invalid_number],
21
+ "54" => STANDARD_ERROR_CODE[:expired_card],
22
+ }
23
+
24
+ def initialize(options={})
25
+ requires!(options, :username, :password)
26
+ super
27
+ end
28
+
29
+ def purchase(money, payment, options={})
30
+ commit("SubmitSinglePayment") do |xml|
31
+ xml.Transaction do
32
+ xml.CustRef options[:order_id]
33
+ add_amount(xml, money)
34
+ xml.TrnType "1"
35
+ add_credit_card(xml, payment)
36
+ add_credentials(xml)
37
+ xml.TrnSource options[:ip]
38
+ end
39
+ end
40
+ end
41
+
42
+ def authorize(money, payment, options={})
43
+ commit("SubmitSinglePayment") do |xml|
44
+ xml.Transaction do
45
+ xml.CustRef options[:order_id]
46
+ add_amount(xml, money)
47
+ xml.TrnType "2"
48
+ add_credit_card(xml, payment)
49
+ add_credentials(xml)
50
+ xml.TrnSource options[:ip]
51
+ end
52
+ end
53
+ end
54
+
55
+ def capture(money, authorization, options={})
56
+ commit("SubmitSingleCapture") do |xml|
57
+ xml.Capture do
58
+ xml.Receipt authorization
59
+ add_amount(xml, money)
60
+ add_credentials(xml)
61
+ end
62
+ end
63
+ end
64
+
65
+ def refund(money, authorization, options={})
66
+ commit("SubmitSingleRefund") do |xml|
67
+ xml.Refund do
68
+ xml.Receipt authorization
69
+ add_amount(xml, money)
70
+ add_credentials(xml)
71
+ end
72
+ end
73
+ end
74
+
75
+ def supports_scrubbing?
76
+ true
77
+ end
78
+
79
+ def scrub(transcript)
80
+ transcript.
81
+ gsub(%r((<CardNumber>)[^<]+(<))i, '\1[FILTERED]\2').
82
+ gsub(%r((<CVN>)[^<]+(<))i, '\1[FILTERED]\2').
83
+ gsub(%r((<Password>)[^<]+(<))i, '\1[FILTERED]\2')
84
+ end
85
+
86
+ private
87
+
88
+ def add_credentials(xml)
89
+ xml.Security do
90
+ xml.UserName @options[:username]
91
+ xml.Password @options[:password]
92
+ end
93
+ end
94
+
95
+ def add_amount(xml, money)
96
+ xml.Amount amount(money)
97
+ end
98
+
99
+ def add_credit_card(xml, payment)
100
+ xml.CreditCard :Registered => "False" do
101
+ xml.CardNumber payment.number
102
+ xml.ExpM format(payment.month, :two_digits)
103
+ xml.ExpY format(payment.year, :four_digits)
104
+ xml.CVN payment.verification_value
105
+ xml.CardHolderName payment.name
106
+ end
107
+ end
108
+
109
+ def parse(body)
110
+ element = Nokogiri::XML(body).root.first_element_child.first_element_child
111
+
112
+ response = {}
113
+ doc = Nokogiri::XML(element)
114
+ doc.root.elements.each do |e|
115
+ response[e.name.underscore.to_sym] = e.inner_text
116
+ end
117
+ response
118
+ end
119
+
120
+ def commit(action, &block)
121
+ headers = {
122
+ "Content-Type" => "text/xml; charset=utf-8",
123
+ "SOAPAction" => "http://www.ippayments.com.au/interface/api/dts/#{action}",
124
+ }
125
+ response = parse(ssl_post(commit_url, new_submit_xml(action, &block), headers))
126
+
127
+ Response.new(
128
+ success_from(response),
129
+ message_from(response),
130
+ response,
131
+ authorization: authorization_from(response),
132
+ error_code: error_code_from(response),
133
+ test: test?,
134
+ )
135
+ end
136
+
137
+ def new_submit_xml(action)
138
+ xml = Builder::XmlMarkup.new(indent: 2)
139
+ xml.instruct!
140
+ xml.soap :Envelope, "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance", "xmlns:xsd" => "http://www.w3.org/2001/XMLSchema", "xmlns:soap" => "http://schemas.xmlsoap.org/soap/envelope/" do
141
+ xml.soap :Body do
142
+ xml.__send__(action, "xmlns" => "http://www.ippayments.com.au/interface/api/dts") do
143
+ xml.trnXML do
144
+ inner_xml = Builder::XmlMarkup.new(indent: 2)
145
+ yield(inner_xml)
146
+ xml.cdata!(inner_xml.target!)
147
+ end
148
+ end
149
+ end
150
+ end
151
+ xml.target!
152
+ end
153
+
154
+ def commit_url
155
+ (test? ? test_url : live_url)
156
+ end
157
+
158
+ def success_from(response)
159
+ (response[:response_code] == "0")
160
+ end
161
+
162
+ def error_code_from(response)
163
+ STANDARD_ERROR_CODE_MAPPING[response[:declined_code]]
164
+ end
165
+
166
+ def message_from(response)
167
+ response[:declined_message]
168
+ end
169
+
170
+ def authorization_from(response)
171
+ response[:receipt]
172
+ end
173
+ end
174
+ end
175
+ end