n8_activemerchant 1.9.3

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 (158) hide show
  1. data/CHANGELOG +574 -0
  2. data/CONTRIBUTORS +175 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +151 -0
  5. data/gem-public_cert.pem +20 -0
  6. data/lib/active_merchant.rb +49 -0
  7. data/lib/active_merchant/billing.rb +9 -0
  8. data/lib/active_merchant/billing/avs_result.rb +98 -0
  9. data/lib/active_merchant/billing/base.rb +57 -0
  10. data/lib/active_merchant/billing/check.rb +68 -0
  11. data/lib/active_merchant/billing/credit_card.rb +161 -0
  12. data/lib/active_merchant/billing/credit_card_formatting.rb +21 -0
  13. data/lib/active_merchant/billing/credit_card_methods.rb +125 -0
  14. data/lib/active_merchant/billing/cvv_result.rb +38 -0
  15. data/lib/active_merchant/billing/expiry_date.rb +34 -0
  16. data/lib/active_merchant/billing/gateway.rb +169 -0
  17. data/lib/active_merchant/billing/gateways.rb +18 -0
  18. data/lib/active_merchant/billing/gateways/authorize_net.rb +654 -0
  19. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +736 -0
  20. data/lib/active_merchant/billing/gateways/beanstream.rb +102 -0
  21. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +244 -0
  22. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
  23. data/lib/active_merchant/billing/gateways/bogus.rb +102 -0
  24. data/lib/active_merchant/billing/gateways/braintree.rb +17 -0
  25. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
  26. data/lib/active_merchant/billing/gateways/braintree_blue.rb +210 -0
  27. data/lib/active_merchant/billing/gateways/braintree_orange.rb +17 -0
  28. data/lib/active_merchant/billing/gateways/card_stream.rb +230 -0
  29. data/lib/active_merchant/billing/gateways/cyber_source.rb +406 -0
  30. data/lib/active_merchant/billing/gateways/data_cash.rb +593 -0
  31. data/lib/active_merchant/billing/gateways/efsnet.rb +229 -0
  32. data/lib/active_merchant/billing/gateways/elavon.rb +134 -0
  33. data/lib/active_merchant/billing/gateways/epay.rb +263 -0
  34. data/lib/active_merchant/billing/gateways/eway.rb +277 -0
  35. data/lib/active_merchant/billing/gateways/exact.rb +222 -0
  36. data/lib/active_merchant/billing/gateways/first_pay.rb +172 -0
  37. data/lib/active_merchant/billing/gateways/garanti.rb +222 -0
  38. data/lib/active_merchant/billing/gateways/inspire.rb +221 -0
  39. data/lib/active_merchant/billing/gateways/instapay.rb +164 -0
  40. data/lib/active_merchant/billing/gateways/iridium.rb +253 -0
  41. data/lib/active_merchant/billing/gateways/jetpay.rb +270 -0
  42. data/lib/active_merchant/billing/gateways/linkpoint.rb +449 -0
  43. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +154 -0
  44. data/lib/active_merchant/billing/gateways/merchant_ware.rb +283 -0
  45. data/lib/active_merchant/billing/gateways/modern_payments.rb +36 -0
  46. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +220 -0
  47. data/lib/active_merchant/billing/gateways/moneris.rb +205 -0
  48. data/lib/active_merchant/billing/gateways/net_registry.rb +189 -0
  49. data/lib/active_merchant/billing/gateways/netaxept.rb +234 -0
  50. data/lib/active_merchant/billing/gateways/netbilling.rb +168 -0
  51. data/lib/active_merchant/billing/gateways/ogone.rb +279 -0
  52. data/lib/active_merchant/billing/gateways/pay_junction.rb +392 -0
  53. data/lib/active_merchant/billing/gateways/pay_secure.rb +120 -0
  54. data/lib/active_merchant/billing/gateways/paybox_direct.rb +203 -0
  55. data/lib/active_merchant/billing/gateways/payflow.rb +236 -0
  56. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +207 -0
  57. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
  58. data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
  59. data/lib/active_merchant/billing/gateways/payflow_express.rb +138 -0
  60. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
  61. data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
  62. data/lib/active_merchant/billing/gateways/payment_express.rb +230 -0
  63. data/lib/active_merchant/billing/gateways/paypal.rb +121 -0
  64. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +326 -0
  65. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +38 -0
  66. data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
  67. data/lib/active_merchant/billing/gateways/paypal_express.rb +145 -0
  68. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +20 -0
  69. data/lib/active_merchant/billing/gateways/paysimple/paysimple.rb +333 -0
  70. data/lib/active_merchant/billing/gateways/paysimple/usaepay.wsdl +1596 -0
  71. data/lib/active_merchant/billing/gateways/plugnpay.rb +292 -0
  72. data/lib/active_merchant/billing/gateways/psigate.rb +214 -0
  73. data/lib/active_merchant/billing/gateways/psl_card.rb +304 -0
  74. data/lib/active_merchant/billing/gateways/quickpay.rb +213 -0
  75. data/lib/active_merchant/billing/gateways/realex.rb +200 -0
  76. data/lib/active_merchant/billing/gateways/sage.rb +146 -0
  77. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +88 -0
  78. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +116 -0
  79. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
  80. data/lib/active_merchant/billing/gateways/sage_pay.rb +315 -0
  81. data/lib/active_merchant/billing/gateways/sallie_mae.rb +144 -0
  82. data/lib/active_merchant/billing/gateways/secure_net.rb +330 -0
  83. data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
  84. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +157 -0
  85. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +113 -0
  86. data/lib/active_merchant/billing/gateways/skip_jack.rb +453 -0
  87. data/lib/active_merchant/billing/gateways/smart_ps.rb +265 -0
  88. data/lib/active_merchant/billing/gateways/trans_first.rb +127 -0
  89. data/lib/active_merchant/billing/gateways/transax.rb +25 -0
  90. data/lib/active_merchant/billing/gateways/trust_commerce.rb +418 -0
  91. data/lib/active_merchant/billing/gateways/usa_epay.rb +194 -0
  92. data/lib/active_merchant/billing/gateways/usa_epay_soap.rb +154 -0
  93. data/lib/active_merchant/billing/gateways/verifi.rb +228 -0
  94. data/lib/active_merchant/billing/gateways/viaklix.rb +189 -0
  95. data/lib/active_merchant/billing/gateways/wirecard.rb +318 -0
  96. data/lib/active_merchant/billing/integrations.rb +17 -0
  97. data/lib/active_merchant/billing/integrations/action_view_helper.rb +68 -0
  98. data/lib/active_merchant/billing/integrations/bogus.rb +23 -0
  99. data/lib/active_merchant/billing/integrations/bogus/helper.rb +17 -0
  100. data/lib/active_merchant/billing/integrations/bogus/notification.rb +11 -0
  101. data/lib/active_merchant/billing/integrations/bogus/return.rb +10 -0
  102. data/lib/active_merchant/billing/integrations/chronopay.rb +23 -0
  103. data/lib/active_merchant/billing/integrations/chronopay/helper.rb +120 -0
  104. data/lib/active_merchant/billing/integrations/chronopay/notification.rb +158 -0
  105. data/lib/active_merchant/billing/integrations/chronopay/return.rb +10 -0
  106. data/lib/active_merchant/billing/integrations/direc_pay.rb +41 -0
  107. data/lib/active_merchant/billing/integrations/direc_pay/helper.rb +188 -0
  108. data/lib/active_merchant/billing/integrations/direc_pay/notification.rb +76 -0
  109. data/lib/active_merchant/billing/integrations/direc_pay/return.rb +32 -0
  110. data/lib/active_merchant/billing/integrations/direc_pay/status.rb +37 -0
  111. data/lib/active_merchant/billing/integrations/gestpay.rb +25 -0
  112. data/lib/active_merchant/billing/integrations/gestpay/common.rb +42 -0
  113. data/lib/active_merchant/billing/integrations/gestpay/helper.rb +70 -0
  114. data/lib/active_merchant/billing/integrations/gestpay/notification.rb +85 -0
  115. data/lib/active_merchant/billing/integrations/gestpay/return.rb +10 -0
  116. data/lib/active_merchant/billing/integrations/helper.rb +96 -0
  117. data/lib/active_merchant/billing/integrations/hi_trust.rb +27 -0
  118. data/lib/active_merchant/billing/integrations/hi_trust/helper.rb +58 -0
  119. data/lib/active_merchant/billing/integrations/hi_trust/notification.rb +59 -0
  120. data/lib/active_merchant/billing/integrations/hi_trust/return.rb +67 -0
  121. data/lib/active_merchant/billing/integrations/nochex.rb +88 -0
  122. data/lib/active_merchant/billing/integrations/nochex/helper.rb +68 -0
  123. data/lib/active_merchant/billing/integrations/nochex/notification.rb +94 -0
  124. data/lib/active_merchant/billing/integrations/nochex/return.rb +10 -0
  125. data/lib/active_merchant/billing/integrations/notification.rb +62 -0
  126. data/lib/active_merchant/billing/integrations/paypal.rb +39 -0
  127. data/lib/active_merchant/billing/integrations/paypal/helper.rb +119 -0
  128. data/lib/active_merchant/billing/integrations/paypal/notification.rb +154 -0
  129. data/lib/active_merchant/billing/integrations/paypal/return.rb +10 -0
  130. data/lib/active_merchant/billing/integrations/quickpay.rb +17 -0
  131. data/lib/active_merchant/billing/integrations/quickpay/helper.rb +72 -0
  132. data/lib/active_merchant/billing/integrations/quickpay/notification.rb +74 -0
  133. data/lib/active_merchant/billing/integrations/return.rb +37 -0
  134. data/lib/active_merchant/billing/integrations/sage_pay_form.rb +37 -0
  135. data/lib/active_merchant/billing/integrations/sage_pay_form/encryption.rb +33 -0
  136. data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +109 -0
  137. data/lib/active_merchant/billing/integrations/sage_pay_form/notification.rb +204 -0
  138. data/lib/active_merchant/billing/integrations/sage_pay_form/return.rb +27 -0
  139. data/lib/active_merchant/billing/integrations/two_checkout.rb +23 -0
  140. data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +59 -0
  141. data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +114 -0
  142. data/lib/active_merchant/billing/integrations/two_checkout/return.rb +17 -0
  143. data/lib/active_merchant/billing/response.rb +32 -0
  144. data/lib/active_merchant/common.rb +14 -0
  145. data/lib/active_merchant/common/connection.rb +162 -0
  146. data/lib/active_merchant/common/country.rb +328 -0
  147. data/lib/active_merchant/common/error.rb +26 -0
  148. data/lib/active_merchant/common/post_data.rb +24 -0
  149. data/lib/active_merchant/common/posts_data.rb +61 -0
  150. data/lib/active_merchant/common/requires_parameters.rb +16 -0
  151. data/lib/active_merchant/common/utils.rb +18 -0
  152. data/lib/active_merchant/common/validateable.rb +76 -0
  153. data/lib/active_merchant/version.rb +3 -0
  154. data/lib/activemerchant.rb +1 -0
  155. data/lib/certs/cacert.pem +7815 -0
  156. data/lib/support/gateway_support.rb +58 -0
  157. data/lib/support/outbound_hosts.rb +25 -0
  158. metadata +270 -0
