activemerchant 1.44.1 → 1.45.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 (58) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +1 -3
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG +48 -0
  5. data/CONTRIBUTORS +12 -0
  6. data/README.md +15 -5
  7. data/lib/active_merchant/billing.rb +2 -0
  8. data/lib/active_merchant/billing/apple_pay_payment_token.rb +22 -0
  9. data/lib/active_merchant/billing/gateway.rb +36 -4
  10. data/lib/active_merchant/billing/gateways/adyen.rb +6 -2
  11. data/lib/active_merchant/billing/gateways/authorize_net.rb +332 -255
  12. data/lib/active_merchant/billing/gateways/bank_frick.rb +225 -0
  13. data/lib/active_merchant/billing/gateways/bogus.rb +9 -9
  14. data/lib/active_merchant/billing/gateways/borgun.rb +0 -1
  15. data/lib/active_merchant/billing/gateways/braintree_blue.rb +8 -0
  16. data/lib/active_merchant/billing/gateways/cashnet.rb +17 -10
  17. data/lib/active_merchant/billing/gateways/checkout.rb +213 -0
  18. data/lib/active_merchant/billing/gateways/conekta.rb +1 -1
  19. data/lib/active_merchant/billing/gateways/cyber_source.rb +1 -1
  20. data/lib/active_merchant/billing/gateways/elavon.rb +3 -3
  21. data/lib/active_merchant/billing/gateways/eway_rapid.rb +114 -13
  22. data/lib/active_merchant/billing/gateways/finansbank.rb +1 -1
  23. data/lib/active_merchant/billing/gateways/global_transport.rb +183 -0
  24. data/lib/active_merchant/billing/gateways/hps.rb +27 -20
  25. data/lib/active_merchant/billing/gateways/iats_payments.rb +68 -35
  26. data/lib/active_merchant/billing/gateways/litle.rb +36 -1
  27. data/lib/active_merchant/billing/gateways/merchant_one.rb +0 -1
  28. data/lib/active_merchant/billing/gateways/merchant_ware.rb +8 -4
  29. data/lib/active_merchant/billing/gateways/mercury.rb +17 -10
  30. data/lib/active_merchant/billing/gateways/moneris.rb +11 -6
  31. data/lib/active_merchant/billing/gateways/moneris_us.rb +126 -33
  32. data/lib/active_merchant/billing/gateways/money_movers.rb +0 -1
  33. data/lib/active_merchant/billing/gateways/net_registry.rb +6 -1
  34. data/lib/active_merchant/billing/gateways/network_merchants.rb +5 -5
  35. data/lib/active_merchant/billing/gateways/nmi.rb +241 -5
  36. data/lib/active_merchant/billing/gateways/openpay.rb +1 -0
  37. data/lib/active_merchant/billing/gateways/optimal_payment.rb +6 -1
  38. data/lib/active_merchant/billing/gateways/orbital.rb +6 -4
  39. data/lib/active_merchant/billing/gateways/pay_junction.rb +9 -5
  40. data/lib/active_merchant/billing/gateways/payex.rb +19 -9
  41. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +2 -2
  42. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +4 -0
  43. data/lib/active_merchant/billing/gateways/payscout.rb +0 -2
  44. data/lib/active_merchant/billing/gateways/pin.rb +1 -1
  45. data/lib/active_merchant/billing/gateways/psigate.rb +1 -2
  46. data/lib/active_merchant/billing/gateways/redsys.rb +37 -40
  47. data/lib/active_merchant/billing/gateways/secure_pay.rb +181 -9
  48. data/lib/active_merchant/billing/gateways/stripe.rb +106 -31
  49. data/lib/active_merchant/billing/gateways/tns.rb +227 -0
  50. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +38 -10
  51. data/lib/active_merchant/billing/gateways/webpay.rb +14 -0
  52. data/lib/active_merchant/billing/payment_token.rb +21 -0
  53. data/lib/active_merchant/billing/response.rb +2 -1
  54. data/lib/active_merchant/country.rb +6 -1
  55. data/lib/active_merchant/version.rb +1 -1
  56. metadata +8 -3
  57. metadata.gz.sig +0 -0
  58. data/lib/active_merchant/billing/gateways/samurai.rb +0 -130
