activemerchant 1.32.1 → 1.33.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +41 -0
  3. data/CONTRIBUTORS +8 -0
  4. data/README.md +6 -4
  5. data/lib/active_merchant/billing/check.rb +4 -3
  6. data/lib/active_merchant/billing/credit_card.rb +7 -3
  7. data/lib/active_merchant/billing/gateways/authorize_net.rb +27 -7
  8. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +8 -1
  9. data/lib/active_merchant/billing/gateways/blue_pay.rb +201 -185
  10. data/lib/active_merchant/billing/gateways/bogus.rb +1 -1
  11. data/lib/active_merchant/billing/gateways/card_stream_modern.rb +155 -0
  12. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +94 -12
  13. data/lib/active_merchant/billing/gateways/litle.rb +41 -11
  14. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +27 -6
  15. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +2 -2
  16. data/lib/active_merchant/billing/gateways/net_registry.rb +8 -3
  17. data/lib/active_merchant/billing/gateways/netaxept.rb +65 -117
  18. data/lib/active_merchant/billing/gateways/orbital.rb +181 -48
  19. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +12 -10
  20. data/lib/active_merchant/billing/gateways/paymill.rb +5 -5
  21. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +11 -6
  22. data/lib/active_merchant/billing/gateways/paypal_express.rb +25 -7
  23. data/lib/active_merchant/billing/gateways/pin.rb +5 -5
  24. data/lib/active_merchant/billing/gateways/sage.rb +10 -5
  25. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +16 -11
  26. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +1 -1
  27. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +21 -16
  28. data/lib/active_merchant/billing/gateways/sage_pay.rb +1 -0
  29. data/lib/active_merchant/billing/gateways/transnational.rb +239 -0
  30. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +8 -3
  31. data/lib/active_merchant/billing/integrations/direc_pay.rb +1 -1
  32. data/lib/active_merchant/billing/integrations/direc_pay/status.rb +1 -1
  33. data/lib/active_merchant/billing/integrations/dwolla.rb +5 -12
  34. data/lib/active_merchant/billing/integrations/dwolla/common.rb +21 -0
  35. data/lib/active_merchant/billing/integrations/dwolla/helper.rb +15 -6
  36. data/lib/active_merchant/billing/integrations/dwolla/notification.rb +11 -6
  37. data/lib/active_merchant/billing/integrations/dwolla/return.rb +12 -4
  38. data/lib/active_merchant/billing/integrations/notification.rb +13 -8
  39. data/lib/active_merchant/billing/integrations/payflow_link/helper.rb +13 -1
  40. data/lib/active_merchant/billing/integrations/payu_in.rb +43 -0
  41. data/lib/active_merchant/billing/integrations/payu_in/helper.rb +74 -0
  42. data/lib/active_merchant/billing/integrations/payu_in/notification.rb +167 -0
  43. data/lib/active_merchant/billing/integrations/payu_in/return.rb +53 -0
  44. data/lib/active_merchant/billing/integrations/quickpay/notification.rb +68 -5
  45. data/lib/active_merchant/billing/integrations/rbkmoney.rb +17 -0
  46. data/lib/active_merchant/billing/integrations/rbkmoney/helper.rb +23 -0
  47. data/lib/active_merchant/billing/integrations/rbkmoney/notification.rb +91 -0
  48. data/lib/active_merchant/version.rb +1 -1
  49. metadata +14 -4
  50. metadata.gz.sig +0 -0
@@ -1,3 +1,4 @@
1
+ require 'nokogiri'
1
2
  module ActiveMerchant #:nodoc:
2
3
  module Billing #:nodoc:
3
4
  module PayflowCommonAPI
@@ -140,17 +141,18 @@ module ActiveMerchant #:nodoc:
140
141
 
141
142
  def parse(data)
142
143
  response = {}
143
- xml = REXML::Document.new(data)
144
- root = REXML::XPath.first(xml, "//ResponseData")
144
+ xml = Nokogiri::XML(data)
145
+ xml.remove_namespaces!
146
+ root = xml.xpath("//ResponseData")
145
147
 
