activemerchant 1.29.1 → 1.34.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 (92) hide show
  1. data/CHANGELOG +143 -0
  2. data/CONTRIBUTORS +43 -0
  3. data/README.md +59 -51
  4. data/lib/active_merchant/billing/check.rb +15 -14
  5. data/lib/active_merchant/billing/credit_card.rb +14 -5
  6. data/lib/active_merchant/billing/credit_card_formatting.rb +8 -8
  7. data/lib/active_merchant/billing/gateway.rb +2 -2
  8. data/lib/active_merchant/billing/gateways/authorize_net.rb +36 -8
  9. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +17 -5
  10. data/lib/active_merchant/billing/gateways/balanced.rb +9 -3
  11. data/lib/active_merchant/billing/gateways/banwire.rb +15 -1
  12. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +8 -1
  13. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +7 -2
  14. data/lib/active_merchant/billing/gateways/beanstream.rb +26 -24
  15. data/lib/active_merchant/billing/gateways/blue_pay.rb +201 -187
  16. data/lib/active_merchant/billing/gateways/bogus.rb +1 -1
  17. data/lib/active_merchant/billing/gateways/braintree_blue.rb +7 -3
  18. data/lib/active_merchant/billing/gateways/card_stream_modern.rb +155 -0
  19. data/lib/active_merchant/billing/gateways/cc5.rb +156 -0
  20. data/lib/active_merchant/billing/gateways/cyber_source.rb +55 -22
  21. data/lib/active_merchant/billing/gateways/data_cash.rb +3 -3
  22. data/lib/active_merchant/billing/gateways/evo_ca.rb +308 -0
  23. data/lib/active_merchant/billing/gateways/eway.rb +114 -171
  24. data/lib/active_merchant/billing/gateways/eway_managed.rb +52 -22
  25. data/lib/active_merchant/billing/gateways/finansbank.rb +22 -0
  26. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +314 -0
  27. data/lib/active_merchant/billing/gateways/garanti.rb +0 -4
  28. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +13 -2
  29. data/lib/active_merchant/billing/gateways/iridium.rb +8 -2
  30. data/lib/active_merchant/billing/gateways/litle.rb +354 -105
  31. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +28 -7
  32. data/lib/active_merchant/billing/gateways/merchant_ware.rb +44 -9
  33. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +190 -0
  34. data/lib/active_merchant/billing/gateways/moneris.rb +4 -6
  35. data/lib/active_merchant/billing/gateways/moneris_us.rb +1 -1
  36. data/lib/active_merchant/billing/gateways/nab_transact.rb +20 -3
  37. data/lib/active_merchant/billing/gateways/net_registry.rb +8 -3
  38. data/lib/active_merchant/billing/gateways/netaxept.rb +65 -117
  39. data/lib/active_merchant/billing/gateways/netbilling.rb +1 -0
  40. data/lib/active_merchant/billing/gateways/netpay.rb +223 -0
  41. data/lib/active_merchant/billing/gateways/ogone.rb +7 -5
  42. data/lib/active_merchant/billing/gateways/optimal_payment.rb +43 -18
  43. data/lib/active_merchant/billing/gateways/orbital.rb +190 -53
  44. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +12 -10
  45. data/lib/active_merchant/billing/gateways/payment_express.rb +62 -1
  46. data/lib/active_merchant/billing/gateways/paymill.rb +179 -0
  47. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +12 -7
  48. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +14 -9
  49. data/lib/active_merchant/billing/gateways/paypal_express.rb +59 -18
  50. data/lib/active_merchant/billing/gateways/pin.rb +165 -0
  51. data/lib/active_merchant/billing/gateways/qbms.rb +3 -2
  52. data/lib/active_merchant/billing/gateways/quickpay.rb +66 -28
  53. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +16 -11
  54. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +1 -1
  55. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +21 -16
  56. data/lib/active_merchant/billing/gateways/sage.rb +10 -5
  57. data/lib/active_merchant/billing/gateways/sage_pay.rb +7 -0
  58. data/lib/active_merchant/billing/gateways/smart_ps.rb +1 -1
  59. data/lib/active_merchant/billing/gateways/spreedly_core.rb +233 -0
  60. data/lib/active_merchant/billing/gateways/stripe.rb +49 -21
  61. data/lib/active_merchant/billing/gateways/transnational.rb +239 -0
  62. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +9 -4
  63. data/lib/active_merchant/billing/gateways/webpay.rb +8 -0
  64. data/lib/active_merchant/billing/gateways/wirecard.rb +15 -9
  65. data/lib/active_merchant/billing/gateways/worldpay.rb +60 -24
  66. data/lib/active_merchant/billing/integrations/direc_pay/status.rb +1 -1
  67. data/lib/active_merchant/billing/integrations/direc_pay.rb +1 -1
  68. data/lib/active_merchant/billing/integrations/dwolla/common.rb +23 -0
  69. data/lib/active_merchant/billing/integrations/dwolla/helper.rb +18 -6
  70. data/lib/active_merchant/billing/integrations/dwolla/notification.rb +16 -7
  71. data/lib/active_merchant/billing/integrations/dwolla/return.rb +16 -5
  72. data/lib/active_merchant/billing/integrations/dwolla.rb +5 -12
  73. data/lib/active_merchant/billing/integrations/notification.rb +13 -8
  74. data/lib/active_merchant/billing/integrations/payflow_link/helper.rb +19 -3
  75. data/lib/active_merchant/billing/integrations/paypal/notification.rb +39 -31
  76. data/lib/active_merchant/billing/integrations/payu_in/helper.rb +74 -0
  77. data/lib/active_merchant/billing/integrations/payu_in/notification.rb +167 -0
  78. data/lib/active_merchant/billing/integrations/payu_in/return.rb +53 -0
  79. data/lib/active_merchant/billing/integrations/payu_in.rb +43 -0
  80. data/lib/active_merchant/billing/integrations/pxpay/helper.rb +1 -0
  81. data/lib/active_merchant/billing/integrations/quickpay/helper.rb +13 -10
  82. data/lib/active_merchant/billing/integrations/quickpay/notification.rb +78 -15
  83. data/lib/active_merchant/billing/integrations/rbkmoney/helper.rb +23 -0
  84. data/lib/active_merchant/billing/integrations/rbkmoney/notification.rb +91 -0
  85. data/lib/active_merchant/billing/integrations/rbkmoney.rb +17 -0
  86. data/lib/active_merchant/billing/integrations/robokassa/common.rb +1 -1
  87. data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +7 -3
  88. data/lib/active_merchant/billing/integrations/world_pay.rb +15 -8
  89. data/lib/active_merchant/version.rb +1 -1
  90. data.tar.gz.sig +0 -0
  91. metadata +124 -50
  92. metadata.gz.sig +0 -0