@@ -0,0 +1,225 @@
1
+ require 'nokogiri'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ # For more information visit {Bank Frick Acquiring Services}[http://www.bankfrickacquiring.com/merchantsolutions_en.html]
6
+ #
7
+ # Written by Piers Chambers (Varyonic.com)
8
+ class BankFrickGateway < Gateway
9
+ self.test_url = 'https://test.ctpe.io/payment/ctpe'
10
+ self.live_url = 'https://ctpe.io/payment/ctpe'
11
+
12
+ self.supported_countries = ['LI','US']
13
+ self.default_currency = 'EUR'
14
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
15
+
16
+ self.homepage_url = 'http://www.bankfrickacquiring.com/'
17
+ self.display_name = 'Bank Frick'
18
+
19
+ # The set of supported transactions for this gateway.
20
+ # More operations are supported by the gateway itself, but
21
+ # are not supported in this library.
22
+ SUPPORTED_TRANSACTIONS = {
23
+ 'sale' => 'CC.DB',
24
+ 'authonly' => 'CC.PA',
25
+ 'capture' => 'CC.CP',
26
+ 'refund' => 'CC.RF',
27
+ 'void' => 'CC.RV',
28
+ }
29
+
30
+ def initialize(options={})
31
+ requires!(options, :sender, :channel, :userid, :userpwd)
32
+ super
33
+ end
34
+
35
+ def purchase(money, payment, options={})
36
+ post = {}
37
+ add_invoice(post, money, options)
38
+ add_payment(post, payment)
39
+ add_address(post, payment, options)
40
+ add_customer_data(post, options)
41
+
42
+ commit('sale', post)
43
+ end
44
+
45
+ def authorize(money, payment, options={})
46
+ post = {}
47
+ add_invoice(post, money, options)
48
+ add_payment(post, payment)
49
+ add_address(post, payment, options)
50
+ add_customer_data(post, options)
51
+
52
+ commit('authonly', post)
53
+ end
54
+
55
+ def capture(money, authorization, options={})
56
+ post = {}
57
+ post[:authorization] = authorization
58
+ add_invoice(post, money, options)
59
+
60
+ commit('capture', post)
61
+ end
62
+
63
+ def refund(money, authorization, options={})
64
+ post = {}
65
+ post[:authorization] = authorization
66
+ add_invoice(post, money, options)
67
+
68
+ commit('refund', post)
69
+ end
70
+
71
+ def void(authorization, options={})
72
+ post = {}
73
+ post[:authorization] = authorization
74
+
75
+ commit('void', post)
76
+ end
77
+
78
+ def verify(credit_card, options={})
79
+ MultiResponse.run(:use_first_response) do |r|
80
+ r.process { authorize(100, credit_card, options) }
81
+ r.process(:ignore_result) { void(r.authorization, options) }
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def add_customer_data(post, options)
88
+ post[:email] = options[:email] || 'noone@example.com'
89
+ post[:ip] = options[:ip] || '0.0.0.0'
90
+ end
91
+
92
+ def add_address(post, creditcard, options)
93
+ if address = options[:billing_address] || options[:address]
94
+ post[:address] = address[:address1].to_s
95
+ post[:company] = address[:company].to_s
96
+ post[:phone] = address[:phone].to_s.gsub(/[^0-9]/, '') || "0000000"
97
+ post[:zip] = address[:zip].to_s
98
+ post[:city] = address[:city].to_s
99
+ post[:country] = address[:country].to_s
100
+ post[:state] = address[:state].blank? ? 'n/a' : address[:state]
101
+ end
102
+ end
103
+
104
+ def add_invoice(post, money, options)
105
+ post[:order_id] = options[:order_id] if post.has_key? :order_id
106
+ post[:amount] = amount(money)
107
+ post[:currency] = (options[:currency] || currency(money))
108
+ post[:description] = options[:description]
109
+ end
110
+
111
+ def add_payment(post, payment)
112
+ post[:first_name] = payment.first_name
113
+ post[:last_name] = payment.last_name
114
+ post[:brand] = payment.brand
115
+ post[:card_num] = payment.number
116
+ post[:card_code] = payment.verification_value if payment.verification_value?
117
+ post[:exp_year] = payment.year
118
+ post[:exp_month] = payment.month
119
+ end
120
+
121
+ def parse(body)
122
+ results = {}
123
+ xml = Nokogiri::XML(body)
124
+ resp = xml.xpath("//Response/Transaction/Identification")
125
+ resp.children.each do |element|
126
+ results[element.name.downcase.to_sym] = element.text
127
+ end
128
+ resp = xml.xpath("//Response/Transaction/Processing")
129
+ resp.children.each do |element|
130
+ results[element.name.downcase.to_sym] = element.text
131
+ end
132
+ results
133
+ end
134
+
135
+ def commit(action, parameters)
136
+ url = (test? ? test_url : live_url)
137
+ headers = {
138
+ 'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8'
139
+ }
140
+ response = parse(ssl_post(url, post_data(action, parameters), headers))
141
+
142
+ Response.new(
143
+ success_from(response),
144
+ message_from(response),
145
+ response,
146
+ authorization: authorization_from(response),
147
+ test: test?
148
+ )
149
+ end
150
+
151
+ def success_from(response)
152
+ response[:result] == 'ACK'
153
+ end
154
+
155
+ def message_from(response)
156
+ response[:return]
157
+ end
158
+
159
+ def authorization_from(response)
160
+ response[:uniqueid]
161
+ end
162
+
163
+ def post_data(action, parameters = {})
164
+ xml = build_xml_request(action, parameters)
165
+ "load=#{CGI.escape(xml)}"
166
+ end
167
+
168
+ def build_xml_request(action, data)
169
+ xml = Builder::XmlMarkup.new :indent => 2
170
+ xml.Request(version: '1.0') do
171
+ xml.Header do
172
+ xml.Security(sender: @options[:sender], type: 'MERCHANT')
173
+ end
174
+ xml.Transaction(response: 'SYNC', channel: @options[:channel], mode: 'LIVE') do
175
+ xml.User(pwd: @options[:userpwd], login: @options[:userid])
176
+ xml.Identification do
177
+ xml.TransactionID data[:order_id] if data.has_key? :order_id
178
+ xml.ReferenceID data[:authorization] if data.has_key? :authorization
179
+ end
180
+ xml.Account do
181
+ xml.Holder "#{data[:first_name]} #{data[:last_name]}"
182
+ xml.Brand data[:brand]
183
+ xml.Number data[:card_num]
184
+ xml.Bank data[:bankname]
185
+ xml.Country data[:country]
186
+ xml.Authorization data[:authorization]
187
+ xml.Verification data[:card_code]
188
+ xml.Year data[:exp_year]
189
+ xml.Month data[:exp_month]
190
+ end if data.has_key? :card_num
191
+ xml.Payment(code: SUPPORTED_TRANSACTIONS[action]) do
192
+ xml.Presentation do
193
+ xml.Amount data[:amount]
194
+ xml.Currency data[:currency]
195
+ xml.Usage data[:description]
196
+ end
197
+ end
198
+ xml.Customer do
199
+ xml.Contact do
200
+ xml.Email data[:email]
201
+ xml.Mobile data[:mobile]
202
+ xml.Ip data[:ip]
203
+ xml.Phone data[:phone]
204
+ end
205
+ xml.Address do
206
+ xml.Street data[:address]
207
+ xml.Zip data[:zip]
208
+ xml.City data[:city]
209
+ xml.State data[:state]
210
+ xml.Country data[:country]
211
+ end
212
+ xml.Name do
213
+ xml.Salutation data[:salutation]
214
+ xml.Title data[:title]
215
+ xml.Given data[:first_name]
216
+ xml.Family data[:last_name]
217
+ xml.Company data[:company]
218
+ end
219
+ end if data.has_key? :last_name
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end
@@ -13,7 +13,7 @@ module ActiveMerchant #:nodoc:
13
13
  REFUND_ERROR_MESSAGE = "Bogus Gateway: Use trans_id number ending in 1 for exception, 2 for error and anything else for success"
