activemerchant 1.0.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 (55) hide show
  1. data/CHANGELOG +40 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +93 -0
  4. data/lib/active_merchant.rb +59 -0
  5. data/lib/active_merchant/billing/base.rb +52 -0
  6. data/lib/active_merchant/billing/credit_card.rb +217 -0
  7. data/lib/active_merchant/billing/gateway.rb +100 -0
  8. data/lib/active_merchant/billing/gateways.rb +15 -0
  9. data/lib/active_merchant/billing/gateways/authorize_net.rb +236 -0
  10. data/lib/active_merchant/billing/gateways/bogus.rb +92 -0
  11. data/lib/active_merchant/billing/gateways/eway.rb +235 -0
  12. data/lib/active_merchant/billing/gateways/linkpoint.rb +445 -0
  13. data/lib/active_merchant/billing/gateways/moneris.rb +205 -0
  14. data/lib/active_merchant/billing/gateways/payflow.rb +84 -0
  15. data/lib/active_merchant/billing/gateways/payflow/f73e89fd.0 +17 -0
  16. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +190 -0
  17. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +30 -0
  18. data/lib/active_merchant/billing/gateways/payflow_express.rb +123 -0
  19. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +9 -0
  20. data/lib/active_merchant/billing/gateways/payflow_uk.rb +17 -0
  21. data/lib/active_merchant/billing/gateways/paypal.rb +90 -0
  22. data/lib/active_merchant/billing/gateways/paypal/api_cert_chain.crt +35 -0
  23. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +208 -0
  24. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +30 -0
  25. data/lib/active_merchant/billing/gateways/paypal_express.rb +115 -0
  26. data/lib/active_merchant/billing/gateways/psigate.rb +265 -0
  27. data/lib/active_merchant/billing/gateways/trust_commerce.rb +330 -0
  28. data/lib/active_merchant/billing/gateways/usa_epay.rb +189 -0
  29. data/lib/active_merchant/billing/integrations.rb +12 -0
  30. data/lib/active_merchant/billing/integrations/action_view_helper.rb +65 -0
  31. data/lib/active_merchant/billing/integrations/bogus.rb +17 -0
  32. data/lib/active_merchant/billing/integrations/bogus/helper.rb +17 -0
  33. data/lib/active_merchant/billing/integrations/bogus/notification.rb +11 -0
  34. data/lib/active_merchant/billing/integrations/chronopay.rb +17 -0
  35. data/lib/active_merchant/billing/integrations/chronopay/helper.rb +81 -0
  36. data/lib/active_merchant/billing/integrations/chronopay/notification.rb +156 -0
  37. data/lib/active_merchant/billing/integrations/gestpay.rb +21 -0
  38. data/lib/active_merchant/billing/integrations/gestpay/common.rb +42 -0
  39. data/lib/active_merchant/billing/integrations/gestpay/helper.rb +72 -0
  40. data/lib/active_merchant/billing/integrations/gestpay/notification.rb +83 -0
  41. data/lib/active_merchant/billing/integrations/helper.rb +79 -0
  42. data/lib/active_merchant/billing/integrations/nochex.rb +21 -0
  43. data/lib/active_merchant/billing/integrations/nochex/helper.rb +68 -0
  44. data/lib/active_merchant/billing/integrations/nochex/notification.rb +101 -0
  45. data/lib/active_merchant/billing/integrations/notification.rb +52 -0
  46. data/lib/active_merchant/billing/integrations/paypal.rb +35 -0
  47. data/lib/active_merchant/billing/integrations/paypal/helper.rb +103 -0
  48. data/lib/active_merchant/billing/integrations/paypal/notification.rb +187 -0
  49. data/lib/active_merchant/billing/response.rb +28 -0
  50. data/lib/active_merchant/lib/country.rb +297 -0
  51. data/lib/active_merchant/lib/posts_data.rb +21 -0
  52. data/lib/active_merchant/lib/requires_parameters.rb +17 -0
  53. data/lib/active_merchant/lib/validateable.rb +76 -0
  54. data/lib/tasks/cia.rb +90 -0
  55. metadata +129 -0
