activemerchant 1.45.0 → 1.46.0

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