johnreitano-activemerchant 1.5.2

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 (131) hide show
  1. data/CHANGELOG +508 -0
  2. data/CONTRIBUTORS +134 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +136 -0
  5. data/gem-public_cert.pem +20 -0
  6. data/lib/active_merchant/billing/avs_result.rb +98 -0
  7. data/lib/active_merchant/billing/base.rb +57 -0
  8. data/lib/active_merchant/billing/check.rb +68 -0
  9. data/lib/active_merchant/billing/credit_card.rb +159 -0
  10. data/lib/active_merchant/billing/credit_card_formatting.rb +21 -0
  11. data/lib/active_merchant/billing/credit_card_methods.rb +125 -0
  12. data/lib/active_merchant/billing/cvv_result.rb +38 -0
  13. data/lib/active_merchant/billing/expiry_date.rb +34 -0
  14. data/lib/active_merchant/billing/gateway.rb +163 -0
  15. data/lib/active_merchant/billing/gateways/authorize_net.rb +654 -0
  16. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +736 -0
  17. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +244 -0
  18. data/lib/active_merchant/billing/gateways/beanstream.rb +102 -0
  19. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
  20. data/lib/active_merchant/billing/gateways/bogus.rb +98 -0
  21. data/lib/active_merchant/billing/gateways/braintree.rb +17 -0
  22. data/lib/active_merchant/billing/gateways/card_stream.rb +230 -0
  23. data/lib/active_merchant/billing/gateways/cyber_source.rb +594 -0
  24. data/lib/active_merchant/billing/gateways/data_cash.rb +593 -0
  25. data/lib/active_merchant/billing/gateways/efsnet.rb +229 -0
  26. data/lib/active_merchant/billing/gateways/elavon.rb +134 -0
  27. data/lib/active_merchant/billing/gateways/eway.rb +277 -0
  28. data/lib/active_merchant/billing/gateways/exact.rb +222 -0
  29. data/lib/active_merchant/billing/gateways/first_pay.rb +172 -0
  30. data/lib/active_merchant/billing/gateways/instapay.rb +164 -0
  31. data/lib/active_merchant/billing/gateways/jetpay.rb +270 -0
  32. data/lib/active_merchant/billing/gateways/linkpoint.rb +449 -0
  33. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +154 -0
  34. data/lib/active_merchant/billing/gateways/merchant_ware.rb +283 -0
  35. data/lib/active_merchant/billing/gateways/modern_payments.rb +36 -0
  36. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +220 -0
  37. data/lib/active_merchant/billing/gateways/moneris.rb +205 -0
  38. data/lib/active_merchant/billing/gateways/net_registry.rb +189 -0
  39. data/lib/active_merchant/billing/gateways/netbilling.rb +168 -0
  40. data/lib/active_merchant/billing/gateways/ogone.rb +279 -0
  41. data/lib/active_merchant/billing/gateways/pay_junction.rb +392 -0
  42. data/lib/active_merchant/billing/gateways/pay_secure.rb +120 -0
  43. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +207 -0
  44. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
  45. data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
  46. data/lib/active_merchant/billing/gateways/payflow.rb +236 -0
  47. data/lib/active_merchant/billing/gateways/payflow_express.rb +138 -0
  48. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
  49. data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
  50. data/lib/active_merchant/billing/gateways/payment_express.rb +230 -0
  51. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +326 -0
  52. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +38 -0
  53. data/lib/active_merchant/billing/gateways/paypal.rb +121 -0
  54. data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
  55. data/lib/active_merchant/billing/gateways/paypal_express.rb +130 -0
  56. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +20 -0
  57. data/lib/active_merchant/billing/gateways/plugnpay.rb +292 -0
  58. data/lib/active_merchant/billing/gateways/psigate.rb +214 -0
  59. data/lib/active_merchant/billing/gateways/psl_card.rb +304 -0
  60. data/lib/active_merchant/billing/gateways/quickpay.rb +213 -0
  61. data/lib/active_merchant/billing/gateways/realex.rb +200 -0
  62. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +88 -0
  63. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +116 -0
  64. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
  65. data/lib/active_merchant/billing/gateways/sage.rb +146 -0
  66. data/lib/active_merchant/billing/gateways/sage_pay.rb +309 -0
  67. data/lib/active_merchant/billing/gateways/sallie_mae.rb +144 -0
  68. data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
  69. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +157 -0
  70. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +113 -0
  71. data/lib/active_merchant/billing/gateways/skip_jack.rb +453 -0
  72. data/lib/active_merchant/billing/gateways/smart_ps.rb +265 -0
  73. data/lib/active_merchant/billing/gateways/trans_first.rb +127 -0
  74. data/lib/active_merchant/billing/gateways/transax.rb +25 -0
  75. data/lib/active_merchant/billing/gateways/trust_commerce.rb +418 -0
  76. data/lib/active_merchant/billing/gateways/usa_epay.rb +194 -0
  77. data/lib/active_merchant/billing/gateways/verifi.rb +228 -0
  78. data/lib/active_merchant/billing/gateways/viaklix.rb +189 -0
  79. data/lib/active_merchant/billing/gateways/wirecard.rb +318 -0
  80. data/lib/active_merchant/billing/gateways.rb +18 -0
  81. data/lib/active_merchant/billing/integrations/action_view_helper.rb +79 -0
  82. data/lib/active_merchant/billing/integrations/bogus/helper.rb +17 -0
  83. data/lib/active_merchant/billing/integrations/bogus/notification.rb +11 -0
  84. data/lib/active_merchant/billing/integrations/bogus/return.rb +10 -0
  85. data/lib/active_merchant/billing/integrations/bogus.rb +23 -0
  86. data/lib/active_merchant/billing/integrations/chronopay/helper.rb +120 -0
  87. data/lib/active_merchant/billing/integrations/chronopay/notification.rb +158 -0
  88. data/lib/active_merchant/billing/integrations/chronopay/return.rb +10 -0
  89. data/lib/active_merchant/billing/integrations/chronopay.rb +23 -0
  90. data/lib/active_merchant/billing/integrations/gestpay/common.rb +42 -0
  91. data/lib/active_merchant/billing/integrations/gestpay/helper.rb +70 -0
  92. data/lib/active_merchant/billing/integrations/gestpay/notification.rb +85 -0
  93. data/lib/active_merchant/billing/integrations/gestpay/return.rb +10 -0
  94. data/lib/active_merchant/billing/integrations/gestpay.rb +25 -0
  95. data/lib/active_merchant/billing/integrations/helper.rb +93 -0
  96. data/lib/active_merchant/billing/integrations/hi_trust/helper.rb +58 -0
  97. data/lib/active_merchant/billing/integrations/hi_trust/notification.rb +59 -0
  98. data/lib/active_merchant/billing/integrations/hi_trust/return.rb +67 -0
  99. data/lib/active_merchant/billing/integrations/hi_trust.rb +27 -0
  100. data/lib/active_merchant/billing/integrations/nochex/helper.rb +68 -0
  101. data/lib/active_merchant/billing/integrations/nochex/notification.rb +94 -0
  102. data/lib/active_merchant/billing/integrations/nochex/return.rb +10 -0
  103. data/lib/active_merchant/billing/integrations/nochex.rb +88 -0
  104. data/lib/active_merchant/billing/integrations/notification.rb +62 -0
  105. data/lib/active_merchant/billing/integrations/paypal/helper.rb +119 -0
  106. data/lib/active_merchant/billing/integrations/paypal/notification.rb +154 -0
  107. data/lib/active_merchant/billing/integrations/paypal/return.rb +10 -0
  108. data/lib/active_merchant/billing/integrations/paypal.rb +39 -0
  109. data/lib/active_merchant/billing/integrations/quickpay/helper.rb +72 -0
  110. data/lib/active_merchant/billing/integrations/quickpay/notification.rb +74 -0
  111. data/lib/active_merchant/billing/integrations/quickpay.rb +17 -0
  112. data/lib/active_merchant/billing/integrations/return.rb +35 -0
  113. data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +59 -0
  114. data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +114 -0
  115. data/lib/active_merchant/billing/integrations/two_checkout/return.rb +17 -0
  116. data/lib/active_merchant/billing/integrations/two_checkout.rb +23 -0
  117. data/lib/active_merchant/billing/integrations.rb +29 -0
  118. data/lib/active_merchant/billing/response.rb +32 -0
  119. data/lib/active_merchant/billing.rb +9 -0
  120. data/lib/active_merchant/lib/connection.rb +170 -0
  121. data/lib/active_merchant/lib/country.rb +319 -0
  122. data/lib/active_merchant/lib/error.rb +4 -0
  123. data/lib/active_merchant/lib/post_data.rb +22 -0
  124. data/lib/active_merchant/lib/posts_data.rb +47 -0
  125. data/lib/active_merchant/lib/requires_parameters.rb +16 -0
  126. data/lib/active_merchant/lib/utils.rb +18 -0
  127. data/lib/active_merchant/lib/validateable.rb +76 -0
  128. data/lib/active_merchant.rb +46 -0
  129. data/lib/certs/cacert.pem +7815 -0
  130. data/lib/support/gateway_support.rb +58 -0
  131. metadata +218 -0
