activemerchant 1.56.0 → 1.66.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +331 -0
  3. data/README.md +9 -9
  4. data/lib/active_merchant/billing/check.rb +3 -0
  5. data/lib/active_merchant/billing/credit_card.rb +8 -3
  6. data/lib/active_merchant/billing/credit_card_methods.rb +41 -1
  7. data/lib/active_merchant/billing/gateway.rb +14 -6
  8. data/lib/active_merchant/billing/gateways/adyen.rb +228 -0
  9. data/lib/active_merchant/billing/gateways/authorize_net.rb +157 -44
  10. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +7 -4
  11. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +283 -0
  12. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +68 -2
  13. data/lib/active_merchant/billing/gateways/blue_pay.rb +2 -2
  14. data/lib/active_merchant/billing/gateways/blue_snap.rb +348 -0
  15. data/lib/active_merchant/billing/gateways/bpoint.rb +1 -1
  16. data/lib/active_merchant/billing/gateways/braintree_blue.rb +58 -20
  17. data/lib/active_merchant/billing/gateways/bridge_pay.rb +37 -8
  18. data/lib/active_merchant/billing/gateways/card_stream.rb +161 -40
  19. data/lib/active_merchant/billing/gateways/cashnet.rb +1 -0
  20. data/lib/active_merchant/billing/gateways/checkout_v2.rb +5 -2
  21. data/lib/active_merchant/billing/gateways/citrus_pay.rb +24 -0
  22. data/lib/active_merchant/billing/gateways/clearhaus.rb +24 -40
  23. data/lib/active_merchant/billing/gateways/conekta.rb +6 -1
  24. data/lib/active_merchant/billing/gateways/creditcall.rb +1 -1
  25. data/lib/active_merchant/billing/gateways/credorax.rb +310 -0
  26. data/lib/active_merchant/billing/gateways/culqi.rb +279 -0
  27. data/lib/active_merchant/billing/gateways/cyber_source.rb +80 -64
  28. data/lib/active_merchant/billing/gateways/data_cash.rb +10 -304
  29. data/lib/active_merchant/billing/gateways/digitzs.rb +292 -0
  30. data/lib/active_merchant/billing/gateways/elavon.rb +40 -26
  31. data/lib/active_merchant/billing/gateways/element.rb +356 -0
  32. data/lib/active_merchant/billing/gateways/fat_zebra.rb +16 -2
  33. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +6 -1
  34. data/lib/active_merchant/billing/gateways/forte.rb +10 -2
  35. data/lib/active_merchant/billing/gateways/global_collect.rb +311 -0
  36. data/lib/active_merchant/billing/gateways/global_transport.rb +1 -0
  37. data/lib/active_merchant/billing/gateways/iats_payments.rb +13 -0
  38. data/lib/active_merchant/billing/gateways/in_context_paypal_express.rb +15 -0
  39. data/lib/active_merchant/billing/gateways/iveri.rb +251 -0
  40. data/lib/active_merchant/billing/gateways/jetpay.rb +33 -19
  41. data/lib/active_merchant/billing/gateways/kushki.rb +217 -0
  42. data/lib/active_merchant/billing/gateways/latitude19.rb +416 -0
  43. data/lib/active_merchant/billing/gateways/linkpoint.rb +2 -0
  44. data/lib/active_merchant/billing/gateways/litle.rb +29 -13
  45. data/lib/active_merchant/billing/gateways/mastercard.rb +268 -0
  46. data/lib/active_merchant/billing/gateways/maxipago.rb +145 -122
  47. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +15 -1
  48. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +10 -7
  49. data/lib/active_merchant/billing/gateways/mercury.rb +13 -5
  50. data/lib/active_merchant/billing/gateways/metrics_global.rb +1 -1
  51. data/lib/active_merchant/billing/gateways/migs.rb +23 -1
  52. data/lib/active_merchant/billing/gateways/monei.rb +1 -1
  53. data/lib/active_merchant/billing/gateways/moneris.rb +21 -1
  54. data/lib/active_merchant/billing/gateways/nab_transact.rb +12 -0
  55. data/lib/active_merchant/billing/gateways/ncr_secure_pay.rb +165 -0
  56. data/lib/active_merchant/billing/gateways/netbanx.rb +245 -0
  57. data/lib/active_merchant/billing/gateways/nmi.rb +30 -9
  58. data/lib/active_merchant/billing/gateways/omise.rb +9 -5
  59. data/lib/active_merchant/billing/gateways/openpay.rb +10 -1
  60. data/lib/active_merchant/billing/gateways/opp.rb +362 -0
  61. data/lib/active_merchant/billing/gateways/orbital.rb +28 -7
  62. data/lib/active_merchant/billing/gateways/pagarme.rb +248 -0
  63. data/lib/active_merchant/billing/gateways/pay_junction_v2.rb +190 -0
  64. data/lib/active_merchant/billing/gateways/payeezy.rb +61 -12
  65. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -1
  66. data/lib/active_merchant/billing/gateways/payflow.rb +6 -0
  67. data/lib/active_merchant/billing/gateways/payment_express.rb +1 -1
  68. data/lib/active_merchant/billing/gateways/paymill.rb +29 -11
  69. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +1 -1
  70. data/lib/active_merchant/billing/gateways/paypal_express.rb +1 -6
  71. data/lib/active_merchant/billing/gateways/payu_in.rb +3 -2
  72. data/lib/active_merchant/billing/gateways/payu_latam.rb +402 -0
  73. data/lib/active_merchant/billing/gateways/pin.rb +6 -3
  74. data/lib/active_merchant/billing/gateways/pro_pay.rb +326 -0
  75. data/lib/active_merchant/billing/gateways/psl_card.rb +3 -3
  76. data/lib/active_merchant/billing/gateways/quickpay/quickpay_common.rb +1 -1
  77. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +0 -2
  78. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v4to7.rb +1 -1
  79. data/lib/active_merchant/billing/gateways/quickpay.rb +3 -3
  80. data/lib/active_merchant/billing/gateways/qvalent.rb +44 -1
  81. data/lib/active_merchant/billing/gateways/redsys.rb +3 -0
  82. data/lib/active_merchant/billing/gateways/s5.rb +8 -5
  83. data/lib/active_merchant/billing/gateways/safe_charge.rb +220 -0
  84. data/lib/active_merchant/billing/gateways/sage.rb +397 -128
  85. data/lib/active_merchant/billing/gateways/sage_pay.rb +45 -20
  86. data/lib/active_merchant/billing/gateways/secure_net.rb +0 -5
  87. data/lib/active_merchant/billing/gateways/secure_pay.rb +1 -1
  88. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +12 -0
  89. data/lib/active_merchant/billing/gateways/securion_pay.rb +46 -17
  90. data/lib/active_merchant/billing/gateways/stripe.rb +125 -29
  91. data/lib/active_merchant/billing/gateways/telr.rb +275 -0
  92. data/lib/active_merchant/billing/gateways/tns.rb +13 -222
  93. data/lib/active_merchant/billing/gateways/trans_first.rb +40 -16
  94. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +606 -0
  95. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +114 -9
  96. data/lib/active_merchant/billing/gateways/vanco.rb +14 -10
  97. data/lib/active_merchant/billing/gateways/visanet_peru.rb +209 -0
  98. data/lib/active_merchant/billing/gateways/wepay.rb +73 -38
  99. data/lib/active_merchant/billing/gateways/wirecard.rb +1 -0
  100. data/lib/active_merchant/billing/gateways/world_net.rb +344 -0
  101. data/lib/active_merchant/billing/gateways/worldpay.rb +48 -16
  102. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +15 -0
  103. data/lib/active_merchant/country.rb +6 -4
  104. data/lib/active_merchant/posts_data.rb +1 -1
  105. data/lib/active_merchant/version.rb +1 -1
  106. metadata +32 -13
  107. data/lib/active_merchant/billing/gateways/app55.rb +0 -176
  108. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +0 -314
  109. data/lib/active_merchant/billing/gateways/certo_direct.rb +0 -278
  110. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +0 -89
  111. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +0 -115
  112. data/lib/active_merchant/billing/gateways/sage/sage_vault.rb +0 -149
  113. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +0 -97