@@ -0,0 +1,265 @@
1
+ # This class implements the Psigate gateway for the ActiveMerchant module.
2
+ # Psigate = http://www.psigate.com/ The class is currently set up to use
3
+ # the psigate test server while rails is in testing or developement mode.
4
+ # The real server will be used while in production mode.
5
+ #
6
+ # Modifications by Sean O'Hara ( sohara at sohara dot com )
7
+ #
8
+ # Usage for a PreAuth (authorize) is as follows:
9
+ #
10
+ # twenty = Money.ca_dollar(2000)
11
+ # gateway = PsigateGateway.new({
12
+ # :store_id => 'teststore',
13
+ # :password => 'psigate1234',
14
+ # })
15
+ #
16
+ # creditcard = CreditCard.new({
17
+ # :number => '4242424242424242',
18
+ # :month => 8,
19
+ # :year => 2006,
20
+ # :first_name => 'Longbob',
21
+ # :last_name => 'Longsen'
22
+ # })
23
+ # response = @gateway.authorize(twenty, creditcard, {:order_id => 1234,
24
+ # :billing_address => {
25
+ # :address1 => '123 fairweather Lane',
26
+ # :address2 => 'Apt B',
27
+ # :city => 'New York',
28
+ # :state => 'NY',
29
+ # :country => 'U.S.A.',
30
+ # :zip => '10010'},
31
+ # :email => 'jack@yahoo.com'
32
+ # })
33
+
34
+ require 'rexml/document'
35
+
36
+ module ActiveMerchant #:nodoc:
37
+ module Billing #:nodoc:
38
+
39
+ class PsigateGateway < Gateway
40
+
41
+ # URL
42
+ attr_reader :url
43
+ attr_reader :response
44
+ attr_reader :options
45
+
46
+ TEST_URL = 'https://dev.psigate.com:7989/Messenger/XMLMessenger'
47
+ LIVE_URL = 'https://secure.psigate.com:7934/Messenger/XMLMessenger'
48
+
49
+ def initialize(options = {})
50
+ requires!(options, :login, :password)
51
+
52
+ options[:store_id] ||= options[:login]
53
+
54
+ # these are the defaults for the psigate test server
55
+ @options = {
56
+ :store_id => "teststore",
57
+ :password => "testpass",
58
+ }.update(options)
59
+ super
60
+ end
61
+
62
+ # Psigate PreAuth
63
+ def authorize(money, creditcard, options = {})
64
+ requires!(options, :order_id)
65
+ options.update({ :CardAction => "1" })
66
+ commit(money, creditcard, options)
67
+ end
68
+
69
+ # Psigate Sale
70
+ def purchase(money, creditcard, options = {})
71
+ requires!(options, :order_id)
72
+ options.update({ :CardAction => "0" })
73
+ commit(money, creditcard, options)
74
+ end
75
+
76
+ # Psigate PostAuth
77
+ def capture(money, authorization, options = {})
78
+ options.update({ :CardAction => "2", :order_id => authorization })
79
+ commit(money, nil, options)
80
+ end
81
+
82
+
83
+ # Psigate Credit
84
+ def credit(money, authorization, options = {})
85
+ options.update({ :CardAction => "3", :order_id => authorization })
86
+ commit(money, nil, options)
87
+ end
88
+
89
+ # We support visa and master card
90
+ def self.supported_cardtypes
91
+ [:visa, :master]
92
+ end
93
+
94
+ private
95
+
96
+ def commit(money, creditcard, options = {})
97
+ parameters = parameters(money, creditcard, options)
98
+
99
+ if result = test_result_from_cc_number(parameters[:CardNumber])
100
+ return result
101
+ end
102
+
103
+ data = ssl_post post_data(parameters)
104
+ @response = parse(data)
105
+ success = (@response[:approved] == "APPROVED")
106
+ message = message_form(@response)
107
+ Response.new(success, message, @response, :test => test?, :authorization => response[:orderid])
108
+ end
109
+
110
+ # Parse psigate response xml into a convinient hash
111
+ def parse(xml)
112
+ # <?xml version="1.0" encoding="UTF-8"?>
113
+ # <Result>
114
+ # <TransTime>Tue Jun 27 22:19:58 EDT 2006</TransTime>
115
+ # <OrderID>1004</OrderID>
116
+ # <TransactionType>POSTAUTH</TransactionType>
117
+ # <Approved>APPROVED</Approved>
118
+ # <ReturnCode>Y:123456:0abcdef:M:X:NNN</ReturnCode>
119
+ # <ErrMsg></ErrMsg>
120
+ # <TaxTotal>0.00</TaxTotal>
121
+ # <ShipTotal>0.00</ShipTotal>
122
+ # <SubTotal>20.00</SubTotal>
123
+ # <FullTotal>20.00</FullTotal>
124
+ # <PaymentType>CC</PaymentType>
125
+ # <CardNumber>......1111</CardNumber>
126
+ # <TransRefNumber>1bd6f76ad1a25804</TransRefNumber>
127
+ # <CardIDResult>M</CardIDResult>
128
+ # <AVSResult>X</AVSResult>
129
+ # <CardAuthNumber>123456</CardAuthNumber>
130
+ # <CardRefNumber>0abcdef</CardRefNumber>
131
+ # <CardType>VISA</CardType>
132
+ # <IPResult>NNN</IPResult>
133
+ # <IPCountry>UN</IPCountry>
134
+ # <IPRegion>UNKNOWN</IPRegion>
135
+ # <IPCity>UNKNOWN</IPCity>
136
+ # </Result>
137
+
138
+ response = {:message => "Global Error Receipt", :complete => false}
139
+
140
+ xml = REXML::Document.new(xml)
141
+ xml.elements.each('//Result/*') do |node|
142
+
143
+ response[node.name.downcase.to_sym] = normalize(node.text)
144
+
145
+ end unless xml.root.nil?
146
+
147
+ response
148
+ end
149
+
150
+ def post_data(parameters = {})
151
+ xml = REXML::Document.new
152
+ xml << REXML::XMLDecl.new
153
+ root = xml.add_element("Order")
154
+
155
+ for key, value in parameters
156
+ root.add_element(key.to_s).text = value if value
157
+ end
158
+
159
+ xml.to_s
160
+ end
161
+
162
+
163
+ # Set up the parameters hash just once so we don't have to do it
164
+ # for every action.
165
+ def parameters(money, creditcard, options = {})
166
+
167
+
168
+ params = {
169
+ # General order paramters
170
+ :StoreID => @options[:store_id],
171
+ :Passphrase => @options[:password],
172
+ :TestResult => options[:test_result],
173
+ :OrderID => options[:order_id],
174
+ :UserID => options[:user_id],
175
+ :Phone => options[:phone],
176
+ :Fax => options[:fax],
177
+ :Email => options[:email],
178
+
179
+ # Credit Card paramaters
180
+ :PaymentType => "CC",
181
+ :CardAction => options[:CardAction],
182
+
183
+ # Financial paramters
184
+ :CustomerIP => options[:ip],
185
+ :SubTotal => amount(money),
186
+ :Tax1 => options[:tax1],
187
+ :Tax2 => options[:tax2],
188
+ :ShippingTotal => options[:shipping_total],
189
+ }
190
+
191
+ if creditcard
192
+ exp_month = sprintf("%.2i", creditcard.month) unless creditcard.month.blank?
193
+ exp_year = creditcard.year.to_s[2,2] unless creditcard.year.blank?
194
+ card_id_code = creditcard.verification_value.blank? ? nil : "1"
195
+
196
+ params.update(
197
+ :CardNumber => creditcard.number,
198
+ :CardExpMonth => exp_month,
199
+ :CardExpYear => exp_year,
200
+ :CardIDCode => card_id_code,
201
+ :CardIDNumber => creditcard.verification_value
202
+ )
203
+ end
204
+
205
+ if address = options[:billing_address] || options[:address]
206
+ params[:Bname] = address[:name] || creditcard.name
207
+ params[:Baddress1] = address[:address1] unless address[:address1].blank?
208
+ params[:Baddress2] = address[:address2] unless address[:address2].blank?
209
+ params[:Bcity] = address[:city] unless address[:city].blank?
210
+ params[:Bprovince] = address[:state] unless address[:state].blank?
211
+ params[:Bpostalcode] = address[:zip] unless address[:zip].blank?
212
+ params[:Bcountry] = address[:country] unless address[:country].blank?
213
+ params[:Bcompany] = address[:company] unless address[:company].blank?
214
+ end
215
+
216
+ if address = options[:shipping_address] || options[:address]
217
+ params[:Sname] = address[:name] || creditcard.name
218
+ params[:Saddress1] = address[:address1] unless address[:address1].blank?
219
+ params[:Saddress2] = address[:address2] unless address[:address2].blank?
220
+ params[:Scity] = address[:city] unless address[:city].blank?
221
+ params[:Sprovince] = address[:state] unless address[:state].blank?
222
+ params[:Spostalcode] = address[:zip] unless address[:zip].blank?
223
+ params[:Scountry] = address[:country] unless address[:country].blank?
224
+ params[:Scompany] = address[:company] unless address[:company].blank?
225
+ end
226
+
227
+ return params
228
+ end
229
+
230
+ # Redefine ssl_post to use correct url depending on test mode
231
+ def ssl_post(data)
232
+ # In my case I only want the LIVE_URL to be used when in prodcution mode
233
+ # because Psigate charges for all transactions
234
+ uri = URI.parse(test? ? TEST_URL : LIVE_URL )
235
+ http = Net::HTTP.new(uri.host, uri.port)
236
+
237
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE unless @ssl_strict
238
+ http.use_ssl = true
239
+
240
+ http.post(uri.path, data).body
241
+ end
242
+
243
+
244
+ def message_form(response)
245
+ if response[:approved] == "APPROVED"
246
+ return 'Success'
247
+ else
248
+ return 'Unspecified error' if response[:errmsg].blank?
249
+ return response[:errmsg].gsub(/[^\w]/, ' ').split.join(" ").capitalize
250
+ end
251
+ end
252
+
253
+ # Make a ruby type out of the response string
254
+ def normalize(field)
255
+ case field
256
+ when "true" then true
257
+ when "false" then false
258
+ when "" then nil
259
+ when "null" then nil
260
+ else field
261
+ end
262
+ end
263
+ end
264
+ end
265
+ end
@@ -0,0 +1,330 @@
1
+ begin
2
+ require 'tclink'
3
+ rescue LoadError
4
+ # Ignore, but we will fail hard if someone actually tries to use this gateway
5
+ end
6
+
7
+ module ActiveMerchant #:nodoc:
8
+ module Billing #:nodoc:
9
+
10
+ # To get started using TrustCommerce with active_merchant, download the tclink library from http://www.trustcommerce.com/tclink.html,
11
+ # following the instructions available there to get it working on your system. Once it is installed, you should be able to make sure
12
+ # that it is visible to your ruby install by opening irb and typing "require 'tclink'", which should return "true".
13
+ #
14
+ # TO USE:
15
+ # First, make sure you have everything setup correctly and all of your dependencies in place with:
16
+ #
17
+ # require 'rubygems'
18
+ # require 'money'
19
+ # require 'active_merchant'
20
+ #
21
+ # The second line is a require for the 'money' library. Make sure you have it installed with 'gem install money'
22
+ #
23
+ # Using the money library, create a money object. Pass the dollar value in cents. In this case, $10 US becomes 1000.
24
+ #
25
+ # tendollar = Money.us_dollar(1000)
26
+ #
27
+ # Next, create a credit card object using a TC approved test card.
28
+ #
29
+ # creditcard = ActiveMerchant::Billing::CreditCard.new(
30
+ # :number => '4111111111111111',
31
+ # :month => 8,
32
+ # :year => 2006,
33
+ # :first_name => 'Longbob',
34
+ # :last_name => 'Longsen'
35
+ # )
36
+ #
37
+ # To finish setting up, create the active_merchant object you will be using, with the TrustCommerce gateway. If you have a
38
+ # functional TrustCommerce account, replace login and password with your account info. Otherwise the defaults will work for
39
+ # testing.
40
+ #
41
+ # gateway = ActiveMerchant::Billing::Base.gateway(:trust_commerce).new(:login => "TestMerchant", :password => "password")
42
+ #
43
+ # Now we are ready to process our transaction
44
+ #
45
+ # response = gateway.purchase(tendollar, creditcard)
46
+ #
47
+ # Sending a transaction to TrustCommerce with active_merchant returns a Response object, which consistently allows you to:
48
+ #
49
+ # 1) Check whether the transaction was successful
50
+ #
51
+ # response.success?
52
+ #
53
+ # 2) Retrieve any message returned by TrustCommerce, either a "transaction was successful" note or an explanation of why the
54
+ # transaction was rejected.
55
+ #
56
+ # response.message
57
+ #
58
+ # 3) Retrieve and store the unique transaction ID returned by Trust Commerece, for use in referencing the transaction in the future.
59
+ #
60
+ # response.params["transid"]
61
+ #
62
+ # This should be enough to get you started with Trust Commerce and active_merchant. For further information, review the methods
63
+ # below and the rest of active_merchant's documentation, as well as Trust Commerce's user and developer documentation.
64
+
65
+ class TrustCommerceGateway < Gateway
66
+ SUCCESS_TYPES = ["approved", "accepted"]
67
+
68
+ DECLINE_CODES = {
69
+ "decline" => "The credit card was declined",
70
+ "avs" => "AVS failed; the address entered does not match the billing address on file at the bank",
71
+ "cvv" => "CVV failed; the number provided is not the correct verification number for the card",
72
+ "call" => "The card must be authorized manually over the phone",
73
+ "expiredcard" => "Issuer was not certified for card verification",
74
+ "carderror" => "Card number is invalid",
75
+ "authexpired" => "Attempt to postauth an expired (more than 14 days old) preauth",
76
+ "fraud" => "CrediGuard fraud score was below requested threshold",
77
+ "blacklist" => "CrediGuard blacklist value was triggered",
78
+ "velocity" => "CrediGuard velocity control value was triggered",
79
+ "dailylimit" => "Daily limit in transaction count or amount as been reached",
80
+ "weeklylimit" => "Weekly limit in transaction count or amount as been reached",
81
+ "monthlylimit" => "Monthly limit in transaction count or amount as been reached"
82
+ }
83
+
84
+ BADDATA_CODES = {
85
+ "missingfields" => "One or more parameters required for this transaction type were not sent",
86
+ "extrafields" => "Parameters not allowed for this transaction type were sent",
87
+ "badformat" => "A field was improperly formatted, such as non-digit characters in a number field",
88
+ "badlength" => "A field was longer or shorter than the server allows",
89
+ "merchantcantaccept" => "The merchant can't accept data passed in this field",
90
+ "mismatch" => "Data in one of the offending fields did not cross-check with the other offending field"
91
+ }
92
+
93
+ ERROR_CODES = {
94
+ "cantconnect" => "Couldn't connect to the TrustCommerce gateway",
95
+ "dnsfailure" => "The TCLink software was unable to resolve DNS hostnames",
96
+ "linkfailure" => "The connection was established, but was severed before the transaction could complete",
97
+ "failtoprocess" => "The bank servers are offline and unable to authorize transactions"
98
+ }
99
+
100
+ # URL
101
+ attr_reader :url
102
+ attr_reader :response
103
+ attr_reader :options
104
+
105
+ self.money_format = :cents
106
+
107
+ def initialize(options = {})
108
+ requires!(options, :login, :password)
109
+ # these are the defaults for trustcommerce
110
+ @options = {
111
+ :login => "TestMerchant",
112
+ :password => "password"
113
+ }.update(options)
114
+
115
+ super
116
+ end
117
+
118
+ # authorize() is the first half of the preauth(authorize)/postauth(capture) model. The TC API docs call this
119
+ # preauth, we preserve active_merchant's nomenclature of authorize() for consistency with the rest of the library. This
120
+ # method simply checks to make sure funds are available for a transaction, and returns a transid that can be used later to
121
+ # postauthorize (capture) the funds.
122
+
123
+ def authorize(money, creditcard, options = {})
124
+ parameters = {
125
+ :amount => amount(money),
126
+ }
127
+
128
+ add_creditcard(parameters, creditcard)
129
+ add_address(parameters, options)
130
+ commit('preauth', parameters)
131
+ end
132
+
133
+ # purchase() is a simple sale. This is one of the most common types of transactions, and is extremely simple. All that you need
134
+ # to process a purchase are an amount in cents or a money object and a creditcard object.
135
+
136
+ def purchase(money, creditcard, options = {})
137
+ parameters = {
138
+ :amount => amount(money),
139
+ }
140
+
141
+ add_creditcard(parameters, creditcard)
142
+ add_address(parameters, options)
143
+ commit('sale', parameters)
144
+ end
145
+
146
+ # capture() is the second half of the preauth(authorize)/postauth(capture) model. The TC API docs call this
147
+ # postauth, we preserve active_merchant's nomenclature of capture() for consistency with the rest of the library. To process
148
+ # a postauthorization with TC, you need an amount in cents or a money object, and a TC transid.
149
+
150
+ def capture(money, authorization, options = {})
151
+ parameters = {
152
+ :amount => amount(money),
153
+ :transid => authorization,
154
+ }
155
+
156
+ commit('postauth', parameters)
157
+ end
158
+
159
+ # credit() allows you to return money to a card that was previously billed. You need to supply the amount, in cents or a money object,
160
+ # that you want to refund, and a TC transid for the transaction that you are refunding.
161
+
162
+ def credit(money, identification, options = {})
163
+ parameters = {
164
+ :amount => amount(money),
165
+ :transid => identification,
166
+ }
167
+
168
+ commit('credit', parameters)
169
+ end
170
+
171
+ # recurring() a TrustCommerce account that is activated for Citatdel, TrustCommerce's
172
+ # hosted customer billing info database.
173
+ #
174
+ # Recurring billing uses the same TC action as a plain-vanilla 'store', but we have a separate method for clarity. It can be called
175
+ # like store, with the addition of a required 'periodicity' parameter:
176
+ #
177
+ # The parameter :periodicity should be specified as either :bimonthly, :monthly, :biweekly, :weekly, :yearly or :daily
178
+ #
179
+ # gateway.recurring(tendollar, creditcard, :periodicity => :weekly)
180
+ #
181
+ # You can optionally specify how long you want payments to continue using 'payments'
182
+
183
+ def recurring(money, creditcard, options = {})
184
+ requires!(options, [:periodicity, :bimonthly, :monthly, :biweekly, :weekly, :yearly, :daily] )
185
+
186
+ cycle = case options[:periodicity]
187
+ when :monthly
188
+ '1m'
189
+ when :bimonthly
190
+ '2m'
191
+ when :weekly
192
+ '1w'
193
+ when :biweekly
194
+ '2w'
195
+ when :yearly
196
+ '1y'
197
+ when :daily
198
+ '1d'
199
+ end
200
+
201
+ parameters = {
202
+ :amount => amount(money),
203
+ :cycle => cycle,
204
+ :verify => options[:verify] || 'y',
205
+ :billingid => options[:billingid] || nil,
206
+ :payments => options[:payments] || nil,
207
+ }
208
+
209
+ add_creditcard(parameters, creditcard)
210
+
211
+ commit('store', parameters)
212
+ end
213
+
214
+ # store() requires a TrustCommerce account that is activated for Citatdel. You can call it with a credit card and a billing ID
215
+ # you would like to use to reference the stored credit card info for future captures. Use 'verify' to specify whether you want
216
+ # to simply store the card in the DB, or you want TC to verify the data first.
217
+
218
+ def store(creditcard, options = {})
219
+ parameters = {
220
+ :verify => options[:verify] || 'y',
221
+ :billingid => options[:billingid] || nil,
222
+ }
223
+
224
+ add_creditcard(parameters, creditcard)
225
+ add_address(parameters, options)
226
+ commit('store', parameters)
227
+ end
228
+
229
+ # To unstore a creditcard stored in Citadel using store() or recurring(), all that is required is the billing id. When you run
230
+ # unstore() the information will be removed and a Response object will be returned indicating the success of the action.
231
+ def unstore(identification, options = {})
232
+ parameters = {
233
+ :billingid => identification,
234
+ }
235
+
236
+ commit('unstore', parameters)
237
+ end
238
+
239
+ def self.supported_cardtypes
240
+ [:visa, :master, :discover, :american_express, :diners_club, :jcb]
241
+ end
242
+
243
+ private
244
+ def expdate(creditcard)
245
+ year = sprintf("%.4i", creditcard.year)
246
+ month = sprintf("%.2i", creditcard.month)
247
+
248
+ "#{month}#{year[-2..-1]}"
249
+ end
250
+
251
+ def add_creditcard(params, creditcard)
252
+ params[:media] = "cc"
253
+ params[:name] = creditcard.name
254
+ params[:cc] = creditcard.number
255
+ params[:exp] = expdate(creditcard)
256
+ params[:cvv] = creditcard.verification_value if creditcard.verification_value?
257
+ end
258
+
259
+ def add_address(params, options)
260
+ address = options[:billing_address] || options[:address]
261
+
262
+ if address
263
+ params[:address1] = address[:address1] unless address[:address1].blank?
264
+ params[:address2] = address[:address2] unless address[:address2].blank?
265
+ params[:city] = address[:city] unless address[:city].blank?
266
+ params[:state] = address[:state] unless address[:state].blank?
267
+ params[:zip] = address[:zip] unless address[:zip].blank?
268
+ params[:country] = address[:country] unless address[:country].blank?
269
+ params[:avs] = 'n'
270
+ end
271
+ end
272
+
273
+ def clean_and_stringify_params(parameters)
274
+ # TCLink wants us to send a hash with string keys, and activemerchant pushes everything around with
275
+ # symbol keys. Before sending our input to TCLink, we convert all our keys to strings and dump the symbol keys.
276
+ # We also remove any pairs with nil values, as these confuse TCLink.
277
+ parameters.keys.reverse.each do |key|
278
+ if parameters[key]
279
+ parameters[key.to_s] = parameters[key]
280
+ end
281
+ parameters.delete(key)
282
+ end
283
+ end
284
+
285
+ def commit(action, parameters)
286
+ test = test? || parameters[:test_request]
287
+ parameters[:custid] = @options[:login]
288
+ parameters[:password] = @options[:password]
289
+ parameters[:demo] = test ? 'y' : 'n'
290
+ parameters[:action] = action
291
+
292
+ if result = test_result_from_cc_number(parameters[:cc])
293
+ return result
294
+ end
295
+
296
+ begin
297
+ clean_and_stringify_params(parameters)
298
+
299
+ data = TCLink.send(parameters)
300
+ # to be considered successful, transaction status must be either "approved" or "accepted"
301
+ success = SUCCESS_TYPES.include?(data["status"])
302
+ message = message_from(data)
303
+
304
+ Response.new(success, message, data, :test => test, :authorization => data["transid"] )
305
+ rescue NameError => e
306
+ if e.message =~ /constant TCLink/
307
+ raise 'Trust Commerce requires "tclink" library from http://www.trustcommerce.com/tclink.html'
308
+ else
309
+ raise
310
+ end
311
+ end
312
+
313
+ end
314
+
315
+ def message_from(data)
316
+ status = case data["status"]
317
+ when "decline"
318
+ return DECLINE_CODES[data["declinetype"]]
319
+ when "baddata"
320
+ return BADDATA_CODES[data["error"]]
321
+ when "error"
322
+ return ERROR_CODES[data["errortype"]]
323
+ else
324
+ return "The transaction was successful"
325
+ end
326
+ end
327
+
328
+ end
329
+ end
330
+ end