146
148
  # REXML::XPath in Ruby 1.8.6 is now unable to match nodes based on their attributes
147
- tx_result = REXML::XPath.first(root, "//TransactionResult")
149
+ tx_result = root.xpath(".//TransactionResult").first
148
150
 
149
- if tx_result && tx_result.attributes['Duplicate'] == "true"
151
+ if tx_result && tx_result.attributes['Duplicate'].to_s == "true"
150
152
  response[:duplicate] = true
151
153
  end
152
154
 
153
- root.elements.to_a.each do |node|
155
+ root.xpath(".//*").each do |node|
154
156
  parse_element(response, node)
155
157
  end
156
158
 
@@ -166,14 +168,14 @@ module ActiveMerchant #:nodoc:
166
168
  # in an RPPaymentResults element so we'll come here multiple times
167
169
  response[node_name] ||= []
168
170
  response[node_name] << ( payment_result_response = {} )
169
- node.elements.each{ |e| parse_element(payment_result_response, e) }
170
- when node.has_elements?
171
- node.elements.each{|e| parse_element(response, e) }
171
+ node.xpath(".//*").each{ |e| parse_element(payment_result_response, e) }
172
+ when node.xpath(".//*").to_a.any?
173
+ node.xpath(".//*").each{|e| parse_element(response, e) }
172
174
  when node_name.to_s =~ /amt$/
173
175
  # *Amt elements don't put the value in the #text - instead they use a Currency attribute
174
- response[node_name] = node.attributes['Currency']
176
+ response[node_name] = node.attributes['Currency'].to_s
175
177
  when node_name == :ext_data
176
- response[node.attributes['Name'].underscore.to_sym] = node.attributes['Value']
178
+ response[node.attributes['Name'].to_s.underscore.to_sym] = node.attributes['Value'].to_s
177
179
  else
178
180
  response[node_name] = node.text
179
181
  end
@@ -1,13 +1,13 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  class PaymillGateway < Gateway
4
- self.supported_countries = %w(AD AT BE CY CZ DE DK EE ES FI FO FR GB GR
5
- HU IE IL IS IT LI LT LU LV MT NL NO PL PT
6
- SE SI SK TR VA)
4
+ self.supported_countries = %w(AD AT BE CH CY CZ DE DK EE ES FI FO FR GB GR
5
+ HU IE IL IS IT LI LT LU LV MT NL NO PL PT SE
6
+ SI SK TR VA)
7
7
 
8
8
  self.supported_cardtypes = [:visa, :master]
9
9
  self.homepage_url = 'https://paymill.com'
10
- self.display_name = 'Paymill'
10
+ self.display_name = 'PAYMILL'
11
11
  self.money_format = :cents
12
12
  self.default_currency = 'EUR'
13
13
 
@@ -154,7 +154,7 @@ module ActiveMerchant #:nodoc:
154
154
  end
155
155
 
156
156
  def save_card_url
157
- (test? ? 'https://test-token.paymill.com' : 'https://token-v2.paymill.com')
157
+ (test? ? 'https://test-token.paymill.com' : 'https://token-v2.paymill.de')
158
158
  end
159
159
 
160
160
  def post_data(params)
@@ -617,11 +617,11 @@ module ActiveMerchant #:nodoc:
617
617
  response = parse(action, ssl_post(endpoint_url, build_request(request), @options[:headers]))
618
618
 
619
619
  build_response(successful?(response), message_from(response), response,
620
- :test => test?,
621
- :authorization => authorization_from(response),
622
- :fraud_review => fraud_review?(response),
623
- :avs_result => { :code => response[:avs_code] },
624
- :cvv_result => response[:cvv2_code]
620
+ :test => test?,
621
+ :authorization => authorization_from(response),
622
+ :fraud_review => fraud_review?(response),
623
+ :avs_result => { :code => response[:avs_code] },
624
+ :cvv_result => response[:cvv2_code]
625
625
  )
626
626
  end