@@ -12,7 +12,7 @@ module ActiveMerchant #:nodoc:
12
12
  # and +refund+ will become mandatory.
13
13
  class MercuryGateway < Gateway
14
14
  URLS = {
15
- :test => 'https://w1.mercurydev.net/ws/ws.asmx',
15
+ :test => 'https://w1.mercurycert.net/ws/ws.asmx',
16
16
  :live => 'https://w1.mercurypay.com/ws/ws.asmx'
17
17
  }
18
18
 
@@ -197,12 +197,20 @@ module ActiveMerchant #:nodoc:
197
197
  if credit_card.track_data.present?
198
198
  # Track 1 has a start sentinel (STX) of '%' and track 2 is ';'
199
199
  # Track 1 and 2 have identical end sentinels (ETX) of '?'
200
+ # Tracks may or may not have checksum (LRC) after the ETX
200
201
  # If the track has no STX or is corrupt, we send it as track 1, to let Mercury
201
202
  #handle with the validation error as it sees fit.
202
- # Track 2 requires having the start and end sentinels stripped. Track 1 does not.
203
- if credit_card.track_data[0] == ';' # track 2 start sentinel (STX)
204
- xml.tag! 'Track2', credit_card.track_data[1..-2]
205
- else # track 1 or a corrupt track
203
+ # Track 2 requires having the STX and ETX stripped. Track 1 does not.
204
+ # Max-length track 1s require having the STX and ETX stripped. Max is 79 bytes including LRC.
205
+ is_track_2 = credit_card.track_data[0] == ';'
206
+ etx_index = credit_card.track_data.rindex('?') || credit_card.track_data.length
207
+ is_max_track1 = etx_index >= 77
208
+
209
+ if is_track_2
210
+ xml.tag! 'Track2', credit_card.track_data[1...etx_index]
211
+ elsif is_max_track1
212
+ xml.tag! 'Track1', credit_card.track_data[1...etx_index]
213
+ else
206
214
  xml.tag! 'Track1', credit_card.track_data