14
14
  CHECK_ERROR_MESSAGE = "Bogus Gateway: Use bank account number ending in 1 for success, 2 for exception and anything else for error"
15
15
 
16
- self.supported_countries = ['US']
16
+ self.supported_countries = []
17
17
  self.supported_cardtypes = [:bogus]
18
18
  self.homepage_url = 'http://example.com'
19
19
  self.display_name = 'Bogus'
@@ -24,7 +24,7 @@ module ActiveMerchant #:nodoc:
24
24
  when /1$/
25
25
  Response.new(true, SUCCESS_MESSAGE, {:authorized_amount => money}, :test => true, :authorization => AUTHORIZATION )
26
26
  when /2$/
27
- Response.new(false, FAILURE_MESSAGE, {:authorized_amount => money, :error => FAILURE_MESSAGE }, :test => true)
27
+ Response.new(false, FAILURE_MESSAGE, {:authorized_amount => money, :error => FAILURE_MESSAGE }, :test => true, :error_code => STANDARD_ERROR_CODE[:processing_error])
28
28
  else
29
29
  raise Error, error_message(paysource)
30
30
  end
@@ -36,7 +36,7 @@ module ActiveMerchant #:nodoc:
36
36
  when /1$/, AUTHORIZATION
37
37
  Response.new(true, SUCCESS_MESSAGE, {:paid_amount => money}, :test => true, :authorization => AUTHORIZATION)