@@ -32,8 +32,8 @@ module ActiveMerchant #:nodoc:
32
32
  API_VERSION = "5.6"
33
33
 
34
34
  POST_HEADERS = {
35
- "MIME-Version" => "1.0",
36
- "Content-Type" => "Application/PTI46",
35
+ "MIME-Version" => "1.1",
36
+ "Content-Type" => "application/PTI56",
37
37
  "Content-transfer-encoding" => "text",
38
38
  "Request-number" => '1',
39
39
  "Document-type" => "Request",
@@ -80,6 +80,63 @@ module ActiveMerchant #:nodoc:
80
80
  "EUR" => '978'
81
81
  }
82
82
 
83
+ # INDUSTRY TYPES
84
+ ECOMMERCE_TRANSACTION = 'EC'
85
+ RECURRING_PAYMENT_TRANSACTION = 'RC'
86
+ MAIL_ORDER_TELEPHONE_ORDER_TRANSACTION = 'MO'
87
+ INTERACTIVE_VOICE_RESPONSE = 'IV'
88
+ # INTERACTIVE_VOICE_RESPONSE = 'IN'
89
+
90
+ # Auth Only No Capture
91
+ AUTH_ONLY = 'A'
92
+ # AC - Auth and Capture = 'AC'
93
+ AUTH_AND_CAPTURE = 'AC'
94
+ # F - Force Auth No Capture and no online authorization = 'F'
95
+ FORCE_AUTH_ONLY = 'F'
96
+ # FR - Force Auth No Capture and no online authorization = 'FR'
97
+ # FC - Force Auth and Capture no online authorization = 'FC'
98
+ FORCE_AUTH_AND_CAPTURE = 'FC'
99
+ # Refund and Capture no online authorization
100
+ REFUND = 'R'
101
+
102
+ # Tax Inds
103
+ TAX_NOT_PROVIDED = 0
104
+ TAX_INCLUDED = 1
105
+ NON_TAXABLE_TRANSACTION = 2
106
+
107
+ # Customer Profile Actions
108
+ CREATE = 'C'
109
+ RETRIEVE = 'R'
110
+ UPDATE = 'U'
111
+ DELETE = 'D'
112
+
113
+ RECURRING = 'R'
114
+ DEFERRED = 'D'
115
+
116
+ # Status
117
+ # Profile Status Flag
118
+ # This field is used to set the status of a Customer Profile.
119
+ ACTIVE = 'A'
120
+ INACTIVE = 'I'
121
+ MANUAL_SUSPEND = 'MS'
122
+
123
+ # CustomerProfileOrderOverrideInd
124
+ # Defines if any Order Data can be pre-populated from
125
+ # the Customer Reference Number (CustomerRefNum)
126
+ NO_MAPPING_TO_ORDER_DATA = 'NO'
127
+ USE_CRN_FOR_ORDER_ID = 'OI'
128
+ USE_CRN_FOR_COMMENTS = 'OD'
129
+ USE_CRN_FOR_ORDER_ID_AND_COMMENTS = 'OA'
130
+
131
+ # CustomerProfileFromOrderInd
132
+ # Method to use to Generate the Customer Profile Number
133
+ # When Customer Profile Action Type = Create, defines
134
+ # what the Customer Profile Number will be:
135
+ AUTO_GENERATE = 'A' # Auto-Generate the CustomerRefNum
136
+ USE_CUSTOMER_REF_NUM = 'S' # Use CustomerRefNum field
137
+ USE_ORDER_ID = 'O' # Use OrderID field
138
+ USE_COMMENTS = 'D' # Use Comments field
139
+
83
140
  def initialize(options = {})