207
215
  end
208
216
  else
@@ -218,7 +218,7 @@ module ActiveMerchant #:nodoc:
218
218
  post[:delim_data] = "TRUE"
219
219
  post[:delim_char] = ","
220
220
  post[:encap_char] = "$"
221
- post[:solution_ID] = application_id if application_id.present? && application_id != "ActiveMerchant"
221
+ post[:solution_ID] = application_id if application_id
222
222
 
223
223
  request = post.merge(parameters).collect { |key, value| "x_#{key}=#{CGI.escape(value.to_s)}" }.join("&")
224
224
  request
@@ -25,6 +25,7 @@ module ActiveMerchant #:nodoc:
25
25
  self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb]
26
26
 
27
27
  self.money_format = :cents
28
+ self.currencies_without_fractions = %w(IDR)
28
29
 
29
30
  # The homepage URL of the gateway
30
31
  self.homepage_url = 'http://mastercard.com/mastercardsps'
@@ -63,6 +64,7 @@ module ActiveMerchant #:nodoc:
63
64
  add_invoice(post, options)
64
65
  add_creditcard(post, creditcard)
65
66
  add_standard_parameters('pay', post, options[:unique_id])
67
+ add_3ds(post, options)
66
68
 
67
69
  commit(post)
68
70
  end
@@ -103,6 +105,17 @@ module ActiveMerchant #:nodoc:
103
105
  commit(post)