627
627
 
@@ -630,7 +630,12 @@ module ActiveMerchant #:nodoc:
630
630
  end
631
631
 
632
632
  def authorization_from(response)
633
- response[:transaction_id] || response[:authorization_id] || response[:refund_transaction_id] # middle one is from reauthorization
633
+ (
634
+ response[:transaction_id] ||
635
+ response[:authorization_id] ||
636
+ response[:refund_transaction_id] ||
637
+ response[:billing_agreement_id]
638
+ )
634
639
  end
635
640
 
636
641
  def successful?(response)
@@ -59,9 +59,19 @@ module ActiveMerchant #:nodoc:
59
59
  commit 'DoExpressCheckoutPayment', build_sale_or_authorization_request('Sale', money, options)
60
60
  end
61
61
 
62
- def reference_transaction(money, options = {})
62
+ def store(token, options = {})
63
+ commit 'CreateBillingAgreement', build_create_billing_agreement_request(token, options)
64
+ end
65
+
66
+ def authorize_reference_transaction(money, options = {})
63
67
  requires!(options, :reference_id, :payment_type, :invoice_id, :description, :ip)
64
68
 
69
+ commit 'DoReferenceTransaction', build_reference_transaction_request('Authorization', money, options)
70
+ end
71
+
72
+ def reference_transaction(money, options = {})
73
+ requires!(options, :reference_id)
74
+
65
75
  commit 'DoReferenceTransaction', build_reference_transaction_request('Sale', money, options)
66
76
  end
67
77
 
@@ -142,7 +152,7 @@ module ActiveMerchant #:nodoc:
142
152
  end
143
153
  xml.tag! 'n2:CallbackURL', options[:callback_url] unless options[:callback_url].blank?
144
154
 
145
- add_payment_details(xml, with_money_default(money), currency_code, options)
155
+ add_payment_details(xml, money, currency_code, options)
146
156
  if options[:shipping_options]
147
157
  options[:shipping_options].each do |shipping_option|
148
158
  xml.tag! 'n2:FlatRateShippingOptions' do
@@ -166,6 +176,18 @@ module ActiveMerchant #:nodoc:
166
176
  xml.target!
167
177
  end
168
178
 
179
+ def build_create_billing_agreement_request(token, options = {})
180
+ xml = Builder::XmlMarkup.new :indent => 2
181
+ xml.tag! 'CreateBillingAgreementReq', 'xmlns' => PAYPAL_NAMESPACE do
182
+ xml.tag! 'CreateBillingAgreementRequest', 'xmlns:n2' => EBAY_NAMESPACE do
183
+ xml.tag! 'n2:Version', API_VERSION
184
+ xml.tag! 'Token', token
185
+ end
186
+ end
187
+
188
+ xml.target!
189
+ end
190
+
169
191
  def build_reference_transaction_request(action, money, options)
170
192
  currency_code = options[:currency] || currency(money)
171
193
 
@@ -179,7 +201,7 @@ module ActiveMerchant #:nodoc:
179
201
  xml.tag! 'n2:ReferenceID', options[:reference_id]
180
202
  xml.tag! 'n2:PaymentAction', action
181
203
  xml.tag! 'n2:PaymentType', options[:payment_type] || 'Any'
182
- add_payment_details(xml, with_money_default(money), currency_code, options)
204
+ add_payment_details(xml, money, currency_code, options)
183
205
  xml.tag! 'n2:IPAddress', options[:ip]
184
206
  end
185
207
  end
@@ -192,10 +214,6 @@ module ActiveMerchant #:nodoc:
192
214
  PaypalExpressResponse.new(success, message, response, options)
193
215
  end
194
216
 
195
- def with_money_default(money)
196
- amount(money).to_f.zero? ? 100 : money
197
- end
198
-
199
217
  def locale_code(country_code)
200
218
  NON_STANDARD_LOCALE_CODES[country_code] || country_code
201
219
  end
@@ -29,7 +29,7 @@ module ActiveMerchant #:nodoc:
29
29
  add_creditcard(post, creditcard)