38
38
  when /2$/
39
- Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE },:test => true)
39
+ Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE }, :test => true, :error_code => STANDARD_ERROR_CODE[:processing_error])
40
40
  else
41
41
  raise Error, error_message(paysource)
42
42
  end
@@ -53,7 +53,7 @@ module ActiveMerchant #:nodoc:
53
53
  when /1$/
54
54
  Response.new(true, SUCCESS_MESSAGE, {:paid_amount => money}, :test => true )
55
55
  when /2$/
56
- Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE }, :test => true)
56
+ Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE }, :test => true, :error_code => STANDARD_ERROR_CODE[:processing_error])
57
57
  else
58
58
  raise Error, error_message(paysource)
59
59
  end
@@ -65,7 +65,7 @@ module ActiveMerchant #:nodoc:
65
65
  when /1$/
66
66
  raise Error, REFUND_ERROR_MESSAGE
67
67
  when /2$/
68
- Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE }, :test => true)
68
+ Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE }, :test => true, :error_code => STANDARD_ERROR_CODE[:processing_error])
69
69
  else
70
70
  Response.new(true, SUCCESS_MESSAGE, {:paid_amount => money}, :test => true)
71
71
  end
@@ -77,7 +77,7 @@ module ActiveMerchant #:nodoc:
77
77
  when /1$/
78
78
  raise Error, CAPTURE_ERROR_MESSAGE
79
79
  when /2$/
80
- Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE }, :test => true)
80
+ Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE }, :test => true, :error_code => STANDARD_ERROR_CODE[:processing_error])
81
81
  else
82
82
  Response.new(true, SUCCESS_MESSAGE, {:paid_amount => money}, :test => true)
83
83
  end
@@ -88,7 +88,7 @@ module ActiveMerchant #:nodoc:
88
88
  when /1$/
89
89
  raise Error, VOID_ERROR_MESSAGE
90
90
  when /2$/
91
- Response.new(false, FAILURE_MESSAGE, {:authorization => reference, :error => FAILURE_MESSAGE }, :test => true)
91
+ Response.new(false, FAILURE_MESSAGE, {:authorization => reference, :error => FAILURE_MESSAGE }, :test => true, :error_code => STANDARD_ERROR_CODE[:processing_error])
92
92
  else
93
93
  Response.new(true, SUCCESS_MESSAGE, {:authorization => reference}, :test => true)
94
94
  end
@@ -99,7 +99,7 @@ module ActiveMerchant #:nodoc:
99
99
  when /1$/
100
100
  Response.new(true, SUCCESS_MESSAGE, {:billingid => '1'}, :test => true, :authorization => AUTHORIZATION)
101
101
  when /2$/
102
- Response.new(false, FAILURE_MESSAGE, {:billingid => nil, :error => FAILURE_MESSAGE }, :test => true)
102
+ Response.new(false, FAILURE_MESSAGE, {:billingid => nil, :error => FAILURE_MESSAGE }, :test => true, :error_code => STANDARD_ERROR_CODE[:processing_error])
103
103
  else
104
104
  raise Error, error_message(paysource)
105
105
  end
@@ -110,7 +110,7 @@ module ActiveMerchant #:nodoc:
110
110
  when /1$/
111
111
  Response.new(true, SUCCESS_MESSAGE, {}, :test => true)
112
112
  when /2$/