104
106
  end
105
107
 
108
+ def void(authorization, options = {})
109
+ requires!(@options, :advanced_login, :advanced_password)
110
+
111
+ post = options.merge(:TransNo => authorization)
112
+
113
+ add_advanced_user(post)
114
+ add_standard_parameters('voidAuthorisation', post, options[:unique_id])
115
+
116
+ commit(post)
117
+ end
118
+
106
119
  def credit(money, authorization, options = {})
107
120
  ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE
108
121
  refund(money, authorization, options)
@@ -189,7 +202,7 @@ module ActiveMerchant #:nodoc:
189
202
  private
190
203
 
191
204
  def add_amount(post, money, options)
192
- post[:Amount] = amount(money)
205
+ post[:Amount] = localized_amount(money, options[:currency])
193
206
  post[:Currency] = options[:currency] if options[:currency]
194
207
  end
195
208
 
@@ -202,6 +215,15 @@ module ActiveMerchant #:nodoc:
202
215
  post[:OrderInfo] = options[:order_id]
203
216
  end
204
217
 
218
+ def add_3ds(post, options)
219
+ post[:VerType] = options[:ver_type] if options[:ver_type]
220
+ post[:VerToken] = options[:ver_token] if options[:ver_token]
221
+ post["3DSXID"] = options[:three_ds_xid] if options[:three_ds_xid]
222
+ post["3DSECI"] = options[:three_ds_eci] if options[:three_ds_eci]
223
+ post["3DSenrolled"] = options[:three_ds_enrolled] if options[:three_ds_enrolled]
224
+ post["3DSstatus"] = options[:three_ds_status] if options[:three_ds_status]
225
+ end
226
+
205
227
  def add_creditcard(post, creditcard)
206
228
  post[:CardNum] = creditcard.number
207
229
  post[:CardSecurityCode] = creditcard.verification_value if creditcard.verification_value?
@@ -14,7 +14,7 @@ module ActiveMerchant #:nodoc:
14
14
  self.test_url = 'https://test.monei-api.net/payment/ctpe'
15
15
  self.live_url = 'https://monei-api.net/payment/ctpe'
16
16
 
17
- self.supported_countries = ['ES']
17
+ self.supported_countries = ['AD', 'AT', 'BE', 'BG', 'CA', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FO', 'FR', 'GB', 'GI', 'GR', 'HU', 'IE', 'IL', 'IS', 'IT', 'LI', 'LT', 'LU', 'LV', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK', 'TR', 'US', 'VA']
18
18
  self.default_currency = 'EUR'
19
19
  self.supported_cardtypes = [:visa, :master, :maestro, :jcb, :american_express]
20
20
 
@@ -53,7 +53,7 @@ module ActiveMerchant #:nodoc:
53
53
  post[:crypt_type] = options[:crypt_type] || @options[:crypt_type]
54
54
  action = if post[:cavv]
55
55
  'cavv_preauth'
56
- elsif post[:data_key].blank?
56
+ elsif post[:data_key].blank?
57
57
  'preauth'
58
58
  else
59
59
  'res_preauth_cc'
@@ -132,6 +132,13 @@ module ActiveMerchant #:nodoc:
132
132
  commit 'refund', crediting_params(authorization, :amount => amount(money))
133
133
  end
134
134
 
135
+ def verify(credit_card, options={})
136
+ MultiResponse.run(:use_first_response) do |r|
137
+ r.process { authorize(100, credit_card, options) }
138
+ r.process(:ignore_result) { void(r.authorization, options) }
139
+ end
140
+ end
141
+
135
142
  def store(credit_card, options = {})
136
143
  post = {}
137
144
  post[:pan] = credit_card.number
@@ -155,6 +162,19 @@ module ActiveMerchant #:nodoc:
155
162
  commit('res_update_cc', post)
156
163
  end
157
164
 
