tlconnor-activemerchant 1.20.4 → 1.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/CHANGELOG +86 -6
  2. data/CONTRIBUTORS +33 -0
  3. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +2 -0
  4. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +4 -4
  5. data/lib/active_merchant/billing/gateways/blue_pay.rb +492 -11
  6. data/lib/active_merchant/billing/gateways/braintree_blue.rb +46 -19
  7. data/lib/active_merchant/billing/gateways/certo_direct.rb +1 -1
  8. data/lib/active_merchant/billing/gateways/elavon.rb +2 -0
  9. data/lib/active_merchant/billing/gateways/epay.rb +3 -1
  10. data/lib/active_merchant/billing/gateways/itransact.rb +450 -0
  11. data/lib/active_merchant/billing/gateways/litle.rb +275 -0
  12. data/lib/active_merchant/billing/gateways/migs.rb +259 -0
  13. data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +100 -0
  14. data/lib/active_merchant/billing/gateways/moneris.rb +4 -30
  15. data/lib/active_merchant/billing/gateways/moneris_us.rb +211 -0
  16. data/lib/active_merchant/billing/gateways/nab_transact.rb +1 -1
  17. data/lib/active_merchant/billing/gateways/ogone.rb +104 -12
  18. data/lib/active_merchant/billing/gateways/orbital.rb +15 -6
  19. data/lib/active_merchant/billing/gateways/paybox_direct.rb +1 -4
  20. data/lib/active_merchant/billing/gateways/payflow.rb +8 -3
  21. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +4 -1
  22. data/lib/active_merchant/billing/gateways/payflow_express.rb +4 -2
  23. data/lib/active_merchant/billing/gateways/payment_express.rb +60 -13
  24. data/lib/active_merchant/billing/gateways/paypal.rb +3 -18
  25. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +333 -3
  26. data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +245 -0
  27. data/lib/active_merchant/billing/gateways/paypal_digital_goods.rb +43 -0
  28. data/lib/active_merchant/billing/gateways/paypal_express.rb +14 -65
  29. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +8 -3
  30. data/lib/active_merchant/billing/gateways/realex.rb +5 -7
  31. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +3 -2
  32. data/lib/active_merchant/billing/gateways/stripe.rb +1 -9
  33. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +2 -2
  34. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +1 -5
  35. data/lib/active_merchant/billing/gateways/viaklix.rb +7 -2
  36. data/lib/active_merchant/billing/gateways/vindicia.rb +359 -0
  37. data/lib/active_merchant/billing/integrations/dotpay.rb +22 -0
  38. data/lib/active_merchant/billing/integrations/dotpay/helper.rb +77 -0
  39. data/lib/active_merchant/billing/integrations/dotpay/notification.rb +86 -0
  40. data/lib/active_merchant/billing/integrations/dotpay/return.rb +11 -0
  41. data/lib/active_merchant/billing/integrations/epay.rb +21 -0
  42. data/lib/active_merchant/billing/integrations/epay/helper.rb +55 -0
  43. data/lib/active_merchant/billing/integrations/epay/notification.rb +110 -0
  44. data/lib/active_merchant/billing/integrations/paypal/notification.rb +2 -1
  45. data/lib/active_merchant/billing/integrations/quickpay/helper.rb +2 -3
  46. data/lib/active_merchant/billing/integrations/robokassa.rb +49 -0
  47. data/lib/active_merchant/billing/integrations/robokassa/common.rb +19 -0
  48. data/lib/active_merchant/billing/integrations/robokassa/helper.rb +50 -0
  49. data/lib/active_merchant/billing/integrations/robokassa/notification.rb +55 -0
  50. data/lib/active_merchant/billing/integrations/robokassa/return.rb +17 -0
  51. data/lib/active_merchant/billing/integrations/two_checkout.rb +25 -3
  52. data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +58 -26
  53. data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +71 -46
  54. data/lib/active_merchant/billing/integrations/verkkomaksut.rb +20 -0
  55. data/lib/active_merchant/billing/integrations/verkkomaksut/helper.rb +87 -0
  56. data/lib/active_merchant/billing/integrations/verkkomaksut/notification.rb +59 -0
  57. data/lib/active_merchant/version.rb +1 -1
  58. metadata +28 -5