@@ -0,0 +1,97 @@
1
+ require File.dirname(__FILE__) + '/sage_core'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class SageVirtualCheckGateway < Gateway #:nodoc:
6
+ include SageCore
7
+ self.url = 'https://www.sagepayments.net/cgi-bin/eftVirtualCheck.dll?transaction'
8
+ self.source = 'virtual_check'
9
+
10
+ def purchase(money, credit_card, options = {})
11
+ post = {}
12
+ add_check(post, credit_card)
13
+ add_check_customer_data(post, options)
14
+ add_transaction_data(post, money, options)
15
+ commit(:purchase, post)
16
+ end
17
+
18
+ def void(reference, options = {})
19
+ post = {}
20
+ add_reference(post, reference)
21
+ commit(:void, post)
22
+ end
23
+
24
+ def credit(money, credit_card, options = {})
25
+ post = {}
26
+ add_check(post, credit_card)
27
+ add_check_customer_data(post, options)
28
+ add_transaction_data(post, money, options)
29
+ commit(:credit, post)
30
+ end
31
+
32
+ private
33
+ def add_check(post, check)
34
+ post[:C_first_name] = check.first_name
35
+ post[:C_last_name] = check.last_name
36
+ post[:C_rte] = check.routing_number
37
+ post[:C_acct] = check.account_number
38
+ post[:C_check_number] = check.number
39
+ post[:C_acct_type] = account_type(check)
40
+ end
41
+
42
+ def add_check_customer_data(post, options)
43
+ # Required  Customer Type – (NACHA Transaction Class)
44
+ # CCD for Commercial, Merchant Initiated
45
+ # PPD for Personal, Merchant Initiated
46
+ # WEB for Internet, Consumer Initiated
47
+ # RCK for Returned Checks
48
+ # ARC for Account Receivable Entry
49
+ # TEL for TelephoneInitiated
50
+ post[:C_customer_type] = "WEB"
51
+
52
+ # Optional  10  Digit Originator  ID – Assigned  By for  each transaction  class  or  business  purpose. If  not provided, the default Originator ID for the specific  Customer Type will be applied. 
53
+ post[:C_originator_id] = options[:originator_id]
54
+
55
+ # Optional  Transaction Addenda
56
+ post[:T_addenda] = options[:addenda]
57
+
58
+ # Required  Check  Writer  Social  Security  Number  (  Numbers Only, No Dashes ) 
59
+ post[:C_ssn] = options[:ssn].to_s.gsub(/[^\d]/, '')
60
+
61
+ post[:C_dl_state_code] = options[:drivers_license_state]
62
+ post[:C_dl_number] = options[:drivers_license_number]
63
+ post[:C_dob] = format_birth_date(options[:date_of_birth])
64
+ end
65
+
66
+ def format_birth_date(date)
67
+ date.respond_to?(:strftime) ? date.strftime("%m/%d/%Y") : date
68
+ end
69
+
70
+ # DDA for Checking
71
+ # SAV for Savings 
72
+ def account_type(check)
73
+ case check.account_type
74
+ when 'checking' then 'DDA'
75
+ when 'savings' then 'SAV'
76
+ else raise ArgumentError, "Unknown account type #{check.account_type}"
77
+ end
78
+ end
79
+
80
+ def parse(data)
81
+ response = {}
82
+ response[:success] = data[1,1]
83
+ response[:code] = data[2,6].strip
84
+ response[:message] = data[8,32].strip
85
+ response[:risk] = data[40, 2]
86
+ response[:reference] = data[42, 10]
87
+
88
+ extra_data = data[53...-1].split("\034")
89
+ response[:order_number] = extra_data[0]
90
+ response[:authentication_indicator] = extra_data[1]
91
+ response[:authentication_disclosure] = extra_data[2]
92
+ response
93
+ end
94
+ end
95
+ end
96
+ end
97
+
@@ -0,0 +1,315 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class SagePayGateway < Gateway
4
+ cattr_accessor :simulate
5
+ self.simulate = false
6
+
7
+ TEST_URL = 'https://test.sagepay.com/gateway/service'
8
+ LIVE_URL = 'https://live.sagepay.com/gateway/service'
9
+ SIMULATOR_URL = 'https://test.sagepay.com/Simulator'
10
+
11
+ APPROVED = 'OK'
12
+
13
+ TRANSACTIONS = {
14
+ :purchase => 'PAYMENT',
15
+ :credit => 'REFUND',
16
+ :authorization => 'DEFERRED',
17
+ :capture => 'RELEASE',
18
+ :void => 'VOID',
19
+ :abort => 'ABORT'
20
+ }
21
+
22
+ CREDIT_CARDS = {
23
+ :visa => "VISA",
24
+ :master => "MC",
25
+ :delta => "DELTA",
26
+ :solo => "SOLO",
27
+ :switch => "MAESTRO",
28
+ :maestro => "MAESTRO",
29
+ :american_express => "AMEX",
30
+ :electron => "UKE",
31
+ :diners_club => "DC",
32
+ :jcb => "JCB"
33
+ }
34
+
35
+ ELECTRON = /^(424519|42496[23]|450875|48440[6-8]|4844[1-5][1-5]|4917[3-5][0-9]|491880)\d{10}(\d{3})?$/
36
+
37
+ AVS_CVV_CODE = {
38
+ "NOTPROVIDED" => nil,
39
+ "NOTCHECKED" => 'X',
40
+ "MATCHED" => 'Y',
41
+ "NOTMATCHED" => 'N'
42
+ }
43
+
44
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :switch, :solo, :maestro, :diners_club]
45
+ self.supported_countries = ['GB']
46
+ self.default_currency = 'GBP'
47
+
48
+ self.homepage_url = 'http://www.sagepay.com'
49
+ self.display_name = 'SagePay'
50
+
51
+ def initialize(options = {})
52
+ requires!(options, :login)
53
+ @options = options
54
+ super
55
+ end
56
+
57
+ def test?
58
+ @options[:test] || super
59
+ end
60
+
61
+ def purchase(money, credit_card, options = {})
62
+ requires!(options, :order_id)
63
+
64
+ post = {}
65
+
66
+ add_amount(post, money, options)
67
+ add_invoice(post, options)
68
+ add_credit_card(post, credit_card)
69
+ add_address(post, options)
70
+ add_customer_data(post, options)
71
+
72
+ commit(:purchase, post)
73
+ end
74
+
75
+ def authorize(money, credit_card, options = {})
76
+ requires!(options, :order_id)
77
+
78
+ post = {}
79
+
80
+ add_amount(post, money, options)
81
+ add_invoice(post, options)
82
+ add_credit_card(post, credit_card)
83
+ add_address(post, options)
84
+ add_customer_data(post, options)
85
+
86
+ commit(:authorization, post)
87
+ end
88
+
89
+ # You can only capture a transaction once, even if you didn't capture the full amount the first time.
90
+ def capture(money, identification, options = {})
91
+ post = {}
92
+
93
+ add_reference(post, identification)
94
+ add_release_amount(post, money, options)
95
+
96
+ commit(:capture, post)
97
+ end
98
+
99
+ def void(identification, options = {})
100
+ post = {}
101
+
102
+ add_reference(post, identification)
103
+ action = abort_or_void_from(identification)
104
+
105
+ commit(action, post)
106
+ end
107
+
108
+ # Crediting requires a new order_id to passed in, as well as a description
109
+ def credit(money, identification, options = {})
110
+ requires!(options, :order_id, :description)
111
+
112
+ post = {}
113
+
114
+ add_credit_reference(post, identification)
115
+ add_amount(post, money, options)
116
+ add_invoice(post, options)
117
+
118
+ commit(:credit, post)
119
+ end
120
+
121
+ private
122
+ def add_reference(post, identification)
123
+ order_id, transaction_id, authorization, security_key = identification.split(';')
124
+
125
+ add_pair(post, :VendorTxCode, order_id)
126
+ add_pair(post, :VPSTxId, transaction_id)
127
+ add_pair(post, :TxAuthNo, authorization)
128
+ add_pair(post, :SecurityKey, security_key)
129
+ end
130
+
131
+ def add_credit_reference(post, identification)
132
+ order_id, transaction_id, authorization, security_key = identification.split(';')
133
+
134
+ add_pair(post, :RelatedVendorTxCode, order_id)
135
+ add_pair(post, :RelatedVPSTxId, transaction_id)
136
+ add_pair(post, :RelatedTxAuthNo, authorization)
137
+ add_pair(post, :RelatedSecurityKey, security_key)
138
+ end
139
+
140
+ def add_amount(post, money, options)
141
+ currency = options[:currency] || currency(money)
142
+ add_pair(post, :Amount, localized_amount(money, currency), :required => true)
143
+ add_pair(post, :Currency, currency, :required => true)
144
+ end
145
+
146
+ # doesn't actually use the currency -- dodgy!
147
+ def add_release_amount(post, money, options)
148
+ add_pair(post, :ReleaseAmount, amount(money), :required => true)
149
+ end
150
+
151
+ def add_customer_data(post, options)
152
+ add_pair(post, :CustomerEMail, options[:email][0,255]) unless options[:email].blank?
153
+ add_pair(post, :BillingPhone, options[:phone].gsub(/[^0-9+]/, '')[0,20]) unless options[:phone].blank?
154
+ add_pair(post, :ClientIPAddress, options[:ip])
155
+ end
156
+
157
+ def add_address(post, options)
158
+ if billing_address = options[:billing_address] || options[:address]
159
+ first_name, last_name = parse_first_and_last_name(billing_address[:name])
160
+ add_pair(post, :BillingSurname, last_name)
161
+ add_pair(post, :BillingFirstnames, first_name)
162
+ add_pair(post, :BillingAddress1, billing_address[:address1])
163
+ add_pair(post, :BillingAddress2, billing_address[:address2])
164
+ add_pair(post, :BillingCity, billing_address[:city])
165
+ add_pair(post, :BillingState, billing_address[:state]) if billing_address[:country] == 'US'
166
+ add_pair(post, :BillingCountry, billing_address[:country])
167
+ add_pair(post, :BillingPostCode, billing_address[:zip])
168
+ end
169
+
170
+ if shipping_address = options[:shipping_address] || billing_address
171
+ first_name, last_name = parse_first_and_last_name(shipping_address[:name])
172
+ add_pair(post, :DeliverySurname, last_name)
173
+ add_pair(post, :DeliveryFirstnames, first_name)
174
+ add_pair(post, :DeliveryAddress1, shipping_address[:address1])
175
+ add_pair(post, :DeliveryAddress2, shipping_address[:address2])
176
+ add_pair(post, :DeliveryCity, shipping_address[:city])
177
+ add_pair(post, :DeliveryState, shipping_address[:state]) if shipping_address[:country] == 'US'
178
+ add_pair(post, :DeliveryCountry, shipping_address[:country])
179
+ add_pair(post, :DeliveryPostCode, shipping_address[:zip])
180
+ end
181
+ end
182
+
183
+ def add_invoice(post, options)
184
+ add_pair(post, :VendorTxCode, sanitize_order_id(options[:order_id]), :required => true)
185
+ add_pair(post, :Description, options[:description] || options[:order_id])
186
+ end
187
+
188
+ def add_credit_card(post, credit_card)
189
+ add_pair(post, :CardHolder, credit_card.name, :required => true)
190
+ add_pair(post, :CardNumber, credit_card.number, :required => true)
191
+
192
+ add_pair(post, :ExpiryDate, format_date(credit_card.month, credit_card.year), :required => true)
193
+
194
+ if requires_start_date_or_issue_number?(credit_card)
195
+ add_pair(post, :StartDate, format_date(credit_card.start_month, credit_card.start_year))
196
+ add_pair(post, :IssueNumber, credit_card.issue_number)
197
+ end
198
+ add_pair(post, :CardType, map_card_type(credit_card))
199
+
200
+ add_pair(post, :CV2, credit_card.verification_value)
201
+ end
202
+
203
+ def sanitize_order_id(order_id)
204
+ order_id.to_s.gsub(/[^-a-zA-Z0-9._]/, '')
205
+ end
206
+
207
+ def map_card_type(credit_card)
208
+ raise ArgumentError, "The credit card type must be provided" if card_brand(credit_card).blank?
209
+
210
+ card_type = card_brand(credit_card).to_sym
211
+
212
+ # Check if it is an electron card
213
+ if card_type == :visa && credit_card.number =~ ELECTRON
214
+ CREDIT_CARDS[:electron]
215
+ else
216
+ CREDIT_CARDS[card_type]
217
+ end
218
+ end
219
+
220
+ # MMYY format
221
+ def format_date(month, year)
222
+ return nil if year.blank? || month.blank?
223
+
224
+ year = sprintf("%.4i", year)
225
+ month = sprintf("%.2i", month)
226
+
227
+ "#{month}#{year[-2..-1]}"
228
+ end
229
+
230
+ def commit(action, parameters)
231
+ response = parse( ssl_post(url_for(action), post_data(action, parameters)) )
232
+
233
+ Response.new(response["Status"] == APPROVED, message_from(response), response,
234
+ :test => test?,
235
+ :authorization => authorization_from(response, parameters, action),
236
+ :avs_result => {
237
+ :street_match => AVS_CVV_CODE[ response["AddressResult"] ],
238
+ :postal_match => AVS_CVV_CODE[ response["PostCodeResult"] ],
239
+ },
240
+ :cvv_result => AVS_CVV_CODE[ response["CV2Result"] ]
241
+ )
242
+ end
243
+
244
+ def authorization_from(response, params, action)
245
+ [ params[:VendorTxCode],
246
+ response["VPSTxId"],
247
+ response["TxAuthNo"],
248
+ response["SecurityKey"],
249
+ action ].join(";")
250
+ end
251
+
252
+ def abort_or_void_from(identification)
253
+ original_transaction = identification.split(';').last
254
+ original_transaction == 'authorization' ? :abort : :void
255
+ end
256
+
257
+ def url_for(action)
258
+ simulate ? build_simulator_url(action) : build_url(action)
259
+ end
260
+
261
+ def build_url(action)
262
+ endpoint = [ :purchase, :authorization ].include?(action) ? "vspdirect-register" : TRANSACTIONS[action].downcase
263
+ "#{test? ? TEST_URL : LIVE_URL}/#{endpoint}.vsp"
264
+ end
265
+
266
+ def build_simulator_url(action)
267
+ endpoint = [ :purchase, :authorization ].include?(action) ? "VSPDirectGateway.asp" : "VSPServerGateway.asp?Service=Vendor#{TRANSACTIONS[action].capitalize}Tx"
268
+ "#{SIMULATOR_URL}/#{endpoint}"
269
+ end
270
+
271
+ def message_from(response)
272
+ response['Status'] == APPROVED ? 'Success' : (response['StatusDetail'] || 'Unspecified error') # simonr 20080207 can't actually get non-nil blanks, so this is shorter
273
+ end
274
+
275
+ def post_data(action, parameters = {})
276
+ parameters.update(
277
+ :Vendor => @options[:login],
278
+ :TxType => TRANSACTIONS[action],
279
+ :VPSProtocol => "2.23"
280
+ )
281
+
282
+ parameters.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
283
+ end
284
+
285
+ # SagePay returns data in the following format
286
+ # Key1=value1
287
+ # Key2=value2
288
+ def parse(body)
289
+ result = {}
290
+ body.to_s.each_line do |pair|
291
+ result[$1] = $2 if pair.strip =~ /\A([^=]+)=(.+)\Z/im
292
+ end
293
+ result
294
+ end
295
+
296
+ def add_pair(post, key, value, options = {})
297
+ post[key] = value if !value.blank? || options[:required]
298
+ end
299
+
300
+ def parse_first_and_last_name(value)
301
+ name = value.to_s.split(' ')
302
+
303
+ last_name = name.pop || ''
304
+ first_name = name.join(' ')
305
+ [ first_name[0,20], last_name[0,20] ]
306
+ end
307
+
308
+ def localized_amount(money, currency)
309
+ amount = amount(money)
310
+ CURRENCIES_WITHOUT_FRACTIONS.include?(currency.to_s) ? amount.split('.').first : amount
311
+ end
312
+ end
313
+ end
314
+ end
315
+
@@ -0,0 +1,144 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class SallieMaeGateway < Gateway
4
+ URL = 'https://trans.salliemae.com/cgi-bin/process.cgi'
5
+
6
+ # The countries the gateway supports merchants from as 2 digit ISO country codes
7
+ self.supported_countries = ['US']
8
+
9
+ # The card types supported by the payment gateway
10
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
11
+
12
+ # The homepage URL of the gateway
13
+ self.homepage_url = 'http://www.salliemae.com/'
14
+
15
+ # The name of the gateway
16
+ self.display_name = 'Sallie Mae'
17
+
18
+ def initialize(options = {})
19
+ requires!(options, :login)
20
+ @options = options
21
+ super
22
+ end
23
+
24
+ def test?
25
+ @options[:login] == "TEST0"
26
+ end
27
+
28
+ def authorize(money, creditcard, options = {})
29
+ post = PostData.new
30
+ add_invoice(post, options)
31
+ add_creditcard(post, creditcard)
32
+ add_address(post, creditcard, options)
33
+ add_customer_data(post, options)
34
+
35
+ commit(:authonly, money, post)
36
+ end
37
+
38
+ def purchase(money, creditcard, options = {})
39
+ post = PostData.new
40
+ add_invoice(post, options)
41
+ add_creditcard(post, creditcard)
42
+ add_address(post, creditcard, options)
43
+ add_customer_data(post, options)
44
+
45
+ commit(:sale, money, post)
46
+ end
47
+
48
+ def capture(money, authorization, options = {})
49
+ post = PostData.new
50
+ post[:postonly] = authorization
51
+ commit(:capture, money, post)
52
+ end
53
+
54
+ private
55
+
56
+ def add_customer_data(post, options)
57
+ if address = options[:billing_address] || options[:shipping_address] || options[:address]
58
+ post[:ci_phone] = address[:phone].to_s
59
+ end
60
+
61
+ post[:ci_email] = options[:email].to_s unless options[:email].blank?
62
+ post[:ci_IP] = options[:ip].to_s unless options[:ip].blank?
63
+ end
64
+
65
+ def add_address(post, creditcard, options)
66
+ if address = options[:billing_address] || options[:address]
67
+ post[:ci_billaddr1] = address[:address1].to_s
68
+ post[:ci_billaddr2] = address[:address2].to_s unless address[:address2].blank?
69
+ post[:ci_billcity] = address[:city].to_s
70
+ post[:ci_billstate] = address[:state].to_s
71
+ post[:ci_billzip] = address[:zip].to_s
72
+ end
73
+
74
+ if shipping_address = options[:shipping_address] || options[:address]
75
+ post[:ci_shipaddr1] = shipping_address[:address1].to_s
76
+ post[:ci_shipaddr2] = shipping_address[:address2].to_s unless shipping_address[:address2].blank?
77
+ post[:ci_shipcity] = shipping_address[:city].to_s
78
+ post[:ci_shipstate] = shipping_address[:state].to_s
79
+ post[:ci_shipzip] = shipping_address[:zip].to_s
80
+ end
81
+ end
82
+
83
+ def add_invoice(post, options)
84
+ memo = "OrderID: #{options[:order_id]}\nDescription: #{options[:description]}"
85
+ post[:ci_memo] = memo
86
+ end
87
+
88
+ def add_creditcard(post, creditcard)
89
+ post[:ccnum] = creditcard.number.to_s
90
+ post[:ccname] = creditcard.name.to_s
91
+ post[:cvv2] = creditcard.verification_value.to_s if creditcard.verification_value?
92
+ post[:expmon] = creditcard.month.to_s
93
+ post[:expyear] = creditcard.year.to_s
94
+ end
95
+
96
+ def parse(body)
97
+ h = {}
98
+ body.gsub!("<html><body><plaintext>", "")
99
+ body.
100
+ split("\r\n").
101
+ map do |i|
102
+ a = i.split("=")
103
+ h[a.first] = a.last unless a.first.nil?
104
+ end
105
+ h
106
+ end
107
+
108
+ def commit(action, money, parameters)
109
+ parameters[:acctid] = @options[:login].to_s
110
+ parameters[:subid] = @options[:sub_id].to_s unless @options[:sub_id].blank?
111
+ parameters[:amount] = amount(money)
112
+
113
+ case action
114
+ when :sale
115
+ parameters[:action] = "ns_quicksale_cc"
116
+ when :authonly
117
+ parameters[:action] = "ns_quicksale_cc"
118
+ parameters[:authonly] = 1
119
+ when :capture
120
+ parameters[:action] = "ns_quicksale_cc"
121
+ end
122
+
123
+ response = parse(ssl_post(URL, parameters.to_post_data) || "")
124
+ Response.new(successful?(response), message_from(response), response,
125
+ :test => test?,
126
+ :authorization => response["refcode"]
127
+ )
128
+ end
129
+
130
+ def successful?(response)
131
+ response["Status"] == "Accepted"
132
+ end
133
+
134
+ def message_from(response)
135
+ if successful?(response)
136
+ "Accepted"
137
+ else
138
+ response["Reason"].split(":")[2].capitalize unless response["Reason"].nil?
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+