30
30
  add_address(post, creditcard, options)
31
31
 
32
- commit('charges', post)
32
+ commit('charges', post, options)
33
33
  end
34
34
 
35
35
  # Create a customer and associated credit card. The token that is returned
@@ -40,14 +40,14 @@ module ActiveMerchant #:nodoc:
40
40
  add_creditcard(post, creditcard)
41
41
  add_customer_data(post, options)
42
42
  add_address(post, creditcard, options)
43
- commit('customers', post)
43
+ commit('customers', post, options)
44
44
  end
45
45
 
46
46
  # Refund a transaction, note that the money attribute is ignored at the
47
47
  # moment as the API does not support partial refunds. The parameter is
48
48
  # kept for compatibility reasons
49
49
  def refund(money, token, options = {})
50
- commit("charges/#{CGI.escape(token)}/refunds", :amount => amount(money))
50
+ commit("charges/#{CGI.escape(token)}/refunds", { :amount => amount(money) }, options)
51
51
  end
52
52
 
53
53
  private
@@ -112,11 +112,11 @@ module ActiveMerchant #:nodoc:
112
112
  result
113
113
  end
114
114
 
115
- def commit(action, params)
115
+ def commit(action, params, options)
116
116
  url = "#{test? ? test_url : live_url}/#{action}"
117
117
 
118
118
  begin
119
- body = parse(ssl_post(url, post_data(params), headers(params)))
119
+ body = parse(ssl_post(url, post_data(params), headers(options)))
120
120
  rescue ResponseError => e
121
121
  body = parse(e.response.body)
122
122
  end
@@ -119,17 +119,22 @@ module ActiveMerchant #:nodoc:
119
119
  end
120
120
  end
121
121
 
122
- # Performs a credit transaction. (Sage +Credit+ transaction).
122
+ def credit(money, source, options = {})
123
+ deprecated CREDIT_DEPRECATION_MESSAGE
124
+ refund(money, source, options)
125
+ end
126
+
127
+ # Performs a refund transaction.
123
128
  #
124
129
  # ==== Parameters
125
130
  #
126
131
  # * <tt>money</tt> - The amount to be authorized as an integer value in cents.
127
- # * <tt>source</tt> - The CreditCard or Check object to be used as the target for the credit.
128
- def credit(money, source, options = {})
132
+ # * <tt>source</tt> - The CreditCard or Check object to be used as the target for the refund.
133
+ def refund(money, source, options = {})
129
134
  if card_brand(source) == "check"
130
- virtual_check.credit(money, source, options)
135
+ virtual_check.refund(money, source, options)
131
136
  else
132
- bankcard.credit(money, source, options)
137
+ bankcard.refund(money, source, options)
133
138
  end
134
139
  end
135
140
 
@@ -6,7 +6,7 @@ module ActiveMerchant #:nodoc:
6
6
  include SageCore
7
7
  self.live_url = 'https://www.sagepayments.net/cgi-bin/eftBankcard.dll?transaction'
8
8
  self.source = 'bankcard'
9
-
9
+
10
10
  # Credit cards supported by Sage
11
11
  # * VISA
12
12
  # * MasterCard
@@ -17,42 +17,47 @@ module ActiveMerchant #:nodoc:
17
17
  # * JCB
18
18
  # * Sears
19
19
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club]
20
-
20
+
21
21
  def authorize(money, credit_card, options = {})
22
22
  post = {}
23
23
  add_credit_card(post, credit_card)
24
24
  add_transaction_data(post, money, options)
25
25
  commit(:authorization, post)
26
26
  end
27
-
27
+
28
28
  def purchase(money, credit_card, options = {})
29
29
  post = {}
30
30
  add_credit_card(post, credit_card)
31
31
  add_transaction_data(post, money, options)
32
32
  commit(:purchase, post)
33
- end
34
-
35
- # The +money+ amount is not used. The entire amount of the
33
+ end
34
+
35
+ # The +money+ amount is not used. The entire amount of the
36
36
  # initial authorization will be captured.