84
141
  requires!(options, :merchant_id)
85
142
  requires!(options, :login, :password) unless options[:ip_authentication]
@@ -88,22 +145,28 @@ module ActiveMerchant #:nodoc:
88
145
 
89
146
  # A – Authorization request
90
147
  def authorize(money, creditcard, options = {})
91
- order = build_new_order_xml('A', money, options) do |xml|
148
+ order = build_new_order_xml(AUTH_ONLY, money, options) do |xml|
92
149
  add_creditcard(xml, creditcard, options[:currency]) unless creditcard.nil? && options[:profile_txn]
93
150
  add_address(xml, creditcard, options)
94
- add_customer_data(xml, options) if @options[:customer_profiles]
151
+ if @options[:customer_profiles]
152
+ add_customer_data(xml, options)
153
+ add_managed_billing(xml, options)
154
+ end
95
155
  end
96
- commit(order, :authorize)
156
+ commit(order, :authorize, options[:trace_number])
97
157
  end
98
158
 
99
159
  # AC – Authorization and Capture
100
160
  def purchase(money, creditcard, options = {})
101
- order = build_new_order_xml('AC', money, options) do |xml|
161
+ order = build_new_order_xml(AUTH_AND_CAPTURE, money, options) do |xml|
102
162
  add_creditcard(xml, creditcard, options[:currency]) unless creditcard.nil? && options[:profile_txn]
103
163
  add_address(xml, creditcard, options)
104
- add_customer_data(xml, options) if @options[:customer_profiles]
164
+ if @options[:customer_profiles]
165
+ add_customer_data(xml, options)
166
+ add_managed_billing(xml, options)
167
+ end
105
168
  end
106
- commit(order, :purchase)
169
+ commit(order, :purchase, options[:trace_number])
107
170
  end
108
171
 
109
172
  # MFC - Mark For Capture
@@ -113,11 +176,11 @@ module ActiveMerchant #:nodoc:
113
176
 
114
177
  # R – Refund request
115
178
  def refund(money, authorization, options = {})
116
- order = build_new_order_xml('R', money, options.merge(:authorization => authorization)) do |xml|
179
+ order = build_new_order_xml(REFUND, money, options.merge(:authorization => authorization)) do |xml|
117
180
  add_refund(xml, options[:currency])
118
181
  xml.tag! :CustomerRefNum, options[:customer_ref_num] if @options[:customer_profiles] && options[:profile_txn]
119
182
  end
120
- commit(order, :refund)
183
+ commit(order, :refund, options[:trace_number])
121
184
  end
122
185
 
123
186
  def credit(money, authorization, options= {})
@@ -125,10 +188,14 @@ module ActiveMerchant #:nodoc:
125
188
  refund(money, authorization, options)
126
189
  end
127
190
 
128
- # setting money to nil will perform a full void
129
- def void(money, authorization, options = {})
130
- order = build_void_request_xml(money, authorization, options)
131
- commit(order, :void)
191
+ def void(authorization, options = {}, deprecated = {})
192
+ if(!options.kind_of?(Hash))
193
+ deprecated("Calling the void method with an amount parameter is deprecated and will be removed in a future version.")
194
+ return void(options, deprecated.merge(:amount => authorization))
195
+ end
196
+
197
+ order = build_void_request_xml(authorization, options)
198
+ commit(order, :void, options[:trace_number])
132
199
  end
133
200
 
134
201
 
@@ -154,25 +221,25 @@ module ActiveMerchant #:nodoc:
154
221
  # 'MS' - Manual Suspend
155
222
 
156
223
  def add_customer_profile(creditcard, options = {})
157
- options.merge!(:customer_profile_action => 'C')
224
+ options.merge!(:customer_profile_action => CREATE)
158
225
  order = build_customer_request_xml(creditcard, options)
159
226
  commit(order, :add_customer_profile)
160
227
  end
161
228
 
162
229
  def update_customer_profile(creditcard, options = {})
163
- options.merge!(:customer_profile_action => 'U')
230
+ options.merge!(:customer_profile_action => UPDATE)
164
231
  order = build_customer_request_xml(creditcard, options)
165
232
  commit(order, :update_customer_profile)
166
233
  end
167
234
 