113
- Response.new(false, FAILURE_MESSAGE, {:error => FAILURE_MESSAGE },:test => true)
113
+ Response.new(false, FAILURE_MESSAGE, {:error => FAILURE_MESSAGE },:test => true, :error_code => STANDARD_ERROR_CODE[:processing_error])
114
114
  else
115
115
  raise Error, UNSTORE_ERROR_MESSAGE
116
116
  end
@@ -7,7 +7,6 @@ module ActiveMerchant #:nodoc:
7
7
 
8
8
  self.test_url = 'https://gatewaytest.borgun.is/ws/Heimir.pub.ws:Authorization'
9
9
  self.live_url = 'https://gateway01.borgun.is/ws/Heimir.pub.ws:Authorization'
10
- self.ssl_version = :SSLv3
11
10
 
12
11
  self.supported_countries = ['IS']
13
12
  self.default_currency = 'ISK'
@@ -500,6 +500,14 @@ module ActiveMerchant #:nodoc:
500
500
  parameters[:billing] = map_address(options[:billing_address]) if options[:billing_address] && !options[:payment_method_token]
501
501
  parameters[:shipping] = map_address(options[:shipping_address]) if options[:shipping_address]
502
502
  parameters[:channel] = application_id if application_id.present? && application_id != "ActiveMerchant"
503
+
504
+ if options[:descriptor_name] || options[:descriptor_phone]
505
+ parameters[:descriptor] = {
506
+ name: options[:descriptor_name],
507
+ phone: options[:descriptor_phone]
508
+ }
509
+ end
510
+
503
511
  parameters
504
512
  end
505
513
  end
@@ -18,7 +18,10 @@ module ActiveMerchant #:nodoc:
18
18
  # * <tt>:password</tt> -- Password (REQUIRED)
19
19
  # * <tt>:merchant_gateway_name</tt> -- Site name (REQUIRED)
20
20
  # * <tt>:station</tt> -- Station (defaults to "WEB")
21
- # * <tt>:default_item_code</tt> -- Item code (defaults to "FEE")
21
+ # * <tt>:custcode</tt> -- Customer code (defaults to
22
+ # "ActiveMerchant/#{ActiveMerchant::VERSION}")
23
+ # * <tt>:default_item_code</tt> -- Default item code (defaults to "FEE",
24
+ # can be overridden on a per-transaction basis with options[:item_code])
22
25
  def initialize(options = {})