37
37
  def capture(money, reference, options = {})
38
38
  post = {}
39
39
  add_reference(post, reference)
40
40
  commit(:capture, post)
41
41
  end
42
-
42
+
43
43
  def void(reference, options = {})
44
44
  post = {}
45
45
  add_reference(post, reference)
46
46
  commit(:void, post)
47
47
  end
48
-
48
+
49
49
  def credit(money, credit_card, options = {})
50
+ deprecated CREDIT_DEPRECATION_MESSAGE
51
+ refund(money, credit_card, options)
52
+ end
53
+
54
+ def refund(money, credit_card, options = {})
50
55
  post = {}
51
56
  add_credit_card(post, credit_card)
52
57
  add_transaction_data(post, money, options)
53
58
  commit(:credit, post)
54
59
  end
55
-
60
+
56
61
  private
57
62
  def exp_date(credit_card)
58
63
  year = sprintf("%.4i", credit_card.year)
@@ -67,7 +72,7 @@ module ActiveMerchant #:nodoc:
67
72
  post[:C_exp] = exp_date(credit_card)
68
73
  post[:C_cvv] = credit_card.verification_value if credit_card.verification_value?
69
74
  end
70
-
75
+
71
76
  def parse(data)
72
77
  response = {}
73
78
  response[:success] = data[1,1]
@@ -78,7 +83,7 @@ module ActiveMerchant #:nodoc:
78
83
  response[:avs_result] = data[43, 1].strip
79
84
  response[:risk] = data[44, 2]
80
85
  response[:reference] = data[46, 10]
81
-
86
+
82
87
  response[:order_number], response[:recurring] = data[57...-1].split("\034")
83
88
  response
84
89
  end
@@ -45,7 +45,7 @@ module ActiveMerchant #:nodoc:
45
45
  end
46
46
 
47
47
  def add_customer_data(post, options)
48
- post[:T_customer_number] = options[:customer]
48
+ post[:T_customer_number] = options[:customer] if Float(options[:customer]) rescue nil
49
49
  end
50
50
 
51
51
  def add_addresses(post, options)
@@ -6,7 +6,7 @@ module ActiveMerchant #:nodoc:
6
6
  include SageCore
7
7
  self.live_url = 'https://www.sagepayments.net/cgi-bin/eftVirtualCheck.dll?transaction'
8
8
  self.source = 'virtual_check'
9
-
9
+
10
10
  def purchase(money, credit_card, options = {})
11
11
  post = {}
12
12
  add_check(post, credit_card)
@@ -14,21 +14,26 @@ module ActiveMerchant #:nodoc:
14
14
  add_transaction_data(post, money, options)
15
15
  commit(:purchase, post)
16
16
  end
17
-
17
+
18
18
  def void(reference, options = {})
19
19
  post = {}
20
20
  add_reference(post, reference)
21
21
  commit(:void, post)
22
22
  end
23
-
23
+
24
24
  def credit(money, credit_card, options = {})
25
+ deprecated CREDIT_DEPRECATION_MESSAGE
26
+ refund(money, source, options)
27
+ end
28
+
29
+ def refund(money, credit_card, options = {})
25
30
  post = {}
26
31
  add_check(post, credit_card)
27
32
  add_check_customer_data(post, options)
28
33
  add_transaction_data(post, money, options)
29
34
  commit(:credit, post)
30
35
  end
31
-
36
+
32
37
  private
33
38
  def add_check(post, check)
34
39
  post[:C_first_name] = check.first_name
@@ -38,37 +43,37 @@ module ActiveMerchant #:nodoc:
38
43
  post[:C_check_number] = check.number
39
44
  post[:C_acct_type] = account_type(check)
40
45
  end
41
-
46
+
42
47
  def add_check_customer_data(post, options)
43
48
  # Required  Customer Type – (NACHA Transaction Class)
44
- # CCD for Commercial, Merchant Initiated
49
+ # CCD for Commercial, Merchant Initiated
45
50
  # PPD for Personal, Merchant Initiated