168
235
  def retrieve_customer_profile(customer_ref_num)
169
- options = {:customer_profile_action => 'R', :customer_ref_num => customer_ref_num}
236
+ options = {:customer_profile_action => RETRIEVE, :customer_ref_num => customer_ref_num}
170
237
  order = build_customer_request_xml(nil, options)
171
238
  commit(order, :retrieve_customer_profile)
172
239
  end
173
240
 
174
241
  def delete_customer_profile(customer_ref_num)
175
- options = {:customer_profile_action => 'D', :customer_ref_num => customer_ref_num}
242
+ options = {:customer_profile_action => DELETE, :customer_ref_num => customer_ref_num}
176
243
  order = build_customer_request_xml(nil, options)
177
244
  commit(order, :delete_customer_profile)
178
245
  end
@@ -192,12 +259,12 @@ module ActiveMerchant #:nodoc:
192
259
  xml.tag! :CustomerRefNum, options[:customer_ref_num]
193
260
  else
194
261
  if options[:customer_ref_num]
195
- xml.tag! :CustomerProfileFromOrderInd, 'S'
262
+ xml.tag! :CustomerProfileFromOrderInd, USE_CUSTOMER_REF_NUM
196
263
  xml.tag! :CustomerRefNum, options[:customer_ref_num]
197
264
  else
198
- xml.tag! :CustomerProfileFromOrderInd, 'A'
265
+ xml.tag! :CustomerProfileFromOrderInd, AUTO_GENERATE
199
266
  end
200
- xml.tag! :CustomerProfileOrderOverrideInd, options[:customer_profile_order_override_ind] || 'NO'
267
+ xml.tag! :CustomerProfileOrderOverrideInd, options[:customer_profile_order_override_ind] || NO_MAPPING_TO_ORDER_DATA
201
268
  end
202
269
  end
203
270
 
@@ -211,32 +278,51 @@ module ActiveMerchant #:nodoc:
211
278
  end
212
279
 
213
280
  def add_address(xml, creditcard, options)
214
- if address = options[:billing_address] || options[:address]
281
+ if(address = (options[:billing_address] || options[:address]))
215
282
  avs_supported = AVS_SUPPORTED_COUNTRIES.include?(address[:country].to_s)
216
283
 
217
284
  if avs_supported
218
- xml.tag! :AVSzip, address[:zip]
219
- xml.tag! :AVSaddress1, address[:address1]
220
- xml.tag! :AVSaddress2, address[:address2]
221
- xml.tag! :AVScity, address[:city]
222
- xml.tag! :AVSstate, address[:state]
223
- xml.tag! :AVSphoneNum, address[:phone] ? address[:phone].scan(/\d/).join.to_s : nil
285
+ xml.tag! :AVSzip, (address[:zip] ? address[:zip].to_s[0..9] : nil)
286
+ xml.tag! :AVSaddress1, (address[:address1] ? address[:address1][0..29] : nil)
287
+ xml.tag! :AVSaddress2, (address[:address2] ? address[:address2][0..29] : nil)
288
+ xml.tag! :AVScity, (address[:city] ? address[:city][0..19] : nil)
289
+ xml.tag! :AVSstate, address[:state]
290
+ xml.tag! :AVSphoneNum, (address[:phone] ? address[:phone].scan(/\d/).join.to_s[0..13] : nil)
224
291
  end
225
- xml.tag! :AVSname, creditcard.name
226
- xml.tag! :AVScountryCode, avs_supported ? address[:country] : ''
292
+ # can't look in billing address?
293
+ xml.tag! :AVSname, ((creditcard && creditcard.name) ? creditcard.name[0..29] : nil)
294
+ xml.tag! :AVScountryCode, (avs_supported ? address[:country] : '')
295
+
296
+ # Needs to come after AVScountryCode
297
+ add_destination_address(xml, address) if avs_supported
298
+ end
299
+ end
300
+
301
+ def add_destination_address(xml, address)
302
+ if address[:dest_zip]
303
+ xml.tag! :AVSDestzip, (address[:dest_zip] ? address[:dest_zip].to_s[0..9] : nil)
304
+ xml.tag! :AVSDestaddress1, (address[:dest_address1] ? address[:dest_address1][0..29] : nil)
305
+ xml.tag! :AVSDestaddress2, (address[:dest_address2] ? address[:dest_address2][0..29] : nil)
306
+ xml.tag! :AVSDestcity, (address[:dest_city] ? address[:dest_city][0..19] : nil)
307
+ xml.tag! :AVSDeststate, address[:dest_state]
308
+ xml.tag! :AVSDestphoneNum, (address[:dest_phone] ? address[:dest_phone].scan(/\d/).join.to_s[0..13] : nil)
309
+
310
+ xml.tag! :AVSDestname, (address[:dest_name] ? address[:dest_name][0..29] : nil)
311
+ xml.tag! :AVSDestcountryCode, address[:dest_country]
227
312
  end