@@ -0,0 +1,449 @@
1
+ require 'rexml/document'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+
6
+ # Initialization Options
7
+ # :login Your store number
8
+ # :pem The text of your linkpoint PEM file. Note
9
+ # this is not the path to file, but its
10
+ # contents. If you are only using one PEM
11
+ # file on your site you can declare it
12
+ # globally and then you won't need to
13
+ # include this option
14
+ #
15
+ #
16
+ # A valid store number is required. Unfortunately, with LinkPoint
17
+ # YOU CAN'T JUST USE ANY OLD STORE NUMBER. Also, you can't just
18
+ # generate your own PEM file. You'll need to use a special PEM file
19
+ # provided by LinkPoint.
20
+ #
21
+ # Go to http://www.linkpoint.com/support/sup_teststore.asp to set up
22
+ # a test account and obtain your PEM file.
23
+ #
24
+ # Declaring PEM file Globally
25
+ # ActiveMerchant::Billing::LinkpointGateway.pem_file = File.read( File.dirname(__FILE__) + '/../mycert.pem' )
26
+ #
27
+ #
28
+ # Valid Order Options
29
+ # :result =>
30
+ # LIVE Production mode
31
+ # GOOD Approved response in test mode
32
+ # DECLINE Declined response in test mode
33
+ # DUPLICATE Duplicate response in test mode
34
+ #
35
+ # :ponumber Order number
36
+ #
37
+ # :transactionorigin => Source of the transaction
38
+ # ECI Email or Internet
39
+ # MAIL Mail order
40
+ # MOTO Mail order/Telephone
41
+ # TELEPHONE Telephone
42
+ # RETAIL Face-to-face
43
+ #
44
+ # :ordertype =>
45
+ # SALE Real live sale
46
+ # PREAUTH Authorize only
47
+ # POSTAUTH Forced Ticket or Ticket Only transaction
48
+ # VOID
49
+ # CREDIT
50
+ # CALCSHIPPING For shipping charges calculations
51
+ # CALCTAX For sales tax calculations
52
+ #
53
+ # Recurring Options
54
+ # :action =>
55
+ # SUBMIT
56
+ # MODIFY
57
+ # CANCEL
58
+ #
59
+ # :installments Identifies how many recurring payments to charge the customer
60
+ # :startdate Date to begin charging the recurring payments. Format: YYYYMMDD or "immediate"
61
+ # :periodicity =>
62
+ # MONTHLY
63
+ # BIMONTHLY
64
+ # WEEKLY
65
+ # BIWEEKLY
66
+ # YEARLY
67
+ # DAILY
68
+ # :threshold Tells how many times to retry the transaction (if it fails) before contacting the merchant.
69
+ # :comments Uh... comments
70
+ #
71
+ #
72
+ # For reference:
73
+ #
74
+ # https://www.linkpointcentral.com/lpc/docs/Help/APIHelp/lpintguide.htm
75
+ #
76
+ # Entities = {
77
+ # :payment => [:subtotal, :tax, :vattax, :shipping, :chargetotal],
78
+ # :billing => [:name, :address1, :address2, :city, :state, :zip, :country, :email, :phone, :fax, :addrnum],
79
+ # :shipping => [:name, :address1, :address2, :city, :state, :zip, :country, :weight, :items, :carrier, :total],
80
+ # :creditcard => [:cardnumber, :cardexpmonth, :cardexpyear, :cvmvalue, :track],
81
+ # :telecheck => [:routing, :account, :checknumber, :bankname, :bankstate, :dl, :dlstate, :void, :accounttype, :ssn],
82
+ # :transactiondetails => [:transactionorigin, :oid, :ponumber, :taxexempt, :terminaltype, :ip, :reference_number, :recurring, :tdate],
83
+ # :periodic => [:action, :installments, :threshold, :startdate, :periodicity, :comments],
84
+ # :notes => [:comments, :referred]
85
+ # :items => [:item => [:price, :quantity, :description, :id, :options => [:option => [:name, :value]]]]
86
+ # }
87
+ #
88
+ #
89
+ # LinkPoint's Items entity is an optional entity that can be attached to orders.
90
+ # It is entered as :line_items to be consistent with the CyberSource implementation
91
+ #
92
+ # The line_item hash goes in the options hash and should look like
93
+ #
94
+ # :line_items => [
95
+ # {
96
+ # :id => '123456',
97
+ # :description => 'Logo T-Shirt',
98
+ # :price => '12.00',
99
+ # :quantity => '1',
100
+ # :options => [
101
+ # {
102
+ # :name => 'Color',
103
+ # :value => 'Red'
104
+ # },
105
+ # {
106
+ # :name => 'Size',
107
+ # :value => 'XL'
108
+ # }
109
+ # ]
110
+ # },
111
+ # {
112
+ # :id => '111',
113
+ # :description => 'keychain',
114
+ # :price => '3.00',
115
+ # :quantity => '1'
116
+ # }
117
+ # ]
118
+ # This functionality is only supported by this particular gateway may
119
+ # be changed at any time
120
+ #
121
+ class LinkpointGateway < Gateway
122
+ # Your global PEM file. This will be assigned to you by linkpoint
123
+ #
124
+ # Example:
125
+ #
126
+ # ActiveMerchant::Billing::LinkpointGateway.pem_file = File.read( File.dirname(__FILE__) + '/../mycert.pem' )
127
+ #
128
+ cattr_accessor :pem_file
129
+
130
+ TEST_URL = 'https://staging.linkpt.net:1129/'
131
+ LIVE_URL = 'https://secure.linkpt.net:1129/'
132
+
133
+ # We don't have the certificate to verify LinkPoint
134
+ self.ssl_strict = false
135
+
136
+ self.supported_countries = ['US']
137
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club]
138
+ self.homepage_url = 'http://www.linkpoint.com/'
139
+ self.display_name = 'LinkPoint'
140
+
141
+ def initialize(options = {})
142
+ requires!(options, :login)
143
+
144
+ @options = {
145
+ :result => 'LIVE',
146
+ :pem => LinkpointGateway.pem_file
147
+ }.update(options)
148
+
149
+ raise ArgumentError, "You need to pass in your pem file using the :pem parameter or set it globally using ActiveMerchant::Billing::LinkpointGateway.pem_file = File.read( File.dirname(__FILE__) + '/../mycert.pem' ) or similar" if @options[:pem].blank?
150
+ end
151
+
152
+ # Send a purchase request with periodic options
153
+ # Recurring Options
154
+ # :action =>
155
+ # SUBMIT
156
+ # MODIFY
157
+ # CANCEL
158
+ #
159
+ # :installments Identifies how many recurring payments to charge the customer
160
+ # :startdate Date to begin charging the recurring payments. Format: YYYYMMDD or "immediate"
161
+ # :periodicity =>
162
+ # :monthly
163
+ # :bimonthly
164
+ # :weekly
165
+ # :biweekly
166
+ # :yearly
167
+ # :daily
168
+ # :threshold Tells how many times to retry the transaction (if it fails) before contacting the merchant.
169
+ # :comments Uh... comments
170
+ #
171
+ def recurring(money, creditcard, options={})
172
+ requires!(options, [:periodicity, :bimonthly, :monthly, :biweekly, :weekly, :yearly, :daily], :installments, :order_id )
173
+
174
+ options.update(
175
+ :ordertype => "SALE",
176
+ :action => options[:action] || "SUBMIT",
177
+ :installments => options[:installments] || 12,
178
+ :startdate => options[:startdate] || "immediate",
179
+ :periodicity => options[:periodicity].to_s || "monthly",
180
+ :comments => options[:comments] || nil,
181
+ :threshold => options[:threshold] || 3
182
+ )
183
+ commit(money, creditcard, options)
184
+ end
185
+
186
+ # Buy the thing
187
+ def purchase(money, creditcard, options={})
188
+ requires!(options, :order_id)
189
+ options.update(
190
+ :ordertype => "SALE"
191
+ )
192
+ commit(money, creditcard, options)
193
+ end
194
+
195
+ #
196
+ # Authorize the transaction
197
+ #
198
+ # Reserves the funds on the customer's credit card, but does not charge the card.
199
+ #
200
+ def authorize(money, creditcard, options = {})
201
+ requires!(options, :order_id)
202
+ options.update(
203
+ :ordertype => "PREAUTH"
204
+ )
205
+ commit(money, creditcard, options)
206
+ end
207
+
208
+ #
209
+ # Post an authorization.
210
+ #
211
+ # Captures the funds from an authorized transaction.
212
+ # Order_id must be a valid order id from a prior authorized transaction.
213
+ #
214
+ def capture(money, authorization, options = {})
215
+ options.update(
216
+ :order_id => authorization,
217
+ :ordertype => "POSTAUTH"
218
+ )
219
+ commit(money, nil, options)
220
+ end
221
+
222
+ # Void a previous transaction
223
+ def void(identification, options = {})
224
+ options.update(
225
+ :order_id => identification,
226
+ :ordertype => "VOID"
227
+ )
228
+ commit(nil, nil, options)
229
+ end
230
+
231
+ #
232
+ # Refund an order
233
+ #
234
+ # identification must be a valid order id previously submitted by SALE
235
+ #
236
+ def credit(money, identification, options = {})
237
+ options.update(
238
+ :ordertype => "CREDIT",
239
+ :order_id => identification
240
+ )
241
+ commit(money, nil, options)
242
+ end
243
+
244
+ def test?
245
+ @options[:test] || super
246
+ end
247
+
248
+ private
249
+ # Commit the transaction by posting the XML file to the LinkPoint server
250
+ def commit(money, creditcard, options = {})
251
+ response = parse(ssl_post(test? ? TEST_URL : LIVE_URL, post_data(money, creditcard, options)))
252
+
253
+ Response.new(successful?(response), response[:message], response,
254
+ :test => test?,
255
+ :authorization => response[:ordernum],
256
+ :avs_result => { :code => response[:avs].to_s[2,1] },
257
+ :cvv_result => response[:avs].to_s[3,1]
258
+ )
259
+ end
260
+
261
+ def successful?(response)
262
+ response[:approved] == "APPROVED"
263
+ end
264
+
265
+ # Build the XML file
266
+ def post_data(money, creditcard, options)
267
+ params = parameters(money, creditcard, options)
268
+
269
+ xml = REXML::Document.new
270
+ order = xml.add_element("order")
271
+
272
+ # Merchant Info
273
+ merchantinfo = order.add_element("merchantinfo")
274
+ merchantinfo.add_element("configfile").text = @options[:login]
275
+
276
+ # Loop over the params hash to construct the XML string
277
+ for key, value in params
278
+ elem = order.add_element(key.to_s)
279
+ if key == :items
280
+ build_items(elem, value)
281
+ else
282
+ for k, v in params[key]
283
+ elem.add_element(k.to_s).text = params[key][k].to_s if params[key][k]
284
+ end
285
+ end
286
+ # Linkpoint doesn't understand empty elements:
287
+ order.delete(elem) if elem.size == 0
288
+ end
289
+ return xml.to_s
290
+ end
291
+
292
+ # adds LinkPoint's Items entity to the XML. Called from post_data
293
+ def build_items(element, items)
294
+ for item in items
295
+ item_element = element.add_element("item")
296
+ for key, value in item
297
+ if key == :options
298
+ options_element = item_element.add_element("options")
299
+ for option in value
300
+ opt_element = options_element.add_element("option")
301
+ opt_element.add_element("name").text = option[:name] unless option[:name].blank?
302
+ opt_element.add_element("value").text = option[:value] unless option[:value].blank?
303
+ end
304
+ else
305
+ item_element.add_element(key.to_s).text = item[key].to_s unless item[key].blank?
306
+ end
307
+ end
308
+ end
309
+ end
310
+
311
+ # Set up the parameters hash just once so we don't have to do it
312
+ # for every action.
313
+ def parameters(money, creditcard, options = {})
314
+
315
+ params = {
316
+ :payment => {
317
+ :subtotal => amount(options[:subtotal]),
318
+ :tax => amount(options[:tax]),
319
+ :vattax => amount(options[:vattax]),
320
+ :shipping => amount(options[:shipping]),
321
+ :chargetotal => amount(money)
322
+ },
323
+ :transactiondetails => {
324
+ :transactionorigin => options[:transactionorigin] || "ECI",
325
+ :oid => options[:order_id],
326
+ :ponumber => options[:ponumber],
327
+ :taxexempt => options[:taxexempt],
328
+ :terminaltype => options[:terminaltype],
329
+ :ip => options[:ip],
330
+ :reference_number => options[:reference_number],
331
+ :recurring => options[:recurring] || "NO", #DO NOT USE if you are using the periodic billing option.
332
+ :tdate => options[:tdate]
333
+ },
334
+ :orderoptions => {
335
+ :ordertype => options[:ordertype],
336
+ :result => @options[:result]
337
+ },
338
+ :periodic => {
339
+ :action => options[:action],
340
+ :installments => options[:installments],
341
+ :threshold => options[:threshold],
342
+ :startdate => options[:startdate],
343
+ :periodicity => options[:periodicity],
344
+ :comments => options[:comments]
345
+ },
346
+ :telecheck => {
347
+ :routing => options[:telecheck_routing],
348
+ :account => options[:telecheck_account],
349
+ :checknumber => options[:telecheck_checknumber],
350
+ :bankname => options[:telecheck_bankname],
351
+ :dl => options[:telecheck_dl],
352
+ :dlstate => options[:telecheck_dlstate],
353
+ :void => options[:telecheck_void],
354
+ :accounttype => options[:telecheck_accounttype],
355
+ :ssn => options[:telecheck_ssn],
356
+ }
357
+ }
358
+
359
+ if creditcard
360
+ params[:creditcard] = {
361
+ :cardnumber => creditcard.number,
362
+ :cardexpmonth => creditcard.month,
363
+ :cardexpyear => format_creditcard_expiry_year(creditcard.year),
364
+ :track => nil
365
+ }
366
+
367
+ if creditcard.verification_value?
368
+ params[:creditcard][:cvmvalue] = creditcard.verification_value
369
+ params[:creditcard][:cvmindicator] = 'provided'
370
+ else
371
+ params[:creditcard][:cvmindicator] = 'not_provided'
372
+ end
373
+ end
374
+
375
+ if billing_address = options[:billing_address] || options[:address]
376
+
377
+ params[:billing] = {}
378
+ params[:billing][:name] = billing_address[:name] || (creditcard ? creditcard.name : nil)
379
+ params[:billing][:address1] = billing_address[:address1] unless billing_address[:address1].blank?
380
+ params[:billing][:address2] = billing_address[:address2] unless billing_address[:address2].blank?
381
+ params[:billing][:city] = billing_address[:city] unless billing_address[:city].blank?
382
+ params[:billing][:state] = billing_address[:state] unless billing_address[:state].blank?
383
+ params[:billing][:zip] = billing_address[:zip] unless billing_address[:zip].blank?
384
+ params[:billing][:country] = billing_address[:country] unless billing_address[:country].blank?
385
+ params[:billing][:company] = billing_address[:company] unless billing_address[:company].blank?
386
+ params[:billing][:phone] = billing_address[:phone] unless billing_address[:phone].blank?
387
+ params[:billing][:email] = options[:email] unless options[:email].blank?
388
+ end
389
+
390
+ if shipping_address = options[:shipping_address]
391
+
392
+ params[:shipping] = {}
393
+ params[:shipping][:name] = shipping_address[:name] || creditcard ? creditcard.name : nil
394
+ params[:shipping][:address1] = shipping_address[:address1] unless shipping_address[:address1].blank?
395
+ params[:shipping][:address2] = shipping_address[:address2] unless shipping_address[:address2].blank?
396
+ params[:shipping][:city] = shipping_address[:city] unless shipping_address[:city].blank?
397
+ params[:shipping][:state] = shipping_address[:state] unless shipping_address[:state].blank?
398
+ params[:shipping][:zip] = shipping_address[:zip] unless shipping_address[:zip].blank?
399
+ params[:shipping][:country] = shipping_address[:country] unless shipping_address[:country].blank?
400
+ end
401
+
402
+ params[:items] = options[:line_items] if options[:line_items]
403
+
404
+ return params
405
+ end
406
+
407
+ def parse(xml)
408
+
409
+ # For reference, a typical response...
410
+ # <r_csp></r_csp>
411
+ # <r_time></r_time>
412
+ # <r_ref></r_ref>
413
+ # <r_error></r_error>
414
+ # <r_ordernum></r_ordernum>
415
+ # <r_message>This is a test transaction and will not show up in the Reports</r_message>
416
+ # <r_code></r_code>
417
+ # <r_tdate>Thu Feb 2 15:40:21 2006</r_tdate>
418
+ # <r_score></r_score>
419
+ # <r_authresponse></r_authresponse>
420
+ # <r_approved>APPROVED</r_approved>
421
+ # <r_avs></r_avs>
422
+
423
+ response = {:message => "Global Error Receipt", :complete => false}
424
+
425
+ xml = REXML::Document.new("<response>#{xml}</response>")
426
+ xml.root.elements.each do |node|
427
+ response[node.name.downcase.sub(/^r_/, '').to_sym] = normalize(node.text)
428
+ end unless xml.root.nil?
429
+
430
+ response
431
+ end
432
+
433
+ # Make a ruby type out of the response string
434
+ def normalize(field)
435
+ case field
436
+ when "true" then true
437
+ when "false" then false
438
+ when "" then nil
439
+ when "null" then nil
440
+ else field
441
+ end
442
+ end
443
+
444
+ def format_creditcard_expiry_year(year)
445
+ sprintf("%.4i", year)[-2..-1]
446
+ end
447
+ end
448
+ end
449
+ end
@@ -0,0 +1,154 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class MerchantESolutionsGateway < Gateway
4
+
5
+ TEST_URL = 'https://test.merchante-solutions.com/mes-api/tridentApi'
6
+ LIVE_URL = 'https://api.merchante-solutions.com/mes-api/tridentApi'
7
+
8
+ # The countries the gateway supports merchants from as 2 digit ISO country codes
9
+ self.supported_countries = ['US']
10
+
11
+ # The card types supported by the payment gateway
12
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb]
13
+
14
+ # The homepage URL of the gateway
15
+ self.homepage_url = 'http://www.merchante-solutions.com/'
16
+
17
+ # The name of the gateway
18
+ self.display_name = 'Merchant e-Solutions'
19
+
20
+ def initialize(options = {})
21
+ requires!(options, :login, :password)
22
+ @options = options
23
+ super
24
+ end
25
+
26
+ def authorize(money, creditcard_or_card_id, options = {})
27
+ post = {}
28
+ add_invoice(post, options)
29
+ add_payment_source(post, creditcard_or_card_id, options)
30
+ add_address(post, options)
31
+ commit('P', money, post)
32
+ end
33
+
34
+ def purchase(money, creditcard_or_card_id, options = {})
35
+ post = {}
36
+ add_invoice(post, options)
37
+ add_payment_source(post, creditcard_or_card_id, options)
38
+ add_address(post, options)
39
+ commit('D', money, post)
40
+ end
41
+
42
+ def capture(money, transaction_id, options = {})
43
+ post ={}
44
+ post[:transaction_id] = transaction_id
45
+ commit('S', money, post)
46
+ end
47
+
48
+ def store(creditcard, options = {})
49
+ post = {}
50
+ add_creditcard(post, creditcard, options)
51
+ commit('T', nil, post)
52
+ end
53
+
54
+ def unstore(card_id)
55
+ post = {}
56
+ post[:card_id] = card_id
57
+ commit('X', nil, post)
58
+ end
59
+
60
+ def credit(money, creditcard_or_card_id, options = {})
61
+ post ={}
62
+ add_payment_source(post, creditcard_or_card_id, options)
63
+ commit('C', money, post)
64
+ end
65
+
66
+ def void(transaction_id)
67
+ post = {}
68
+ post[:transaction_id] = transaction_id
69
+ commit('V', nil, post)
70
+ end
71
+
72
+ private
73
+
74
+ def add_address(post, options)
75
+ if address = options[:billing_address] || options[:address]
76
+ post[:cardholder_street_address] = address[:address1].to_s.gsub(/[^\w.]/, '+')
77
+ post[:cardholder_zip] = address[:zip].to_s
78
+ end
79
+ end
80
+
81
+ def add_invoice(post, options)
82
+ if options.has_key? :order_id
83
+ post[:invoice_number] = options[:order_id].to_s.gsub(/[^\w.]/, '')
84
+ end
85
+ end
86
+
87
+ def add_payment_source(post, creditcard_or_card_id, options)
88
+ if creditcard_or_card_id.is_a?(String)
89
+ # using stored card
90
+ post[:card_id] = creditcard_or_card_id
91
+ else
92
+ # card info is provided
93
+ add_creditcard(post, creditcard_or_card_id, options)
94
+ end
95
+ end
96
+
97
+ def add_creditcard(post, creditcard, options)
98
+ post[:card_number] = creditcard.number
99
+ post[:cvv2] = creditcard.verification_value if creditcard.verification_value?
100
+ post[:card_exp_date] = expdate(creditcard)
101
+ end
102
+
103
+ def parse(body)
104
+ results = {}
105
+ body.split(/&/).each do |pair|
106
+ key,val = pair.split(/=/)
107
+ results[key] = val
108
+ end
109
+ results
110
+ end
111
+
112
+ def commit(action, money, parameters)
113
+
114
+ url = test? ? TEST_URL : LIVE_URL
115
+ parameters[:transaction_amount] = amount(money) if money unless action == 'V'
116
+
117
+ response = parse( ssl_post(url, post_data(action,parameters)) )
118
+
119
+ Response.new(response["error_code"] == "000", message_from(response), response,
120
+ :authorization => response["transaction_id"],
121
+ :test => test?,
122
+ :cvv_result => response["cvv2_result"],
123
+ :avs_result => { :code => response["avs_result"] }
124
+ )
125
+
126
+ end
127
+
128
+ def expdate(creditcard)
129
+ year = sprintf("%.4i", creditcard.year)
130
+ month = sprintf("%.2i", creditcard.month)
131
+ "#{month}#{year[-2..-1]}"
132
+ end
133
+
134
+ def message_from(response)
135
+ if response["error_code"] == "000"
136
+ "This transaction has been approved"
137
+ else
138
+ response["auth_response_text"]
139
+ end
140
+ end
141
+
142
+ def post_data(action, parameters = {})
143
+ post = {}
144
+ post[:profile_id] = @options[:login]
145
+ post[:profile_key] = @options[:password]
146
+ post[:transaction_type] = action if action
147
+
148
+ request = post.merge(parameters).map {|key,value| "#{key}=#{CGI.escape(value.to_s)}"}.join("&")
149
+ request
150
+ end
151
+ end
152
+ end
153
+ end
154
+