46
- # WEB for Internet, Consumer Initiated
47
- # RCK for Returned Checks
48
- # ARC for Account Receivable Entry
51
+ # WEB for Internet, Consumer Initiated
52
+ # RCK for Returned Checks
53
+ # ARC for Account Receivable Entry
49
54
  # TEL for TelephoneInitiated
50
55
  post[:C_customer_type] = "WEB"
51
56
 
52
- # Optional  10  Digit Originator  ID – Assigned  By for  each transaction  class  or  business  purpose. If  not provided, the default Originator ID for the specific  Customer Type will be applied. 
57
+ # Optional  10  Digit Originator  ID – Assigned  By for  each transaction  class  or  business  purpose. If  not provided, the default Originator ID for the specific  Customer Type will be applied. 
53
58
  post[:C_originator_id] = options[:originator_id]
54
59
 
55
60
  # Optional  Transaction Addenda
56
61
  post[:T_addenda] = options[:addenda]
57
62
 
58
- # Required  Check  Writer  Social  Security  Number  (  Numbers Only, No Dashes ) 
63
+ # Required  Check  Writer  Social  Security  Number  (  Numbers Only, No Dashes ) 
59
64
  post[:C_ssn] = options[:ssn].to_s.gsub(/[^\d]/, '')
60
65
 
61
66
  post[:C_dl_state_code] = options[:drivers_license_state]
62
67
  post[:C_dl_number] = options[:drivers_license_number]
63
68
  post[:C_dob] = format_birth_date(options[:date_of_birth])
64
69
  end
65
-
70
+
66
71
  def format_birth_date(date)
67
72
  date.respond_to?(:strftime) ? date.strftime("%m/%d/%Y") : date
68
73
  end
69
74
 
70
- # DDA for Checking
71
- # SAV for Savings 
75
+ # DDA for Checking
76
+ # SAV for Savings 
72
77
  def account_type(check)
73
78
  case check.account_type
74
79
  when 'checking' then 'DDA'
@@ -76,7 +81,7 @@ module ActiveMerchant #:nodoc:
76
81
  else raise ArgumentError, "Unknown account type #{check.account_type}"
77
82
  end
78
83
  end
79
-
84
+
80
85
  def parse(data)
81
86
  response = {}
82
87
  response[:success] = data[1,1]
@@ -84,7 +89,7 @@ module ActiveMerchant #:nodoc:
84
89
  response[:message] = data[8,32].strip
85
90
  response[:risk] = data[40, 2]
86
91
  response[:reference] = data[42, 10]
87
-
92
+
88
93
  extra_data = data[53...-1].split("\034")
89
94
  response[:order_number] = extra_data[0]
90
95
  response[:authentication_indicator] = extra_data[1]
@@ -160,6 +160,7 @@ module ActiveMerchant #:nodoc:
160
160
 
161
161
  def add_optional_data(post, options)
162
162
  add_pair(post, :GiftAidPayment, options[:gift_aid_payment]) unless options[:gift_aid_payment].blank?
163
+ add_pair(post, :Apply3DSecure, options[:apply_3d_secure]) unless options[:apply_3d_secure].blank?
163
164
  end
164
165
 
165
166
  def add_address(post, options)