228
313
  end
229
314
 
230
315
  # For Profile requests
231
316
  def add_customer_address(xml, options)
232
- if address = options[:billing_address] || options[:address]
233
- xml.tag! :CustomerAddress1, address[:address1]
234
- xml.tag! :CustomerAddress2, address[:address2]
235
- xml.tag! :CustomerCity, address[:city]
317
+ if(address = (options[:billing_address] || options[:address]))
318
+ xml.tag! :CustomerAddress1, (address[:address1] ? address[:address1][0..29] : nil)
319
+ xml.tag! :CustomerAddress2, (address[:address2] ? address[:address2][0..29] : nil)
320
+ xml.tag! :CustomerCity, (address[:city] ? address[:city][0..19] : nil)
236
321
  xml.tag! :CustomerState, address[:state]
237
- xml.tag! :CustomerZIP, address[:zip]
238
- xml.tag! :CustomerPhone, address[:phone] ? address[:phone].scan(/\d/).to_s : nil
239
- xml.tag! :CustomerCountryCode, address[:country]
322
+ xml.tag! :CustomerZIP, (address[:zip] ? address[:zip].to_s[0..9] : nil)
323
+ xml.tag! :CustomerEmail, address[:email].to_s[0..49] if address[:email]
324
+ xml.tag! :CustomerPhone, (address[:phone] ? address[:phone].scan(/\d/).join.to_s : nil)
325
+ xml.tag! :CustomerCountryCode, (address[:country] ? address[:country][0..1] : nil)
240
326
  end
241
327
  end
242
328
 
@@ -257,7 +343,7 @@ module ActiveMerchant #:nodoc:
257
343
  # Do not submit the attribute at all.
258
344
  # - http://download.chasepaymentech.com/docs/orbital/orbital_gateway_xml_specification.pdf
259
345
  if %w( visa discover ).include?(creditcard.brand)
260
- xml.tag! :CardSecValInd, creditcard.verification_value? ? '1' : '9'
346
+ xml.tag! :CardSecValInd, (creditcard.verification_value? ? '1' : '9')
261
347
  end
262
348
  xml.tag! :CardSecVal, creditcard.verification_value if creditcard.verification_value?
263
349
  end
@@ -269,6 +355,27 @@ module ActiveMerchant #:nodoc:
269
355
  xml.tag! :CurrencyExponent, '2' # Will need updating to support currencies such as the Yen.
270
356
  end
271
357
 
358
+ def add_managed_billing(xml, options)
359
+ if mb = options[:managed_billing]
360
+ # default to recurring (R). Other option is deferred (D).
361
+ xml.tag! :MBType, mb[:type] || RECURRING
362
+ # default to Customer Reference Number
363
+ xml.tag! :MBOrderIdGenerationMethod, mb[:order_id_generation_method] || 'IO'
364
+ # By default use MBRecurringEndDate, set to N.
365
+ # MMDDYYYY
366
+ xml.tag! :MBRecurringStartDate, mb[:start_date].scan(/\d/).join.to_s if mb[:start_date]
367
+ # MMDDYYYY
368
+ xml.tag! :MBRecurringEndDate, mb[:end_date].scan(/\d/).join.to_s if mb[:end_date]
369
+ # By default listen to any value set in MBRecurringEndDate.
370
+ xml.tag! :MBRecurringNoEndDateFlag, mb[:no_end_date_flag] || 'N' # 'Y' || 'N' (Yes or No).
371
+ xml.tag! :MBRecurringMaxBillings, mb[:max_billings] if mb[:max_billings]
372
+ xml.tag! :MBRecurringFrequency, mb[:frequency] if mb[:frequency]
373
+ xml.tag! :MBDeferredBillDate, mb[:deferred_bill_date] if mb[:deferred_bill_date]
374
+ xml.tag! :MBMicroPaymentMaxDollarValue, mb[:max_dollar_value] if mb[:max_dollar_value]
375
+ xml.tag! :MBMicroPaymentMaxBillingDays, mb[:max_billing_days] if mb[:max_billing_days]
376
+ xml.tag! :MBMicroPaymentMaxTransactions, mb[:max_transactions] if mb[:max_transactions]
377
+ end
378
+ end
272
379
 
273
380
  def parse(body)
274
381
  response = {}
@@ -291,8 +398,10 @@ module ActiveMerchant #:nodoc:
291
398
  end
292
399
  end
293
400
 
294
- def commit(order, message_type)
401
+ def commit(order, message_type, trace_number=nil)
295
402
  headers = POST_HEADERS.merge("Content-length" => order.size.to_s)
403
+ headers.merge!( "Trace-number" => trace_number.to_s,
404
+ "Merchant-Id" => @options[:merchant_id] ) if @options[:retry_logic] && trace_number
296
405
  request = lambda{|url| parse(ssl_post(url, order, headers))}
297
406
 