23
26
  requires!(
24
27
  options,
@@ -31,18 +34,20 @@ module ActiveMerchant #:nodoc:
31
34
  super
32
35
  end
33
36
 
34
- def purchase(money, payment_object, fields = {})
37
+ def purchase(money, payment_object, options = {})
35
38
  post = {}
36
39
  add_creditcard(post, payment_object)
37
- add_invoice(post, fields)
38
- add_address(post, fields)
39
- add_customer_data(post, fields)
40
+ add_invoice(post, options)
41
+ add_address(post, options)
42
+ add_customer_data(post, options)
40
43
  commit('SALE', money, post)
41
44
  end
42
45
 
43
- def refund(money, identification, fields = {})
44
- fields[:origtx] = identification
45
- commit('REFUND', money, fields)
46
+ def refund(money, identification, options = {})
47
+ post = {}
48
+ post[:origtx] = identification
49
+ add_invoice(post, options)
50
+ commit('REFUND', money, post)
46
51
  end
47
52
 
48
53
  private
@@ -69,8 +74,7 @@ module ActiveMerchant #:nodoc:
69
74
  post[:operator] = @options[:operator]
70
75
  post[:password] = @options[:password]
71
76
  post[:station] = (@options[:station] || "WEB")
72
- post[:itemcode] = (options[:item_code] || @options[:default_item_code])
73
- post[:custcode] = "ActiveMerchant/#{ActiveMerchant::VERSION}"
77
+ post[:custcode] = (@options[:custcode] || "ActiveMerchant/#{ActiveMerchant::VERSION}")
74
78
  post.merge(parameters).collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
75
79
  end
76
80
 
@@ -79,10 +83,13 @@ module ActiveMerchant #:nodoc:
79
83
  post[:cid] = creditcard.verification_value
80
84
  post[:expdate] = expdate(creditcard)
81
85
  post[:card_name_g] = creditcard.name
86
+ post[:fname] = creditcard.first_name
87
+ post[:lname] = creditcard.last_name
82
88
  end
83
89
 
84
90
  def add_invoice(post, options)
85
91
  post[:order_number] = options[:order_id] if options[:order_id].present?
92
+ post[:itemcode] = (options[:item_code] || @options[:default_item_code])
86
93
  end
87
94
 
88
95
  def add_address(post, options)
@@ -0,0 +1,213 @@
1
+ require 'rubygems'
2
+ require 'nokogiri'
3
+
4
+ module ActiveMerchant #:nodoc:
5
+ module Billing #:nodoc:
6
+ class CheckoutGateway < Gateway
7
+ self.default_currency = 'USD'
8
+ self.money_format = :decimals
9
+
10
+ self.supported_countries = ['AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GR', 'HR', 'HU', 'IE', 'IS', 'IT', 'LI', 'LT', 'LU', 'LV', 'MT', 'MU', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK', 'US']
11
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club]
12
+
13
+ self.homepage_url = 'https://www.checkout.com/'
14
+ self.display_name = 'Checkout.com'
15
+
16
+ self.live_url = 'https://api.checkout.com/Process/gateway.aspx'
17
+
18
+ ACTIONS = {
19
+ 'purchase' => '1',
20
+ 'authorize' => '4',
21
+ 'capture' => '5',
22
+ 'refund' => '2',
23
+ 'void_purchase' => '3',
24
+ 'void_authorize' => '9',
25
+ 'void_capture' => '7'
26
+ }
27
+
28
+ def initialize(options = {})
29
+ @url = (options[:gateway_url] || self.live_url)
30
+
31
+ requires!(options, :merchant_id, :password)
32
+ super
33
+ end
34
+
35
+ def purchase(amount, payment_method, options)
36
+ requires!(options, :order_id)
37
+
38
+ commit('purchase', amount, options) do |xml|
39
+ add_credentials(xml, options)
40
+ add_invoice(xml, amount, options)
41
+ add_payment_method(xml, payment_method)
42
+ add_billing_info(xml, options)
43
+ add_shipping_info(xml, options)
44
+ add_user_defined_fields(xml, options)
45
+ add_other_fields(xml, options)
46
+ end
47
+ end
48
+
49
+ def authorize(amount, payment_method, options)
50
+ requires!(options, :order_id)
51
+
52
+ commit('authorize', amount, options) do |xml|
53
+ add_credentials(xml, options)
54
+ add_invoice(xml, amount, options)
55
+ add_payment_method(xml, payment_method)
56
+ add_billing_info(xml, options)
57
+ add_shipping_info(xml, options)
58
+ add_user_defined_fields(xml, options)
59
+ add_other_fields(xml, options)
60
+ end
61
+ end
62
+
63
+ def capture(amount, authorization, options = {})
64
+ commit('capture', amount, options) do |xml|
65
+ add_credentials(xml, options)
66
+ add_reference(xml, authorization)
67
+ add_invoice(xml, amount, options)
68
+ add_user_defined_fields(xml, options)
69
+ add_other_fields(xml, options)
70
+ end
71
+ end
72
+
73
+ def void(authorization, options = {})
74
+ _, _, orig_action, amount, currency = split_authorization(authorization)
75
+ commit("void_#{orig_action}") do |xml|
76
+ add_credentials(xml, options)
77
+ add_invoice(xml, amount.to_i, options.merge(currency: currency))
78
+ add_reference(xml, authorization)
79
+ end
80
+ end
81
+
82
+ def refund(amount, authorization, options = {})
83
+ commit('refund') do |xml|
84
+ add_credentials(xml, options)
85
+ add_invoice(xml, amount.to_i, options)
86
+ add_reference(xml, authorization)
87
+ end
88
+ end
89
+
90
+ def verify(credit_card, options={})
91
+ MultiResponse.run(:use_first_response) do |r|
92
+ r.process { authorize(100, credit_card, options) }
93
+ r.process(:ignore_result) { void(r.authorization, options) }
94
+ end
95
+ end
96
+
97
+ private
98
+
99
+ def add_credentials(xml, options)
100
+ xml.merchantid_ @options[:merchant_id]
101
+ xml.password_ @options[:password]
102
+ end
103
+
104
+ def add_invoice(xml, amount, options)
105
+ xml.bill_amount_ amount(amount)
106
+ xml.bill_currencycode_ options[:currency] || currency(amount)
107
+ xml.trackid_ options[:order_id] if options[:order_id]
108
+ end
109
+
110
+ def add_payment_method(xml, payment_method)
111
+ xml.bill_cardholder_ payment_method.name
112
+ xml.bill_cc_ payment_method.number
113
+ xml.bill_expmonth_ format(payment_method.month, :two_digits)
114
+ xml.bill_expyear_ format(payment_method.year, :four_digits)
115
+ if payment_method.verification_value?
116
+ xml.bill_cvv2_ payment_method.verification_value
117
+ end
118
+ end
119
+
120
+ def add_billing_info(xml, options)
121
+ if options[:billing_address]
122
+ xml.bill_address_ options[:billing_address][:address1]
123
+ xml.bill_city_ options[:billing_address][:city]
124
+ xml.bill_state_ options[:billing_address][:state]
125
+ xml.bill_postal_ options[:billing_address][:zip]
126
+ xml.bill_country_ options[:billing_address][:country]
127
+ xml.bill_phone_ options[:billing_address][:phone]
128
+ end
129
+ end
130
+
131
+ def add_shipping_info(xml, options)
132
+ if options[:shipping_address]
133
+ xml.ship_address_ options[:shipping_address][:address1]
134
+ xml.ship_address2_ options[:shipping_address][:address2]
135
+ xml.ship_city_ options[:shipping_address][:city]
136
+ xml.ship_state_ options[:shipping_address][:state]
137
+ xml.ship_postal_ options[:shipping_address][:zip]
138
+ xml.ship_country_ options[:shipping_address][:country]
139
+ xml.ship_phone_ options[:shipping_address][:phone]
140
+ end
141
+ end
142
+
143
+ def add_user_defined_fields(xml, options)
144
+ xml.udf1_ options[:udf1]
145
+ xml.udf2_ options[:udf2]
146
+ xml.udf3_ options[:udf3]
147
+ xml.udf4_ options[:udf4]
148
+ xml.udf5_ options[:udf5]
149
+ end
150
+
151
+ def add_other_fields(xml, options)
152
+ xml.bill_email_ options[:email]
153
+ xml.bill_customerip_ options[:ip]
154
+ xml.merchantcustomerid_ options[:customer]
155
+ end
156
+
157
+ def add_reference(xml, authorization)
158
+ transid, trackid, _, _, _ = split_authorization(authorization)
159
+ xml.transid transid
160
+ xml.trackid trackid if trackid
161
+ end
162
+
163
+ def commit(action, amount=nil, options={}, &builder)
164
+ response = parse_xml(ssl_post(@url, build_xml(action, &builder)))
165
+ Response.new(
166
+ (response[:responsecode] == "0"),
167
+ (response[:result] || response[:error_text] || "Unknown Response"),
168
+ response,
169
+ authorization: authorization_from(response, action, amount, options),
170
+ test: test?
171
+ )
172
+ end
173
+
174
+ def build_xml(action)
175
+ Nokogiri::XML::Builder.new do |xml|
176
+ xml.request do
177
+ xml.action_ ACTIONS[action]
178
+ yield xml
179
+ end
180
+ end.to_xml
181
+ end
182
+
183
+ def parse_xml(xml)
184
+ response = {}
185
+
186
+ Nokogiri::XML(CGI.unescapeHTML(xml)).xpath("//response").children.each do |node|
187
+ if node.text?
188
+ next
189
+ elsif (node.elements.size == 0)
190
+ response[node.name.downcase.to_sym] = node.text
191
+ else
192
+ node.elements.each do |childnode|
193
+ name = "#{node.name.downcase}_#{childnode.name.downcase}"
194
+ response[name.to_sym] = childnode.text
195
+ end
196
+ end
197
+ end
198
+
199
+ response
200
+ end
201
+
202
+ def authorization_from(response, action, amount, options)
203
+ currency = options[:currency] || currency(amount)
204
+ [response[:tranid], response[:trackid], action, amount, currency].join("|")
205
+ end
206
+
207
+ def split_authorization(authorization)
208
+ transid, trackid, action, amount, currency = authorization.split("|")
209
+ [transid, trackid, action, amount, currency]
210
+ end
211
+ end
212
+ end
213
+ end