@@ -74,7 +74,7 @@ module ActiveMerchant #:nodoc:
74
74
 
75
75
  def store(creditcard, options = {})
76
76
  commit do
77
- result = Braintree::Customer.create(
77
+ parameters = {
78
78
  :first_name => creditcard.first_name,
79
79
  :last_name => creditcard.last_name,
80
80
  :email => options[:email],
@@ -84,7 +84,8 @@ module ActiveMerchant #:nodoc:
84
84
  :expiration_month => creditcard.month.to_s.rjust(2, "0"),
85
85
  :expiration_year => creditcard.year.to_s
86
86
  }
87
- )
87
+ }
88
+ result = Braintree::Customer.create(merge_credit_card_options(parameters, options))
88
89
  Response.new(result.success?, message_from_result(result),
89
90
  {
90
91
  :braintree_customer => (customer_hash(result.customer) if result.success?),
@@ -99,24 +100,25 @@ module ActiveMerchant #:nodoc:
99
100
  customer_update_result = commit do
100
101
  braintree_credit_card = Braintree::Customer.find(vault_id).credit_cards.detect { |cc| cc.default? }
101
102
  return Response.new(false, 'Braintree::NotFoundError') if braintree_credit_card.nil?
102
- result = Braintree::Customer.update(vault_id,
103
- :first_name => creditcard.first_name,
104
- :last_name => creditcard.last_name,
105
- :email => options[:email]
106
- )
107
- Response.new(result.success?, message_from_result(result),
108
- :braintree_customer => (customer_hash(Braintree::Customer.find(vault_id)) if result.success?)
109
- )
110
- end
111
- return customer_update_result unless customer_update_result.success?
112
- credit_card_update_result = commit do
113
- result = Braintree::CreditCard.update(braintree_credit_card.token,
103
+
104
+ options.merge!(:update_existing_token => braintree_credit_card.token)
105
+ credit_card_params = merge_credit_card_options({
106
+ :credit_card => {
114
107
  :number => creditcard.number,
115
108
  :expiration_month => creditcard.month.to_s.rjust(2, "0"),
116
109
  :expiration_year => creditcard.year.to_s
110
+ }
111
+ }, options)[:credit_card]
112
+
113
+ result = Braintree::Customer.update(vault_id,
114
+ :first_name => creditcard.first_name,
115
+ :last_name => creditcard.last_name,
116
+ :email => options[:email],
117
+ :credit_card => credit_card_params
117
118
  )
118
119
  Response.new(result.success?, message_from_result(result),
119
- :braintree_customer => (customer_hash(Braintree::Customer.find(vault_id)) if result.success?)
120
+ :braintree_customer => (customer_hash(Braintree::Customer.find(vault_id)) if result.success?),
121
+ :customer_vault_id => (result.customer.id if result.success?)
120
122
  )
121
123
  end
122
124
  end
@@ -131,17 +133,38 @@ module ActiveMerchant #:nodoc:
131
133
 
132
134
  private
133
135
 
136
+ def merge_credit_card_options(parameters, options)
137
+ valid_options = {}
138
+ options.each do |key, value|
139
+ valid_options[key] = value if [:update_existing_token, :verify_card, :verification_merchant_account_id].include?(key)
140
+ end
141
+
142
+ parameters[:credit_card] ||= {}
143
+ parameters[:credit_card].merge!(:options => valid_options)
144
+ parameters[:credit_card][:billing_address] = map_address(options[:billing_address]) if options[:billing_address]
145
+ parameters
146
+ end
147
+
134
148
  def map_address(address)
135
149
  return {} if address.nil?
136
- {
150
+ mapped = {
137
151
  :street_address => address[:address1],
138
152
  :extended_address => address[:address2],
139
153
  :company => address[:company],
140
154
  :locality => address[:city],
141
155
  :region => address[:state],
142
156
  :postal_code => address[:zip],
143
- :country_name => address[:country]
144
157
  }
158
+ if(address[:country] || address[:country_code_alpha2])
159
+ mapped[:country_code_alpha2] = (address[:country] || address[:country_code_alpha2])
160
+ elsif address[:country_name]
161
+ mapped[:country_name] = address[:country_name]
162
+ elsif address[:country_code_alpha3]
163
+ mapped[:country_code_alpha3] = address[:country_code_alpha3]
164
+ elsif address[:country_code_numeric]
165
+ mapped[:country_code_numeric] = address[:country_code_numeric]
166
+ end
167
+ mapped
145
168
  end
146
169
 
147
170
  def commit(&block)
@@ -153,6 +176,8 @@ module ActiveMerchant #:nodoc:
153
176
  def message_from_result(result)
154
177
  if result.success?
155
178
  "OK"
179
+ elsif result.errors.size == 0 && result.credit_card_verification
180
+ "Processor declined: #{result.credit_card_verification.processor_response_text} (#{result.credit_card_verification.processor_response_code})"
156
181
  else
157
182
  result.errors.map { |e| "#{e.message} (#{e.code})" }.join(" ")
158
183
  end
@@ -207,7 +232,8 @@ module ActiveMerchant #:nodoc:
207
232
  credit_cards = customer.credit_cards.map do |cc|
208
233
  {
209
234
  "bin" => cc.bin,
210
- "expiration_date" => cc.expiration_date
235
+ "expiration_date" => cc.expiration_date,
236
+ "token" => cc.token
211
237
  }
212
238
  end
213
239
 
@@ -215,7 +241,8 @@ module ActiveMerchant #:nodoc:
215
241
  "email" => customer.email,
216
242
  "first_name" => customer.first_name,
217
243
  "last_name" => customer.last_name,
218
- "credit_cards" => credit_cards
244
+ "credit_cards" => credit_cards,
245
+ "id" => customer.id
219
246
  }
220
247
  end
221
248
 
@@ -7,7 +7,7 @@ module ActiveMerchant #:nodoc:
7
7
  self.supported_countries = [
8
8
  "BE", "BG", "CZ", "DK", "DE", "EE", "IE", "EL", "ES", "FR",
9
9
  "IT", "CY", "LV", "LT", "LU", "HU", "MT", "NL", "AT", "PL",
10
- "PT", "RO", "SI", "SK", "FI", "SE", "UK"
10
+ "PT", "RO", "SI", "SK", "FI", "SE", "GB"
11
11
  ]
12
12
 
13
13
  self.supported_cardtypes = [:visa, :master, :american_express, :discover]
@@ -59,6 +59,7 @@ module ActiveMerchant #:nodoc:
59
59
  add_creditcard(form, creditcard)
60
60
  add_address(form, options)
61
61
  add_customer_data(form, options)
62
+ add_test_mode(form, options)
62
63
  commit(:authorize, money, form)
63
64
  end
64
65
 
@@ -77,6 +78,7 @@ module ActiveMerchant #:nodoc:
77
78
  add_invoice(form, options)
78
79
  add_creditcard(form, options[:credit_card])
79
80
  add_customer_data(form, options)
81
+ add_test_mode(form, options)
80
82
  commit(:capture, money, form)
81
83
  end
82
84
 
@@ -192,7 +192,9 @@ module ActiveMerchant #:nodoc:
192
192
  else
193
193
  return {
194
194
  'accept' => '0',
195
- 'errortext' => 'No Location header returned.'
195
+ 'errortext' => 'ePay did not respond as expected. Please try again.',
196
+ 'response_code' => response.code,
197
+ 'response_message' => response.message
196
198
  }
197
199
  end
198
200
 
@@ -0,0 +1,450 @@
1
+ require 'nokogiri'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ # iTransact, Inc. is an authorized reseller of the PaymentClearing gateway. If your merchant service provider uses PaymentClearing.com to process payments, you can use this module.
6
+ #
7
+ #
8
+ # Please note, the username and API Access Key are not what you use to log into the Merchant Control Panel.
9
+ #
10
+ # ==== How to get your GatewayID and API Access Key
11
+ #
12
+ # 1. If you don't already have a Gateway Account, go to http://www.itransact.com/merchant/test.html to sign up.
13
+ # 2. Go to http://support.paymentclearing.com and login or register, if necessary.
14
+ # 3. Click on "Submit a Ticket."
15
+ # 4. Select "Merchant Support" as the department and click "Next"
16
+ # 5. Enter *both* your company name and GatewayID. Put "API Access Key" in the subject. In the body, you can request a username, but it may already be in use.
17
+ #
18
+ # ==== Initialization
19
+ #
20
+ # Once you have the username, API Access Key, and your GatewayId, you're ready
21
+ # to begin. You initialize the Gateway like so:
22
+ #
23
+ # gateway = ActiveMerchant::Billing::ItransactGateway.new(
24
+ # :login => "#{THE_USERNAME}",
25
+ # :password => "#{THE_API_ACCESS_KEY}",
26
+ # :gateway_id => "#{THE_GATEWAY_ID}"
27
+ # )
28
+ #
29
+ # ==== Important Notes
30
+ # 1. Recurring is not implemented
31
+ # 1. CreditTransactions are not implemented (these are credits not related to a previously run transaction).
32
+ # 1. TransactionStatus is not implemented
33
+ #
34
+ class ItransactGateway < Gateway
35
+ URL = 'https://secure.paymentclearing.com/cgi-bin/rc/xmltrans2.cgi'
36
+
37
+ # The countries the gateway supports merchants from as 2 digit ISO country codes
38
+ self.supported_countries = ['US']
39
+
40
+ # The card types supported by the payment gateway
41
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
42
+
43
+ # The homepage URL of the gateway
44
+ self.homepage_url = 'http://www.itransact.com/'
45
+
46
+ # The name of the gateway
47
+ self.display_name = 'iTransact'
48
+
49
+ #
50
+ # Creates a new instance of the iTransact Gateway.
51
+ #
52
+ # ==== Parameters
53
+ # * <tt>options</tt> - A Hash of options
54
+ #
55
+ # ==== Options Hash
56
+ # * <tt>:login</tt> - A String containing your PaymentClearing assigned API Access Username
57
+ # * <tt>:password</tt> - A String containing your PaymentClearing assigned API Access Key
58
+ # * <tt>:gateway_id</tt> - A String containing your PaymentClearing assigned GatewayID
59
+ # * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Run *all* transactions with the 'TestMode' element set to 'TRUE'.
60
+ #
61
+ def initialize(options = {})
62
+ requires!(options, :login, :password, :gateway_id)
63
+ @options = options
64
+ super
65
+ end
66
+
67
+ # Performs an authorize transaction. In PaymentClearing's documentation
68
+ # this is known as a "PreAuth" transaction.
69
+ #
70
+ # ==== Parameters
71
+ # * <tt>money</tt> - The amount to be captured. Should be an Integer amount in cents.
72
+ # * <tt>creditcard</tt> - The CreditCard details for the transaction
73
+ # * <tt>options</tt> - A Hash of options
74
+ #
75
+ # ==== Options Hash
76
+ # The standard options apply here (:order_id, :ip, :customer, :invoice, :merchant, :description, :email, :currency, :address, :billing_address, :shipping_address), as well as:
77
+ # * <tt>:order_items</tt> - An Array of Hash objects with the keys <tt>:description</tt>, <tt>:cost</tt> (in cents!), and <tt>:quantity</tt>. If this is provided, <tt>:description</tt> and <tt>money</tt> will be ignored.
78
+ # * <tt>:vendor_data</tt> - An Array of Hash objects with the keys being the name of the VendorData element and value being the value.
79
+ # * <tt>:send_customer_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendCustomerEmail' element set to 'TRUE' or 'FALSE'.
80
+ # * <tt>:send_merchant_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendMerchantEmail' element set to 'TRUE' or 'FALSE'.
81
+ # * <tt>:email_text</tt> - An Array of (up to ten (10)) String objects to be included in emails
82
+ # * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'TestMode' element set to 'TRUE' or 'FALSE'.
83
+ #
84
+ # ==== Examples
85
+ # response = gateway.authorize(1000, creditcard,
86
+ # :order_id => '1212', :address => {...}, :email => 'test@test.com',
87
+ # :order_items => [
88
+ # {:description => 'Line Item 1', :cost => '8.98', :quantity => '6'},
89
+ # {:description => 'Line Item 2', :cost => '6.99', :quantity => '4'}
90
+ # ],
91
+ # :vendor_data => [{'repId' => '1234567'}, {'customerId' => '9886'}],
92
+ # :send_customer_email => true,
93
+ # :send_merchant_email => true,
94
+ # :email_text => ['line1', 'line2', 'line3'],
95
+ # :test_mode => true
96
+ # )
97
+ #
98
+ def authorize(money, payment_source, options = {})
99
+ payload = Nokogiri::XML::Builder.new do |xml|
100
+ xml.AuthTransaction {
101
+ xml.Preauth
102
+ add_customer_data(xml, payment_source, options)
103
+ add_invoice(xml, money, options)
104
+ add_payment_source(xml, payment_source)
105
+ add_transaction_control(xml, options)
106
+ add_vendor_data(xml, options)
107
+ }
108
+ end.doc
109
+
110
+ commit(payload)
111
+ end
112
+
113
+ # Performs an authorize and capture in single transaction. In PaymentClearing's
114
+ # documentation this is known as an "Auth" or a "Sale" transaction
115
+ #
116
+ # ==== Parameters
117
+ # * <tt>money</tt> - The amount to be captured. Should be <tt>nil</tt> or an Integer amount in cents.
118
+ # * <tt>creditcard</tt> - The CreditCard details for the transaction
119
+ # * <tt>options</tt> - A Hash of options
120
+ #
121
+ # ==== Options Hash
122
+ # The standard options apply here (:order_id, :ip, :customer, :invoice, :merchant, :description, :email, :currency, :address, :billing_address, :shipping_address), as well as:
123
+ # * <tt>:order_items</tt> - An Array of Hash objects with the keys <tt>:description</tt>, <tt>:cost</tt> (in cents!), and <tt>:quantity</tt>. If this is provided, <tt>:description</tt> and <tt>money</tt> will be ignored.
124
+ # * <tt>:vendor_data</tt> - An Array of Hash objects with the keys being the name of the VendorData element and value being the value.
125
+ # * <tt>:send_customer_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendCustomerEmail' element set to 'TRUE' or 'FALSE'.
126
+ # * <tt>:send_merchant_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendMerchantEmail' element set to 'TRUE' or 'FALSE'.
127
+ # * <tt>:email_text</tt> - An Array of (up to ten (10)) String objects to be included in emails
128
+ # * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'TestMode' element set to 'TRUE' or 'FALSE'.
129
+ #
130
+ # ==== Examples
131
+ # response = gateway.purchase(1000, creditcard,
132
+ # :order_id => '1212', :address => {...}, :email => 'test@test.com',
133
+ # :order_items => [
134
+ # {:description => 'Line Item 1', :cost => '8.98', :quantity => '6'},
135
+ # {:description => 'Line Item 2', :cost => '6.99', :quantity => '4'}
136
+ # ],
137
+ # :vendor_data => [{'repId' => '1234567'}, {'customerId' => '9886'}],
138
+ # :send_customer_email => true,
139
+ # :send_merchant_email => true,
140
+ # :email_text => ['line1', 'line2', 'line3'],
141
+ # :test_mode => true
142
+ # )
143
+ #
144
+ def purchase(money, payment_source, options = {})
145
+ payload = Nokogiri::XML::Builder.new do |xml|
146
+ xml.AuthTransaction {
147
+ add_customer_data(xml, payment_source, options)
148
+ add_invoice(xml, money, options)
149
+ add_payment_source(xml, payment_source)
150
+ add_transaction_control(xml, options)
151
+ add_vendor_data(xml, options)
152
+ }
153
+ end.doc
154
+
155
+ commit(payload)
156
+ end
157
+
158
+ # Captures the funds from an authorize transaction. In PaymentClearing's
159
+ # documentation this is known as a "PostAuth" transaction.
160
+ #
161
+ # ==== Parameters
162
+ # * <tt>money</tt> - The amount to be captured. Should be an Integer amount in cents
163
+ # * <tt>authorization</tt> - The authorization returned from the previous capture or purchase request
164
+ # * <tt>options</tt> - A Hash of options, all are optional.
165
+ #
166
+ # ==== Options Hash
167
+ # The standard options apply here (:order_id, :ip, :customer, :invoice, :merchant, :description, :email, :currency, :address, :billing_address, :shipping_address), as well as:
168
+ # * <tt>:vendor_data</tt> - An Array of Hash objects with the keys being the name of the VendorData element and value being the value.
169
+ # * <tt>:send_customer_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendCustomerEmail' element set to 'TRUE' or 'FALSE'.
170
+ # * <tt>:send_merchant_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendMerchantEmail' element set to 'TRUE' or 'FALSE'.
171
+ # * <tt>:email_text</tt> - An Array of (up to ten (10)) String objects to be included in emails
172
+ # * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'TestMode' element set to 'TRUE' or 'FALSE'.
173
+ #
174
+ # ==== Examples
175
+ # response = gateway.capture(1000, creditcard,
176
+ # :vendor_data => [{'repId' => '1234567'}, {'customerId' => '9886'}],
177
+ # :send_customer_email => true,
178
+ # :send_merchant_email => true,
179
+ # :email_text => ['line1', 'line2', 'line3'],
180
+ # :test_mode => true
181
+ # )
182
+ #
183
+ def capture(money, authorization, options = {})
184
+ payload = Nokogiri::XML::Builder.new do |xml|
185
+ xml.PostAuthTransaction {
186
+ xml.OperationXID(authorization)
187
+ add_invoice(xml, money, options)
188
+ add_transaction_control(xml, options)
189
+ add_vendor_data(xml, options)
190
+ }
191
+ end.doc
192
+
193
+ commit(payload)
194
+ end
195
+
196
+ # This will reverse a previously run transaction which *has* *not* settled.
197
+ #
198
+ # ==== Parameters
199
+ # * <tt>money</tt> - This parameter is ignored -- the PaymentClearing gateway does not allow partial voids.
200
+ # * <tt>authorization</tt> - The authorization returned from the previous capture or purchase request
201
+ # * <tt>options</tt> - A Hash of options, all are optional
202
+ #
203
+ # ==== Options Hash
204
+ # The standard options (:order_id, :ip, :customer, :invoice, :merchant, :description, :email, :currency, :address, :billing_address, :shipping_address) are ignored.
205
+ # * <tt>:vendor_data</tt> - An Array of Hash objects with the keys being the name of the VendorData element and value being the value.
206
+ # * <tt>:send_customer_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendCustomerEmail' element set to 'TRUE' or 'FALSE'.
207
+ # * <tt>:send_merchant_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendMerchantEmail' element set to 'TRUE' or 'FALSE'.
208
+ # * <tt>:email_text</tt> - An Array of (up to ten (10)) String objects to be included in emails
209
+ # * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'TestMode' element set to 'TRUE' or 'FALSE'.
210
+ #
211
+ # ==== Examples
212
+ # response = gateway.void(nil, '9999999999',
213
+ # :vendor_data => [{'repId' => '1234567'}, {'customerId' => '9886'}],
214
+ # :send_customer_email => true,
215
+ # :send_merchant_email => true,
216
+ # :email_text => ['line1', 'line2', 'line3'],
217
+ # :test_mode => true
218
+ # )
219
+ #
220
+ def void(money, authorization, options = {})
221
+ payload = Nokogiri::XML::Builder.new do |xml|
222
+ xml.VoidTransaction {
223
+ xml.OperationXID(authorization)
224
+ add_transaction_control(xml, options)
225
+ add_vendor_data(xml, options)
226
+ }
227
+ end.doc
228
+
229
+ commit(payload)
230
+ end
231
+
232
+ # This will reverse a previously run transaction which *has* settled.
233
+ #
234
+ # ==== Parameters
235
+ # * <tt>money</tt> - The amount to be credited. Should be <tt>nil</tt> or an Integer amount in cents
236
+ # * <tt>authorization</tt> - The authorization returned from the previous capture or purchase request
237
+ # * <tt>options</tt> - A Hash of options, all are optional
238
+ #
239
+ # ==== Options Hash
240
+ # The standard options (:order_id, :ip, :customer, :invoice, :merchant, :description, :email, :currency, :address, :billing_address, :shipping_address) are ignored.
241
+ # * <tt>:vendor_data</tt> - An Array of Hash objects with the keys being the name of the VendorData element and value being the value.
242
+ # * <tt>:send_customer_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendCustomerEmail' element set to 'TRUE' or 'FALSE'.
243
+ # * <tt>:send_merchant_email</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'SendMerchantEmail' element set to 'TRUE' or 'FALSE'.
244
+ # * <tt>:email_text</tt> - An Array of (up to ten (10)) String objects to be included in emails
245
+ # * <tt>:test_mode</tt> - <tt>true</tt> or <tt>false</tt>. Runs the transaction with the 'TestMode' element set to 'TRUE' or 'FALSE'.
246
+ #
247
+ # ==== Examples
248
+ # response = gateway.credit(nil, '9999999999',
249
+ # :vendor_data => [{'repId' => '1234567'}, {'customerId' => '9886'}],
250
+ # :send_customer_email => true,
251
+ # :send_merchant_email => true,
252
+ # :email_text => ['line1', 'line2', 'line3'],
253
+ # :test_mode => true
254
+ # )
255
+ #
256
+ def credit(money, authorization, options = {})
257
+ payload = Nokogiri::XML::Builder.new do |xml|
258
+ xml.TranCreditTransaction {
259
+ xml.OperationXID(authorization)
260
+ add_invoice(xml, money, options)
261
+ add_transaction_control(xml, options)
262
+ add_vendor_data(xml, options)
263
+ }
264
+ end.doc
265
+
266
+ commit(payload)
267
+ end
268
+
269
+ private
270
+
271
+ def add_customer_data(xml, payment_source, options)
272
+ billing_address = options[:billing_address] || options[:address]
273
+ shipping_address = options[:shipping_address] || options[:address]
274
+
275
+ xml.CustomerData {
276
+ xml.Email(options[:email]) unless options[:email].blank?
277
+ xml.CustId(options[:order_id]) unless options[:order_id].blank?
278
+ xml.BillingAddress {
279
+ xml.FirstName(payment_source.first_name || parse_first_name(billing_address[:name]))
280
+ xml.LastName(payment_source.last_name || parse_last_name(billing_address[:name]))
281
+ xml.Address1(billing_address[:address1])
282
+ xml.Address2(billing_address[:address2]) unless billing_address[:address2].blank?
283
+ xml.City(billing_address[:city])
284
+ xml.State(billing_address[:state])
285
+ xml.Zip(billing_address[:zip])
286
+ xml.Country(billing_address[:country])
287
+ xml.Phone(billing_address[:phone])
288
+ }
289
+ xml.ShippingAddress {
290
+ xml.FirstName(payment_source.first_name || parse_first_name(shipping_address[:name]))
291
+ xml.LastName(payment_source.last_name || parse_last_name(shipping_address[:name]))
292
+ xml.Address1(shipping_address[:address1])
293
+ xml.Address2(shipping_address[:address2]) unless shipping_address[:address2].blank?
294
+ xml.City(shipping_address[:city])
295
+ xml.State(shipping_address[:state])
296
+ xml.Zip(shipping_address[:zip])
297
+ xml.Country(shipping_address[:country])
298
+ xml.Phone(shipping_address[:phone])
299
+ } unless shipping_address.blank?
300
+ }
301
+ end
302
+
303
+ def add_invoice(xml, money, options)
304
+ xml.AuthCode options[:force] if options[:force]
305
+ if options[:order_items].blank?
306
+ xml.Total(amount(money)) unless(money.nil? || money < 0.01)
307
+ xml.Description(options[:description]) unless( options[:description].blank?)
308
+ else
309
+ xml.OrderItems {
310
+ options[:order_items].each do |item|
311
+ xml.Item {
312
+ xml.Description(item[:description])
313
+ xml.Cost(amount(item[:cost]))
314
+ xml.Qty(item[:quantity].to_s)
315
+ }
316
+ end
317
+ }
318
+ end
319
+ end
320
+
321
+ def add_payment_source(xml, source)
322
+ case determine_funding_source(source)
323
+ when :credit_card then add_creditcard(xml, source)
324
+ when :check then add_check(xml, source)
325
+ end
326
+ end
327
+
328
+ def determine_funding_source(payment_source)
329
+ case payment_source
330
+ when ActiveMerchant::Billing::CreditCard
331
+ :credit_card
332
+ when ActiveMerchant::Billing::Check
333
+ :check
334
+ end
335
+ end
336
+
337
+ def add_creditcard(xml, creditcard)
338
+ xml.AccountInfo {
339
+ xml.CardAccount {
340
+ xml.AccountNumber(creditcard.number.to_s)
341
+ xml.ExpirationMonth(creditcard.month.to_s.rjust(2,'0'))
342
+ xml.ExpirationYear(creditcard.year.to_s)
343
+ xml.CVVNumber(creditcard.verification_value.to_s) unless creditcard.verification_value.blank?
344
+ }
345
+ }
346
+ end
347
+
348
+ def add_check(xml, check)
349
+ xml.AccountInfo {
350
+ xml.ABA(check.routing_number.to_s)
351
+ xml.AccountNumber(check.account_number.to_s)
352
+ xml.AccountSource(check.account_type.to_s)
353
+ xml.AccountType(check.account_holder_type.to_s)
354
+ xml.CheckNumber(check.number.to_s)
355
+ }
356
+ end
357
+
358
+ def add_transaction_control(xml, options)
359
+ xml.TransactionControl {
360
+ # if there was a 'global' option set...
361
+ xml.TestMode(@options[:test_mode].upcase) if !@options[:test_mode].blank?
362
+ # allow the global option to be overridden...
363
+ xml.TestMode(options[:test_mode].upcase) if !options[:test_mode].blank?
364
+ xml.SendCustomerEmail(options[:send_customer_email].upcase) unless options[:send_customer_email].blank?
365
+ xml.SendMerchantEmail(options[:send_merchant_email].upcase) unless options[:send_merchant_email].blank?
366
+ xml.EmailText {
367
+ options[:email_text].each do |item|
368
+ xml.EmailTextItem(item)
369
+ end
370
+ } if options[:email_text]
371
+ }
372
+ end
373
+
374
+ def add_vendor_data(xml, options)
375
+ return if options[:vendor_data].blank?
376
+ xml.VendorData {
377
+ options[:vendor_data].each do |k,v|
378
+ xml.Element {
379
+ xml.Name(k)
380
+ xml.Key(v)
381
+ }
382
+ end
383
+ }
384
+ end
385
+
386
+ def commit(payload)
387
+ # Set the Content-Type header -- otherwise the URL decoding messes up
388
+ # the Base64 encoded payload signature!
389
+ response = parse(ssl_post(URL, post_data(payload), 'Content-Type' => 'text/xml'))
390
+
391
+ Response.new(successful?(response), response[:error_message], response,
392
+ :test => test?,
393
+ :authorization => response[:xid],
394
+ :avs_result => { :code => response[:avs_response] },
395
+ :cvv_result => response[:cvv_response])
396
+ end
397
+
398
+ def post_data(payload)
399
+ payload_xml = payload.root.to_xml(:indent => 0)
400
+
401
+ payload_signature = sign_payload(payload_xml)
402
+
403
+ request = Nokogiri::XML::Builder.new do |xml|
404
+ xml.GatewayInterface {
405
+ xml.APICredentials {
406
+ xml.Username(@options[:login])
407
+ xml.PayloadSignature(payload_signature)
408
+ xml.TargetGateway(@options[:gateway_id])
409
+ }
410
+ }
411
+ end.doc
412
+
413
+ request.root.children.first.after payload.root
414
+ request.to_xml(:indent => 0)
415
+ end
416
+
417
+ def parse(raw_xml)
418
+ doc = REXML::Document.new(raw_xml)
419
+ response = Hash.new
420
+ transaction_result = doc.root.get_elements('TransactionResponse/TransactionResult/*')
421
+ transaction_result.each do |e|
422
+ response[e.name.to_s.underscore.to_sym] = e.text unless e.text.blank?
423
+ end
424
+ response
425
+ end
426
+
427
+ def successful?(response)
428
+ # Turns out the PaymentClearing gateway is not consistent...
429
+ response[:status].downcase =='ok'
430
+ end
431
+
432
+ def test_mode?(response)
433
+ # The '1' is a legacy thing; most of the time it should be 'TRUE'...
434
+ response[:test_mode] == 'TRUE' || response[:test_mode] == '1'
435
+ end
436
+
437
+ def message_from(response)
438
+ response[:error_message]
439
+ end
440
+
441
+ def sign_payload(payload)
442
+ key = @options[:password].to_s
443
+ digest=OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new(key), key, payload)
444
+ signature = Base64.encode64(digest)
445
+ signature.chomp!
446
+ end
447
+ end
448
+ end
449
+ end
450
+