298
407
  # Failover URL will be attempted in the event of a connection error
@@ -314,9 +423,9 @@ module ActiveMerchant #:nodoc:
314
423
 
315
424
  def remote_url(url=:primary)
316
425
  if url == :primary
317
- self.test? ? self.test_url : self.live_url
426
+ (self.test? ? self.test_url : self.live_url)
318
427
  else
319
- self.test? ? self.secondary_test_url : self.secondary_live_url
428
+ (self.test? ? self.secondary_test_url : self.secondary_live_url)
320
429
  end
321
430
  end
322
431
 
@@ -345,7 +454,18 @@ module ActiveMerchant #:nodoc:
345
454
  xml.tag! :Request do
346
455
  xml.tag! :NewOrder do
347
456
  add_xml_credentials(xml)
348
- xml.tag! :IndustryType, parameters[:industry_type] || "EC"
457
+ # EC - Ecommerce transaction
458
+ # RC - Recurring Payment transaction
459
+ # MO - Mail Order Telephone Order transaction
460
+ # IV - Interactive Voice Response
461
+ # IN - Interactive Voice Response
462
+ xml.tag! :IndustryType, parameters[:industry_type] || ECOMMERCE_TRANSACTION
463
+ # A - Auth Only No Capture
464
+ # AC - Auth and Capture
465
+ # F - Force Auth No Capture and no online authorization
466
+ # FR - Force Auth No Capture and no online authorization
467
+ # FC - Force Auth and Capture no online authorization
468
+ # R - Refund and Capture no online authorization
349
469
  xml.tag! :MessageType, action
350
470
  add_bin_merchant_and_terminal(xml, parameters)
351
471
 
@@ -355,6 +475,8 @@ module ActiveMerchant #:nodoc:
355
475
  xml.tag! :Amount, amount(money)
356
476
  xml.tag! :Comments, parameters[:comments] if parameters[:comments]
357
477
 
478
+ # CustomerAni, AVSPhoneType and AVSDestPhoneType could be added here.
479
+
358
480
  if parameters[:soft_descriptors].is_a?(OrbitalSoftDescriptors)
359
481
  add_soft_descriptors(xml, parameters[:soft_descriptors])
360
482
  end
@@ -362,7 +484,7 @@ module ActiveMerchant #:nodoc:
362
484
  set_recurring_ind(xml, parameters)
363
485
 
364
486
  # Append Transaction Reference Number at the end for Refund transactions
365
- if action == "R"
487
+ if action == REFUND
366
488
  tx_ref_num, _ = split_authorization(parameters[:authorization])
367
489
  xml.tag! :TxRefNum, tx_ref_num
368
490
  end
@@ -396,7 +518,7 @@ module ActiveMerchant #:nodoc:
396
518
  xml.target!
397
519
  end
398
520
 
399
- def build_void_request_xml(money, authorization, parameters = {})
521
+ def build_void_request_xml(authorization, parameters = {})
400
522
  tx_ref_num, order_id = split_authorization(authorization)
401
523
  xml = xml_envelope
402
524
  xml.tag! :Request do
@@ -404,9 +526,11 @@ module ActiveMerchant #:nodoc:
404
526
  add_xml_credentials(xml)
405
527
  xml.tag! :TxRefNum, tx_ref_num
406
528
  xml.tag! :TxRefIdx, parameters[:transaction_index]
407
- xml.tag! :AdjustedAmt, amount(money)
529
+ xml.tag! :AdjustedAmt, parameters[:amount] # setting adjusted amount to nil will void entire amount
408
530
  xml.tag! :OrderID, format_order_id(order_id || parameters[:order_id])
409
531
  add_bin_merchant_and_terminal(xml, parameters)
532
+ xml.tag! :ReversalRetryNumber, parameters[:reversal_retry_number] if parameters[:reversal_retry_number]
533
+ xml.tag! :OnlineReversalInd, parameters[:online_reversal_ind] if parameters[:online_reversal_ind]
410
534
  end
411
535
  end
412
536
  xml.target!
@@ -431,8 +555,10 @@ module ActiveMerchant #:nodoc:
431
555
  end
432
556
 
433
557
  def add_xml_credentials(xml)
434
- xml.tag! :OrbitalConnectionUsername, @options[:login] unless ip_authentication?
435
- xml.tag! :OrbitalConnectionPassword, @options[:password] unless ip_authentication?
558
+ unless ip_authentication?
559
+ xml.tag! :OrbitalConnectionUsername, @options[:login]
560
+ xml.tag! :OrbitalConnectionPassword, @options[:password]
561
+ end
436
562
  end
437
563
 
438
564
  def add_bin_merchant_and_terminal(xml, parameters)
@@ -471,22 +597,33 @@ module ActiveMerchant #:nodoc:
471
597
  add_customer_address(xml, options)
472
598
 
473
599
  xml.tag! :CustomerProfileAction, options[:customer_profile_action] # C, R, U, D