165
+ def supports_scrubbing?
166
+ true
167
+ end
168
+
169
+ def scrub(transcript)
170
+ transcript.
171
+ gsub(%r((<store_id>).+(</store_id>)), '\1[FILTERED]\2').
172
+ gsub(%r((<api_token>).+(</api_token>)), '\1[FILTERED]\2').
173
+ gsub(%r((<pan>).+(</pan>)), '\1[FILTERED]\2').
174
+ gsub(%r((<cvd_value>).+(</cvd_value>)), '\1[FILTERED]\2').
175
+ gsub(%r((<cavv>).+(</cavv>)), '\1[FILTERED]\2')
176
+ end
177
+
158
178
  private # :nodoc: all
159
179
 
160
180
  def expdate(creditcard)
@@ -79,6 +79,18 @@ module ActiveMerchant #:nodoc:
79
79
  commit_periodic(:deletecrn, build_unstore_request(identification, options))
80
80
  end
81
81
 
82
+ def supports_scrubbing?
83
+ true
84
+ end
85
+
86
+ def scrub(transcript)
87
+ return "" if transcript.blank?
88
+ transcript.
89
+ gsub(%r((<cardNumber>)[^<]+(<))i, '\1[FILTERED]\2').
90
+ gsub(%r((<cvv>)[^<]+(<))i, '\1[FILTERED]\2').
91
+ gsub(%r((<password>)[^<]+(<))i, '\1[FILTERED]\2')
92
+ end
93
+
82
94
  private
83
95
 
84
96
  def add_metadata(xml, options)