@@ -0,0 +1,239 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class TransnationalGateway < Gateway
4
+ self.live_url = self.test_url = 'https://secure.networkmerchants.com/api/transact.php'
5
+
6
+ self.supported_countries = ['US']
7
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
8
+
9
+ self.homepage_url = 'http://www.tnbci.com/'
10
+ self.display_name = 'Transnational'
11
+
12
+ self.money_format = :dollars
13
+ self.default_currency = 'USD'
14
+
15
+ def initialize(options = {})
16
+ requires!(options, :login, :password)
17
+ super
18
+ end
19
+
20
+ def authorize(money, creditcard_or_vault_id, options = {})
21
+ post = build_auth_post(money, creditcard_or_vault_id, options)
22
+ commit('auth', post)
23
+ end
24
+
25
+ def purchase(money, creditcard_or_vault_id, options = {})
26
+ post = build_purchase_post(money, creditcard_or_vault_id, options)
27
+ commit('sale', post)
28
+ end
29
+
30
+ def capture(money, authorization, options = {})
31
+ post = build_capture_post(money, authorization, options)
32
+ commit('capture', post)
33
+ end
34
+
35
+ def void(authorization, options = {})
36
+ post = build_void_post(authorization, options)
37
+ commit('void', post)
38
+ end
39
+
40
+ def refund(money, authorization, options = {})
41
+ post = build_refund_post(money, authorization, options)
42
+ commit('refund', post)
43
+ end
44
+
45
+ def store(creditcard, options = {})
46
+ post = build_store_post(creditcard, options)
47
+ commit_vault('add_customer', post)
48
+ end
49
+
50
+ def unstore(customer_vault_id, options = {})
51
+ post = build_unstore_post(customer_vault_id, options)
52
+ commit_vault('delete_customer', post)
53
+ end
54
+
55
+ private
56
+
57
+ def build_auth_post(money, creditcard_or_vault_id, options)
58
+ post = {}
59
+ add_order(post, options)
60
+ add_address(post, options)
61
+ add_shipping_address(post, options)
62
+ add_payment_method(post, creditcard_or_vault_id, options)
63
+ add_amount(post, money)
64
+ post
65
+ end
66
+
67
+ def build_purchase_post(money, creditcard, options)
68
+ build_auth_post(money, creditcard, options)
69
+ end
70
+
71
+ def build_capture_post(money, authorization, option)
72
+ post = {}
73
+ post[:transactionid] = authorization
74
+ add_amount(post, money)
75
+ post
76
+ end
77
+
78
+ def build_void_post(authorization, options)
79
+ post = {}
80
+ post[:transactionid] = authorization
81
+ post
82
+ end
83
+
84
+ def build_refund_post(money, authorization, options)
85
+ post = {}
86
+ post[:transactionid] = authorization
87
+ add_amount(post, money)
88
+ post
89
+ end
90
+
91
+ def build_store_post(creditcard_or_check, options)
92
+ post = {}
93
+ add_address(post, options)
94
+ add_shipping_address(post, options)
95
+ add_payment_method(post, creditcard_or_check, options)
96
+ post
97
+ end
98
+
99
+ def build_unstore_post(customer_vault_id, options)
100
+ post = {}
101
+ post['customer_vault_id'] = customer_vault_id
102
+ post
103
+ end
104
+
105
+ def add_order(post, options)
106
+ post[:orderid] = options[:order_id]
107
+ post[:orderdescription] = options[:description]
108
+ end
109
+
110
+ def add_address(post, options)
111
+ post[:email] = options[:email]
112
+ post[:ipaddress] = options[:ip]
113
+
114
+ address = options[:billing_address] || options[:address] || {}
115
+ post[:address1] = address[:address1]
116
+ post[:address2] = address[:address2]
117
+ post[:city] = address[:city]
118
+ post[:state] = address[:state]
119
+ post[:zip] = address[:zip]
120
+ post[:country] = address[:country]
121
+ post[:phone] = address[:phone]
122
+ end
123
+
124
+ def add_shipping_address(post, options)
125
+ shipping_address = options[:shipping_address] || {}
126
+ post[:shipping_address1] = shipping_address[:address1]
127
+ post[:shipping_address2] = shipping_address[:address2]
128
+ post[:shipping_city] = shipping_address[:city]
129
+ post[:shipping_state] = shipping_address[:state]
130
+ post[:shipping_zip] = shipping_address[:zip]
131
+ post[:shipping_country] = shipping_address[:country]
132
+ end
133
+
134
+ def add_swipe_data(post, options)
135
+ # unencrypted tracks
136
+ post[:track_1] = options[:track_1]
137
+ post[:track_2] = options[:track_2]
138
+ post[:track_3] = options[:track_3]
139
+
140
+ # encrypted tracks
141
+ post[:magnesafe_track_1] = options[:magnesafe_track_1]
142
+ post[:magnesafe_track_2] = options[:magnesafe_track_2]
143
+ post[:magnesafe_track_3] = options[:magnesafe_track_3]
144
+ post[:magnesafe_magneprint] = options[:magnesafe_magneprint]
145
+ post[:magnesafe_ksn] = options[:magnesafe_ksn]
146
+ post[:magnesafe_magneprint_status] = options[:magnesafe_magneprint_status]
147
+ end
148
+
149
+ def add_payment_method(post, creditcard_or_check_or_vault_id, options)
150
+ post[:processor_id] = options[:processor_id]
151
+ post[:customer_vault] = 'add_customer' if options[:store]
152
+
153
+ add_swipe_data(post, options)
154
+
155
+ # creditcard_or_check can be blank if using swipe data
156
+ if creditcard_or_check_or_vault_id.is_a?(CreditCard) # creditcard or check
157
+ creditcard = creditcard_or_check_or_vault_id
158
+ post[:firstname] = creditcard.first_name
159
+ post[:lastname] = creditcard.last_name
160
+ post[:ccnumber] = creditcard.number
161
+ post[:ccexp] = format(creditcard.month, :two_digits) + format(creditcard.year, :two_digits)
162
+ post[:cvv] = creditcard.verification_value
163
+ post[:payment] = 'creditcard'
164
+ elsif creditcard_or_check_or_vault_id.is_a?(Check)
165
+ check = creditcard_or_check_or_vault_id
166
+ post[:firstname] = check.first_name
167
+ post[:lastname] = check.last_name
168
+ post[:checkname] = check.name
169
+ post[:checkaba] = check.routing_number
170
+ post[:checkaccount] = check.account_number
171
+ post[:account_type] = check.account_type
172
+ post[:account_holder_type] = check.account_holder_type
173
+ post[:payment] = 'check'
174
+ else
175
+ post[:customer_vault_id] = creditcard_or_check_or_vault_id
176
+ end
177
+ end
178
+
179
+ def add_login(post)
180
+ post[:username] = @options[:login]
181
+ post[:password] = @options[:password]
182
+ end
183
+
184
+ def add_amount(post, money)
185
+ post[:currency] = options[:currency] || currency(money)
186
+ post[:amount] = amount(money)
187
+ end
188
+
189
+ def commit_vault(action, parameters)
190
+ commit(nil, parameters.merge(:customer_vault => action))
191
+ end
192
+
193
+ def commit(action, parameters)
194
+ raw = parse(ssl_post(self.live_url, build_request(action, parameters)))
195
+
196
+ success = (raw['response'] == ResponseCodes::APPROVED)
197
+
198
+ authorization = authorization_from(success, parameters, raw)
199
+
200
+ Response.new(success, raw['responsetext'], raw,
201
+ :test => test?,
202
+ :authorization => authorization,
203
+ :avs_result => { :code => raw['avsresponse']},
204
+ :cvv_result => raw['cvvresponse']
205
+ )
206
+ end
207
+
208
+ def build_request(action, parameters)
209
+ parameters[:type] = action if action
210
+ add_login(parameters)
211
+ parameters.to_query
212
+ end
213
+
214
+ def authorization_from(success, parameters, response)
215
+ return nil unless success
216
+
217
+ authorization = response['transactionid']
218
+ if(parameters[:customer_vault] && (authorization.nil? || authorization.empty?))
219
+ authorization = response['customer_vault_id']
220
+ end
221
+
222
+ authorization
223
+ end
224
+
225
+ class ResponseCodes
226
+ APPROVED = '1'
227
+ DENIED = '2'
228
+ ERROR = '3'
229
+ end
230
+
231
+ def parse(raw_response)
232
+ rsp = CGI.parse(raw_response)
233
+ rsp.keys.each { |k| rsp[k] = rsp[k].first } # flatten out the values
234
+ rsp
235
+ end
236
+ end
237
+ end
238
+ end
239
+