474
- xml.tag! :CustomerProfileOrderOverrideInd, options[:customer_profile_order_override_ind] || 'NO'
475
-
476
- if options[:customer_profile_action] == 'C'
477
- xml.tag! :CustomerProfileFromOrderInd, options[:customer_ref_num] ? 'S' : 'A'
600
+ # NO No mapping to order data
601
+ # OI Use <CustomerRefNum> for <OrderID>
602
+ # OD Use <CustomerReferNum> for <Comments>
603
+ # OA Use <CustomerRefNum> for <OrderID> and <Comments>
604
+ xml.tag! :CustomerProfileOrderOverrideInd, options[:customer_profile_order_override_ind] || NO_MAPPING_TO_ORDER_DATA
605
+
606
+ if options[:customer_profile_action] == CREATE
607
+ # A Auto-Generate the CustomerRefNum
608
+ # S Use CustomerRefNum field
609
+ # O Use OrderID field
610
+ # D Use Comments field
611
+ xml.tag! :CustomerProfileFromOrderInd, (options[:customer_ref_num] ? USE_CUSTOMER_REF_NUM : AUTO_GENERATE)
478
612
  end
479
613
 
480
614
  xml.tag! :OrderDefaultDescription, options[:order_default_description][0..63] if options[:order_default_description]
481
615
  xml.tag! :OrderDefaultAmount, options[:order_default_amount] if options[:order_default_amount]
482
616
 
483
- if ['C', 'U'].include? options[:customer_profile_action]
617
+ if [CREATE, UPDATE].include? options[:customer_profile_action]
484
618
  xml.tag! :CustomerAccountType, 'CC' # Only credit card supported
485
- xml.tag! :Status, options[:status] || 'A' # Active
619
+ xml.tag! :Status, options[:status] || ACTIVE # Active
486
620
  end
487
621
 
488
622
  xml.tag! :CCAccountNum, creditcard.number if creditcard
489
623
  xml.tag! :CCExpireDate, creditcard.expiry_date.expiration.strftime("%m%y") if creditcard
624
+
625
+ # This has to come after CCExpireDate.
626
+ add_managed_billing(xml, options)
490
627
  end
491
628
  end
492
629
  xml.target!
@@ -1,3 +1,4 @@
1
+ require 'nokogiri'
1
2
  module ActiveMerchant #:nodoc:
2
3
  module Billing #:nodoc:
3
4
  module PayflowCommonAPI
@@ -139,17 +140,18 @@ module ActiveMerchant #:nodoc:
139
140
 
140
141
  def parse(data)
141
142
  response = {}
142
- xml = REXML::Document.new(data)
143
- root = REXML::XPath.first(xml, "//ResponseData")
143
+ xml = Nokogiri::XML(data)
144
+ xml.remove_namespaces!
145
+ root = xml.xpath("//ResponseData")
144
146
 
145
147
  # REXML::XPath in Ruby 1.8.6 is now unable to match nodes based on their attributes
146
- tx_result = REXML::XPath.first(root, "//TransactionResult")
148
+ tx_result = root.xpath(".//TransactionResult").first
147
149
 
148
- if tx_result && tx_result.attributes['Duplicate'] == "true"
150
+ if tx_result && tx_result.attributes['Duplicate'].to_s == "true"
149
151
  response[:duplicate] = true
150
152
  end
151
153
 
152
- root.elements.to_a.each do |node|
154
+ root.xpath(".//*").each do |node|
153
155
  parse_element(response, node)
154
156
  end
155
157
 
@@ -165,14 +167,14 @@ module ActiveMerchant #:nodoc:
165
167
  # in an RPPaymentResults element so we'll come here multiple times
166
168
  response[node_name] ||= []
167
169
  response[node_name] << ( payment_result_response = {} )
168
- node.elements.each{ |e| parse_element(payment_result_response, e) }
169
- when node.has_elements?
170
- node.elements.each{|e| parse_element(response, e) }
170
+ node.xpath(".//*").each{ |e| parse_element(payment_result_response, e) }
171
+ when node.xpath(".//*").to_a.any?
172
+ node.xpath(".//*").each{|e| parse_element(response, e) }
171
173
  when node_name.to_s =~ /amt$/
172
174
  # *Amt elements don't put the value in the #text - instead they use a Currency attribute
173
- response[node_name] = node.attributes['Currency']
175
+ response[node_name] = node.attributes['Currency'].to_s
174
176
  when node_name == :ext_data
175
- response[node.attributes['Name'].underscore.to_sym] = node.attributes['Value']
177
+ response[node.attributes['Name'].to_s.underscore.to_sym] = node.attributes['Value'].to_s
176
178
  else
177
179
  response[node_name] = node.text
178
180
  end
@@ -140,6 +140,7 @@ module ActiveMerchant #:nodoc:
140
140
  add_amount(result, money, options)