@@ -0,0 +1,165 @@
1
+ require 'nokogiri'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class NcrSecurePayGateway < Gateway
6
+ self.test_url = 'https://testbox.monetra.com:8665/'
7
+ self.live_url = 'https://portal.ncrsecurepay.com:8444/'
8
+
9
+ self.supported_countries = ['US']
10
+ self.default_currency = 'USD'
11
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
12
+
13
+ self.homepage_url = 'http://www.ncrretailonline.com'
14
+ self.display_name = 'NCR Secure Pay'
15
+
16
+ def initialize(options={})
17
+ requires!(options, :username, :password)
18
+ super
19
+ end
20
+
21
+ def purchase(money, payment, options={})
22
+ post = {}
23
+ add_invoice(post, money, options)
24
+ add_payment(post, payment)
25
+ add_address(post, payment, options)
26
+
27
+ commit('sale', post)
28
+ end
29
+
30
+ def authorize(money, payment, options={})
31
+ post = {}
32
+ add_invoice(post, money, options)
33
+ add_payment(post, payment)
34
+ add_address(post, payment, options)
35
+
36
+ commit('preauth', post)
37
+ end
38
+
39
+ def capture(money, authorization, options={})
40
+ post = {}
41
+ add_reference(post, authorization)
42
+ add_invoice(post, money, options)
43
+
44
+ commit('preauthcomplete', post)
45
+ end
46
+
47
+ def refund(money, authorization, options={})
48
+ post = {}
49
+ add_reference(post, authorization)
50
+ add_invoice(post, money, options)
51
+
52
+ commit('credit', post)
53
+ end
54
+
55
+ def void(authorization, options={})
56
+ post = {}
57
+ add_reference(post, authorization)
58
+ commit('void', post)
59
+ end
60
+
61
+ def verify(credit_card, options={})
62
+ MultiResponse.run(:use_first_response) do |r|
63
+ r.process { authorize(100, credit_card, options) }
64
+ r.process(:ignore_result) { void(r.authorization, options) }
65
+ end
66
+ end
67
+
68
+ def supports_scrubbing?
69
+ true
70
+ end
71
+
72
+ def scrub(transcript)
73
+ transcript.gsub(%r((<password>)[^<]*(</password>))i, '\1[FILTERED]\2').
74
+ gsub(%r((<account>)[^<]*(</account>))i, '\1[FILTERED]\2').
75
+ gsub(%r((<cv>)[^<]*(</cv>))i, '\1[FILTERED]\2')
76
+ end
77
+
78
+ private
79
+
80
+ def add_reference(post, reference)
81
+ post[:ttid] = reference
82
+ end
83
+
84
+ def add_address(post, payment, options)
85
+ address = options[:billing_address] || options[:address]
86
+ post[:zip] = address[:zip]
87
+ post[:street] = address[:address1]
88
+ end
89
+
90
+ def add_invoice(post, money, options)
91
+ post[:amount] = amount(money)
92
+ post[:currency] = (options[:currency] || currency(money))
93
+ post[:descmerch] = options[:merchant] if options[:merchant]
94
+ post[:ordernum] = options[:order_id] if options[:order_id]
95
+ post[:comments] = options[:description] if options[:description]
96
+ end
97
+
98
+ def add_payment(post, payment)
99
+ post[:cardholdername] = payment.name
100
+ post[:account] = payment.number
101
+ post[:cv] = payment.verification_value
102
+ post[:expdate] = expdate(payment)
103
+ end
104
+
105
+ def parse(body)
106
+ doc = Nokogiri::XML(body)
107
+ doc.remove_namespaces!
108
+ response = doc.xpath("/MonetraResp/Resp")[0]
109
+ resp_params = {}
110
+
111
+ response.elements.each do |node|
112
+ resp_params[node.name.downcase.to_sym] = node.text
113
+ end
114
+ resp_params
115
+ end
116
+
117
+ def commit(action, parameters)
118
+ url = (test? ? test_url : live_url)
119
+ response = parse(ssl_post(url, request_body(action, parameters)))
120
+
121
+ Response.new(
122
+ success_from(response),
123
+ message_from(response),
124
+ response,
125
+ authorization: authorization_from(response),
126
+ test: test?,
127
+ error_code: error_code_from(response)
128
+ )
129
+ end
130
+
131
+ def success_from(response)
132
+ response[:code] == "AUTH"
133
+ end
134
+
135
+ def message_from(response)
136
+ response[:verbiage]
137
+ end
138
+
139
+ def authorization_from(response)
140
+ response[:ttid]
141
+ end
142
+
143
+ def request_body(action, parameters = {})
144
+ Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |xml|
145
+ xml.MonetraTrans do
146
+ xml.Trans(identifier: parameters.delete(:identifier) || "1") do
147
+ xml.username(options[:username])
148
+ xml.password(options[:password])
149
+ xml.action(action)
150
+ parameters.each do |name, value|
151
+ xml.send(name, value)
152
+ end
153
+ end
154
+ end
155
+ end.to_xml
156
+ end
157
+
158
+ def error_code_from(response)
159
+ unless success_from(response)
160
+ response[:msoft_code] || response[:phard_code]
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,245 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class NetbanxGateway < Gateway
4
+ # Netbanx is the new REST based API for Optimal Payments / Paysafe
5
+ self.test_url = 'https://api.test.netbanx.com/'
6
+ self.live_url = 'https://api.netbanx.com/'
7
+
8
+ self.supported_countries = ['CA', 'US', 'GB']
9
+ self.default_currency = 'CAD'
10
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
11
+ self.money_format = :cents
12
+
13
+ self.homepage_url = 'https://processing.paysafe.com/'
14
+ self.display_name = 'Netbanx by PaySafe'
15
+
16
+ STANDARD_ERROR_CODE_MAPPING = {}
17
+
18
+ def initialize(options={})
19
+ requires!(options, :account_number, :api_key)
20
+ super
21
+ end
22
+
23
+ def purchase(money, payment, options={})
24
+ post = {}
25
+ add_invoice(post, money, options)
26
+ add_settle_with_auth(post)
27
+ add_payment(post, payment)
28
+
29
+ commit(:post, 'auths', post)
30
+ end
31
+
32
+ def authorize(money, payment, options={})
33
+ post = {}
34
+ add_invoice(post, money, options)
35
+ add_payment(post, payment)
36
+
37
+ commit(:post, 'auths', post)
38
+ end
39
+
40
+ def capture(money, authorization, options={})
41
+ post = {}
42
+ add_invoice(post, money, options)
43
+
44
+ commit(:post, "auths/#{authorization}/settlements", post)
45
+ end
46
+
47
+ def refund(money, authorization, options={})
48
+ post = {}
49
+ add_invoice(post, money, options)
50
+
51
+ commit(:post, "settlements/#{authorization}/refunds", post)
52
+ end
53
+
54
+ def void(authorization, options={})
55
+ post = {}
56
+ add_order_id(post, options)
57
+
58
+ commit(:post, "auths/#{authorization}/voidauths", post)
59
+ end
60
+
61
+ def verify(credit_card, options={})
62
+ post = {}
63
+ add_payment(post, credit_card)
64
+ add_order_id(post, options)
65
+
66
+ commit(:post, 'verifications', post)
67
+ end
68
+
69
+ # note: when passing options[:customer] we only attempt to add the
70
+ # card to the profile_id passed as the options[:customer]
71
+ def store(credit_card, options={})
72
+ # locale can only be one of en_US, fr_CA, en_GB
73
+ requires!(options, :locale)
74
+ post = {}
75
+ add_credit_card(post, credit_card, options)
76
+ add_customer_data(post, options)
77
+
78
+ commit(:post, 'customervault/v1/profiles', post)
79
+ end
80
+
81
+ def unstore(identification, options = {})
82
+ customer_id, card_id = identification.split('|')
83
+
84
+ if card_id.nil?
85
+ # deleting the profile
86
+ commit(:delete, "customervault/v1/profiles/#{CGI.escape(customer_id)}", nil)
87
+ else
88
+ # deleting the card from the profile
89
+ commit(:delete, "customervault/v1/profiles/#{CGI.escape(customer_id)}/cards/#{CGI.escape(card_id)}", nil)
90
+ end
91
+ end
92
+
93
+ def supports_scrubbing?
94
+ true
95
+ end
96
+
97
+ def scrub(transcript)
98
+ transcript.
99
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
100
+ gsub(%r(("card\\?":{\\?"cardNum\\?":\\?")\d+), '\1[FILTERED]').
101
+ gsub(%r(("cvv\\?":\\?")\d+), '\1[FILTERED]')
102
+ end
103
+
104
+ private
105
+
106
+ def add_settle_with_auth(post)
107
+ post[:settleWithAuth] = true
108
+ end
109
+
110
+ def add_customer_data(post, options)
111
+ post[:merchantCustomerId] = (options[:merchant_customer_id] || SecureRandom.uuid)
112
+ post[:locale] = options[:locale]
113
+ # if options[:billing_address]
114
+ # post[:address] = map_address(options[:billing_address])
115
+ # end
116
+ end
117
+
118
+ def add_credit_card(post, credit_card, options = {})
119
+ post[:card] ||= {}
120
+ post[:card][:cardNum] = credit_card.number
121
+ post[:card][:holderName] = credit_card.name
122
+ post[:card][:cvv] = credit_card.verification_value
123
+ post[:card][:cardExpiry] = expdate(credit_card)
124
+ if options[:billing_address]
125
+ post[:card][:billingAddress] = map_address(options[:billing_address])
126
+ end
127
+ end
128
+
129
+ def add_invoice(post, money, options)
130
+ post[:amount] = amount(money)
131
+ post[:currencyCode] = options[:currency] if options[:currency]
132
+ add_order_id(post, options)
133
+
134
+ if options[:billing_address]
135
+ post[:billingDetails] = map_address(options[:billing_address])
136
+ end
137
+
138
+ end
139
+
140
+ def add_payment(post, credit_card_or_reference, options = {})
141
+ post[:card] ||= {}
142
+ if credit_card_or_reference.is_a?(String)
143
+ post[:card][:paymentToken] = credit_card_or_reference
144
+ else
145
+ post[:card][:cardNum] = credit_card_or_reference.number
146
+ post[:card][:cvv] = credit_card_or_reference.verification_value
147
+ post[:card][:cardExpiry] = expdate(credit_card_or_reference)
148
+ end
149
+ end
150
+
151
+ def expdate(credit_card)
152
+ year = format(credit_card.year, :four_digits)
153
+ month = format(credit_card.month, :two_digits)
154
+
155
+ # returns a hash (necessary in the card JSON object)
156
+ { :month => month, :year => year }
157
+ end
158
+
159
+ def add_order_id(post, options)
160
+ post[:merchantRefNum] = (options[:order_id] || SecureRandom.uuid)
161
+ end
162
+
163
+ def map_address(address)
164
+ return {} if address.nil?
165
+ country = Country.find(address[:country]) if address[:country]
166
+ mapped = {
167
+ :street => address[:address1],
168
+ :city => address[:city],
169
+ :zip => address[:zip],
170
+ }
171
+ mapped.merge!({:country => country.code(:alpha2).value}) unless country.blank?
172
+
173
+ mapped
174
+ end
175
+
176
+ def parse(body)
177
+ body.blank? ? {} : JSON.parse(body)
178
+ end
179
+
180
+ def commit(method, uri, parameters)
181
+ params = parameters.to_json unless parameters.nil?
182
+ response = begin
183
+ parse(ssl_request(method, get_url(uri), params, headers))
184
+ rescue ResponseError => e
185
+ return Response.new(false, 'Invalid Login') if(e.response.code == '401')
186
+ parse(e.response.body)
187
+ end
188
+
189
+ success = success_from(response)
190
+ Response.new(
191
+ success,
192
+ message_from(success, response),
193
+ response,
194
+ :test => test?,
195
+ :authorization => authorization_from(success, get_url(uri), method, response)
196
+ )
197
+ end
198
+
199
+ def get_url(uri)
200
+ url = (test? ? test_url : live_url)
201
+ if uri =~ /^customervault/
202
+ "#{url}#{uri}"
203
+ else
204
+ "#{url}cardpayments/v1/accounts/#{@options[:account_number]}/#{uri}"
205
+ end
206
+ end
207
+
208
+ def success_from(response)
209
+ response.blank? || !response.key?('error')
210
+ end
211
+
212
+ def message_from(success, response)
213
+ success ? 'OK' : (response['error']['message'] || "Unknown error - please contact Netbanx-Paysafe")
214
+ end
215
+
216
+ def authorization_from(success, url, method, response)
217
+ if success && response.present? && url.match(/cardpayments\/v1\/accounts\/.*\//)
218
+ response['id']
219
+ elsif method == :post && url.match(/customervault\/.*\//)
220
+ # auth for tokenised customer vault is returned as
221
+ # customer_profile_id|card_id|payment_method_token
222
+ #
223
+ # customer_profile_id is the uuid that identifies the customer
224
+ # card_id is the uuid that identifies the card
225
+ # payment_method_token is the token that needs to be used when
226
+ # calling purchase with a token
227
+ #
228
+ # both id's are used to unstore, the payment token is only used for
229
+ # purchase transactions
230
+ [response['id'], response['cards'].first['id'], response['cards'].first['paymentToken']].join("|")
231
+ end
232
+ end
233
+
234
+ # Builds the auth and U-A headers for the request
235
+ def headers
236
+ {
237
+ 'Accept' => 'application/json',
238
+ 'Content-type' => 'application/json',
239
+ 'Authorization' => "Basic #{Base64.strict_encode64(@options[:api_key].to_s).strip}",
240
+ 'User-Agent' => "Netbanx-Paysafe v1.0/ActiveMerchant #{ActiveMerchant::VERSION}"
241
+ }
242
+ end
243
+ end
244
+ end
245
+ end