141
141
  add_invoice(result, options)
142
142
  add_address_verification_data(result, options)
143
+ add_optional_elements(result, options)
143
144
  result
144
145
  end
145
146
 
@@ -149,6 +150,7 @@ module ActiveMerchant #:nodoc:
149
150
  add_amount(result, money, options)
150
151
  add_invoice(result, options)
151
152
  add_reference(result, identification)
153
+ add_optional_elements(result, options)
152
154
  result
153
155
  end
154
156
 
@@ -157,6 +159,7 @@ module ActiveMerchant #:nodoc:
157
159
  add_credit_card(result, credit_card)
158
160
  add_amount(result, 100, options) #need to make an auth request for $1
159
161
  add_token_request(result, options)
162
+ add_optional_elements(result, options)
160
163
  result
161
164
  end
162
165
 
@@ -209,7 +212,7 @@ module ActiveMerchant #:nodoc:
209
212
 
210
213
  def add_invoice(xml, options)
211
214
  xml.add_element("TxnId").text = options[:order_id].to_s.slice(0, 16) unless options[:order_id].blank?
212
- xml.add_element("MerchantReference").text = options[:description] unless options[:description].blank?
215
+ xml.add_element("MerchantReference").text = options[:description].to_s.slice(0, 50) unless options[:description].blank?
213
216
  end
214
217
 
215
218
  def add_address_verification_data(xml, options)
@@ -223,6 +226,52 @@ module ActiveMerchant #:nodoc:
223
226
  xml.add_element("AvsPostCode").text = address[:zip]
224
227
  end
225
228
 
229
+ # The options hash may contain optional data which will be passed
230
+ # through the the specialized optional fields at PaymentExpress
231
+ # as follows:
232
+ #
233
+ # {
234
+ # :client_type => :web, # Possible values are: :web, :ivr, :moto, :unattended, :internet, or :recurring
235
+ # :txn_data1 => "String up to 255 characters",
236
+ # :txn_data2 => "String up to 255 characters",
237
+ # :txn_data3 => "String up to 255 characters"
238
+ # }
239
+ #
240
+ # +:client_type+, while not documented for PxPost, will be sent as
241
+ # the +ClientType+ XML element as described in the documentation for
242
+ # the PaymentExpress WebService: http://www.paymentexpress.com/Technical_Resources/Ecommerce_NonHosted/WebService#clientType
243
+ # (PaymentExpress have confirmed that this value works the same in PxPost).
244
+ # The value sent for +:client_type+ will be normalized and sent
245
+ # as one of the explicit values allowed by PxPost:
246
+ #
247
+ # :web => "Web"
248
+ # :ivr => "IVR"
249
+ # :moto => "MOTO"
250
+ # :unattended => "Unattended"
251
+ # :internet => "Internet"
252
+ # :recurring => "Recurring"
253
+ #
254
+ # If you set the +:client_type+ to any value not listed above,
255
+ # the ClientType element WILL NOT BE INCLUDED at all in the
256
+ # POST data.
257
+ #
258
+ # +:txn_data1+, +:txn_data2+, and +:txn_data3+ will be sent as
259
+ # +TxnData1+, +TxnData2+, and +TxnData3+, respectively, and are
260
+ # free form fields of the merchant's choosing, as documented here:
261
+ # http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#txndata
262
+ #
263
+ # These optional elements are added to all transaction types:
264
+ # +purchase+, +authorize+, +capture+, +refund+, +store+
265
+ def add_optional_elements(xml, options)
266
+ if client_type = normalized_client_type(options[:client_type])
267
+ xml.add_element("ClientType").text = client_type
268
+ end
269
+
270
+ xml.add_element("TxnData1").text = options[:txn_data1].to_s.slice(0,255) unless options[:txn_data1].blank?
271
+ xml.add_element("TxnData2").text = options[:txn_data2].to_s.slice(0,255) unless options[:txn_data2].blank?
272
+ xml.add_element("TxnData3").text = options[:txn_data3].to_s.slice(0,255) unless options[:txn_data3].blank?
273
+ end
274
+
226
275
  def new_transaction
227
276
  REXML::Document.new.add_element("Txn")
228
277
  end
@@ -266,6 +315,18 @@ module ActiveMerchant #:nodoc:
266
315
  def format_date(month, year)
267
316
  "#{format(month, :two_digits)}#{format(year, :two_digits)}"
268
317
  end
318
+
319
+ def normalized_client_type(client_type_from_options)
320
+ case client_type_from_options.to_s.downcase
321
+ when 'web' then "Web"
322
+ when 'ivr' then "IVR"
323
+ when 'moto' then "MOTO"
324
+ when 'unattended' then "Unattended"
325
+ when 'internet' then "Internet"
326
+ when 'recurring' then "Recurring"
327
+ else nil
328
+ end
329
+ end
269
330
  end
270
331
 
271
332
  class PaymentExpressResponse < Response