aktivemerchant 2.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 (193) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +1596 -0
  3. data/CONTRIBUTORS +511 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +18 -0
  6. data/lib/active_merchant.rb +108 -0
  7. data/lib/active_merchant/billing.rb +13 -0
  8. data/lib/active_merchant/billing/apple_pay_payment_token.rb +22 -0
  9. data/lib/active_merchant/billing/avs_result.rb +98 -0
  10. data/lib/active_merchant/billing/base.rb +72 -0
  11. data/lib/active_merchant/billing/check.rb +76 -0
  12. data/lib/active_merchant/billing/compatibility.rb +120 -0
  13. data/lib/active_merchant/billing/credit_card.rb +352 -0
  14. data/lib/active_merchant/billing/credit_card_formatting.rb +24 -0
  15. data/lib/active_merchant/billing/credit_card_methods.rb +160 -0
  16. data/lib/active_merchant/billing/cvv_result.rb +38 -0
  17. data/lib/active_merchant/billing/gateway.rb +268 -0
  18. data/lib/active_merchant/billing/gateways.rb +17 -0
  19. data/lib/active_merchant/billing/gateways/adyen.rb +209 -0
  20. data/lib/active_merchant/billing/gateways/alfabank.rb +117 -0
  21. data/lib/active_merchant/billing/gateways/app55.rb +176 -0
  22. data/lib/active_merchant/billing/gateways/authorize_net.rb +419 -0
  23. data/lib/active_merchant/billing/gateways/authorize_net_arb.rb +417 -0
  24. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +976 -0
  25. data/lib/active_merchant/billing/gateways/balanced.rb +256 -0
  26. data/lib/active_merchant/billing/gateways/bank_frick.rb +225 -0
  27. data/lib/active_merchant/billing/gateways/banwire.rb +105 -0
  28. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +314 -0
  29. data/lib/active_merchant/billing/gateways/barclays_epdq_extra_plus.rb +15 -0
  30. data/lib/active_merchant/billing/gateways/be2bill.rb +131 -0
  31. data/lib/active_merchant/billing/gateways/beanstream.rb +188 -0
  32. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +393 -0
  33. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
  34. data/lib/active_merchant/billing/gateways/blue_pay.rb +506 -0
  35. data/lib/active_merchant/billing/gateways/bogus.rb +140 -0
  36. data/lib/active_merchant/billing/gateways/borgun.rb +210 -0
  37. data/lib/active_merchant/billing/gateways/braintree.rb +19 -0
  38. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
  39. data/lib/active_merchant/billing/gateways/braintree_blue.rb +515 -0
  40. data/lib/active_merchant/billing/gateways/braintree_orange.rb +20 -0
  41. data/lib/active_merchant/billing/gateways/bridge_pay.rb +189 -0
  42. data/lib/active_merchant/billing/gateways/card_save.rb +23 -0
  43. data/lib/active_merchant/billing/gateways/card_stream.rb +220 -0
  44. data/lib/active_merchant/billing/gateways/cashnet.rb +191 -0
  45. data/lib/active_merchant/billing/gateways/cc5.rb +201 -0
  46. data/lib/active_merchant/billing/gateways/cecabank.rb +229 -0
  47. data/lib/active_merchant/billing/gateways/certo_direct.rb +278 -0
  48. data/lib/active_merchant/billing/gateways/checkout.rb +213 -0
  49. data/lib/active_merchant/billing/gateways/commercegate.rb +143 -0
  50. data/lib/active_merchant/billing/gateways/conekta.rb +209 -0
  51. data/lib/active_merchant/billing/gateways/cyber_source.rb +709 -0
  52. data/lib/active_merchant/billing/gateways/data_cash.rb +600 -0
  53. data/lib/active_merchant/billing/gateways/efsnet.rb +219 -0
  54. data/lib/active_merchant/billing/gateways/elavon.rb +348 -0
  55. data/lib/active_merchant/billing/gateways/epay.rb +275 -0
  56. data/lib/active_merchant/billing/gateways/evo_ca.rb +308 -0
  57. data/lib/active_merchant/billing/gateways/eway.rb +214 -0
  58. data/lib/active_merchant/billing/gateways/eway_managed.rb +291 -0
  59. data/lib/active_merchant/billing/gateways/eway_rapid.rb +524 -0
  60. data/lib/active_merchant/billing/gateways/exact.rb +218 -0
  61. data/lib/active_merchant/billing/gateways/fat_zebra.rb +173 -0
  62. data/lib/active_merchant/billing/gateways/federated_canada.rb +160 -0
  63. data/lib/active_merchant/billing/gateways/finansbank.rb +23 -0
  64. data/lib/active_merchant/billing/gateways/first_giving.rb +143 -0
  65. data/lib/active_merchant/billing/gateways/first_pay.rb +160 -0
  66. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +355 -0
  67. data/lib/active_merchant/billing/gateways/garanti.rb +257 -0
  68. data/lib/active_merchant/billing/gateways/global_transport.rb +183 -0
  69. data/lib/active_merchant/billing/gateways/hdfc.rb +207 -0
  70. data/lib/active_merchant/billing/gateways/hps.rb +288 -0
  71. data/lib/active_merchant/billing/gateways/iats_payments.rb +251 -0
  72. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +246 -0
  73. data/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem +13 -0
  74. data/lib/active_merchant/billing/gateways/ideal/ideal_response.rb +29 -0
  75. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +66 -0
  76. data/lib/active_merchant/billing/gateways/inspire.rb +213 -0
  77. data/lib/active_merchant/billing/gateways/instapay.rb +163 -0
  78. data/lib/active_merchant/billing/gateways/iridium.rb +457 -0
  79. data/lib/active_merchant/billing/gateways/itransact.rb +448 -0
  80. data/lib/active_merchant/billing/gateways/jetpay.rb +275 -0
  81. data/lib/active_merchant/billing/gateways/linkpoint.rb +438 -0
  82. data/lib/active_merchant/billing/gateways/litle.rb +346 -0
  83. data/lib/active_merchant/billing/gateways/maxipago.rb +197 -0
  84. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +170 -0
  85. data/lib/active_merchant/billing/gateways/merchant_one.rb +114 -0
  86. data/lib/active_merchant/billing/gateways/merchant_ware.rb +319 -0
  87. data/lib/active_merchant/billing/gateways/merchant_ware_version_four.rb +268 -0
  88. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +195 -0
  89. data/lib/active_merchant/billing/gateways/mercury.rb +333 -0
  90. data/lib/active_merchant/billing/gateways/metrics_global.rb +303 -0
  91. data/lib/active_merchant/billing/gateways/migs.rb +265 -0
  92. data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +100 -0
  93. data/lib/active_merchant/billing/gateways/modern_payments.rb +37 -0
  94. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +219 -0
  95. data/lib/active_merchant/billing/gateways/moneris.rb +309 -0
  96. data/lib/active_merchant/billing/gateways/moneris_us.rb +291 -0
  97. data/lib/active_merchant/billing/gateways/money_movers.rb +152 -0
  98. data/lib/active_merchant/billing/gateways/nab_transact.rb +280 -0
  99. data/lib/active_merchant/billing/gateways/net_registry.rb +198 -0
  100. data/lib/active_merchant/billing/gateways/netaxept.rb +181 -0
  101. data/lib/active_merchant/billing/gateways/netbilling.rb +190 -0
  102. data/lib/active_merchant/billing/gateways/netpay.rb +223 -0
  103. data/lib/active_merchant/billing/gateways/network_merchants.rb +242 -0
  104. data/lib/active_merchant/billing/gateways/nmi.rb +256 -0
  105. data/lib/active_merchant/billing/gateways/ogone.rb +435 -0
  106. data/lib/active_merchant/billing/gateways/openpay.rb +194 -0
  107. data/lib/active_merchant/billing/gateways/optimal_payment.rb +313 -0
  108. data/lib/active_merchant/billing/gateways/orbital.rb +803 -0
  109. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +47 -0
  110. data/lib/active_merchant/billing/gateways/pac_net_raven.rb +207 -0
  111. data/lib/active_merchant/billing/gateways/pago_facil.rb +122 -0
  112. data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +261 -0
  113. data/lib/active_merchant/billing/gateways/pay_junction.rb +390 -0
  114. data/lib/active_merchant/billing/gateways/pay_secure.rb +112 -0
  115. data/lib/active_merchant/billing/gateways/pay_u_latam.rb +462 -0
  116. data/lib/active_merchant/billing/gateways/paybox_direct.rb +188 -0
  117. data/lib/active_merchant/billing/gateways/payex.rb +412 -0
  118. data/lib/active_merchant/billing/gateways/payflow.rb +304 -0
  119. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +209 -0
  120. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
  121. data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
  122. data/lib/active_merchant/billing/gateways/payflow_express.rb +224 -0
  123. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
  124. data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
  125. data/lib/active_merchant/billing/gateways/payment_express.rb +353 -0
  126. data/lib/active_merchant/billing/gateways/paymill.rb +281 -0
  127. data/lib/active_merchant/billing/gateways/paypal.rb +117 -0
  128. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +670 -0
  129. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +65 -0
  130. data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +262 -0
  131. data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
  132. data/lib/active_merchant/billing/gateways/paypal_digital_goods.rb +44 -0
  133. data/lib/active_merchant/billing/gateways/paypal_express.rb +264 -0
  134. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +30 -0
  135. data/lib/active_merchant/billing/gateways/payscout.rb +162 -0
  136. data/lib/active_merchant/billing/gateways/paystation.rb +199 -0
  137. data/lib/active_merchant/billing/gateways/payway.rb +207 -0
  138. data/lib/active_merchant/billing/gateways/pin.rb +197 -0
  139. data/lib/active_merchant/billing/gateways/plugnpay.rb +283 -0
  140. data/lib/active_merchant/billing/gateways/psigate.rb +216 -0
  141. data/lib/active_merchant/billing/gateways/psl_card.rb +303 -0
  142. data/lib/active_merchant/billing/gateways/qbms.rb +292 -0
  143. data/lib/active_merchant/billing/gateways/quantum.rb +276 -0
  144. data/lib/active_merchant/billing/gateways/quickpay.rb +367 -0
  145. data/lib/active_merchant/billing/gateways/realex.rb +298 -0
  146. data/lib/active_merchant/billing/gateways/redsys.rb +391 -0
  147. data/lib/active_merchant/billing/gateways/sage.rb +175 -0
  148. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +87 -0
  149. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +114 -0
  150. data/lib/active_merchant/billing/gateways/sage/sage_vault.rb +149 -0
  151. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +102 -0
  152. data/lib/active_merchant/billing/gateways/sage_pay.rb +398 -0
  153. data/lib/active_merchant/billing/gateways/sallie_mae.rb +143 -0
  154. data/lib/active_merchant/billing/gateways/secure_net.rb +252 -0
  155. data/lib/active_merchant/billing/gateways/secure_pay.rb +201 -0
  156. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +281 -0
  157. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +105 -0
  158. data/lib/active_merchant/billing/gateways/skip_jack.rb +452 -0
  159. data/lib/active_merchant/billing/gateways/smart_ps.rb +283 -0
  160. data/lib/active_merchant/billing/gateways/so_easy_pay.rb +194 -0
  161. data/lib/active_merchant/billing/gateways/spreedly_core.rb +247 -0
  162. data/lib/active_merchant/billing/gateways/stripe.rb +411 -0
  163. data/lib/active_merchant/billing/gateways/swipe_checkout.rb +157 -0
  164. data/lib/active_merchant/billing/gateways/tns.rb +227 -0
  165. data/lib/active_merchant/billing/gateways/trans_first.rb +126 -0
  166. data/lib/active_merchant/billing/gateways/transax.rb +23 -0
  167. data/lib/active_merchant/billing/gateways/transnational.rb +10 -0
  168. data/lib/active_merchant/billing/gateways/trust_commerce.rb +416 -0
  169. data/lib/active_merchant/billing/gateways/usa_epay.rb +25 -0
  170. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +1516 -0
  171. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +254 -0
  172. data/lib/active_merchant/billing/gateways/verifi.rb +225 -0
  173. data/lib/active_merchant/billing/gateways/viaklix.rb +183 -0
  174. data/lib/active_merchant/billing/gateways/vindicia.rb +385 -0
  175. data/lib/active_merchant/billing/gateways/webpay.rb +97 -0
  176. data/lib/active_merchant/billing/gateways/wepay.rb +189 -0
  177. data/lib/active_merchant/billing/gateways/wirecard.rb +421 -0
  178. data/lib/active_merchant/billing/gateways/worldpay.rb +331 -0
  179. data/lib/active_merchant/billing/gateways/worldpay_us.rb +181 -0
  180. data/lib/active_merchant/billing/model.rb +30 -0
  181. data/lib/active_merchant/billing/payment_token.rb +21 -0
  182. data/lib/active_merchant/billing/rails.rb +3 -0
  183. data/lib/active_merchant/billing/response.rb +91 -0
  184. data/lib/active_merchant/country.rb +332 -0
  185. data/lib/active_merchant/empty.rb +20 -0
  186. data/lib/active_merchant/errors.rb +29 -0
  187. data/lib/active_merchant/offsite_payments_shim.rb +19 -0
  188. data/lib/active_merchant/version.rb +3 -0
  189. data/lib/activemerchant.rb +1 -0
  190. data/lib/support/gateway_support.rb +71 -0
  191. data/lib/support/outbound_hosts.rb +25 -0
  192. data/lib/support/ssl_verify.rb +93 -0
  193. metadata +400 -0
@@ -0,0 +1,398 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class SagePayGateway < Gateway
4
+ cattr_accessor :simulate
5
+ self.simulate = false
6
+
7
+ class_attribute :simulator_url
8
+
9
+ self.test_url = 'https://test.sagepay.com/gateway/service'
10
+ self.live_url = 'https://live.sagepay.com/gateway/service'
11
+ self.simulator_url = 'https://test.sagepay.com/Simulator'
12
+
13
+ APPROVED = 'OK'
14
+
15
+ TRANSACTIONS = {
16
+ :purchase => 'PAYMENT',
17
+ :credit => 'REFUND',
18
+ :authorization => 'DEFERRED',
19
+ :capture => 'RELEASE',
20
+ :void => 'VOID',
21
+ :abort => 'ABORT',
22
+ :store => 'TOKEN',
23
+ :unstore => 'REMOVETOKEN'
24
+ }
25
+
26
+ CREDIT_CARDS = {
27
+ :visa => "VISA",
28
+ :master => "MC",
29
+ :delta => "DELTA",
30
+ :solo => "SOLO",
31
+ :switch => "MAESTRO",
32
+ :maestro => "MAESTRO",
33
+ :american_express => "AMEX",
34
+ :electron => "UKE",
35
+ :diners_club => "DC",
36
+ :jcb => "JCB"
37
+ }
38
+
39
+ ELECTRON = /^(424519|42496[23]|450875|48440[6-8]|4844[1-5][1-5]|4917[3-5][0-9]|491880)\d{10}(\d{3})?$/
40
+
41
+ AVS_CVV_CODE = {
42
+ "NOTPROVIDED" => nil,
43
+ "NOTCHECKED" => 'X',
44
+ "MATCHED" => 'Y',
45
+ "NOTMATCHED" => 'N'
46
+ }
47
+
48
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :switch, :solo, :maestro, :diners_club]
49
+ self.supported_countries = ['GB', 'IE']
50
+ self.default_currency = 'GBP'
51
+
52
+ self.homepage_url = 'http://www.sagepay.com'
53
+ self.display_name = 'SagePay'
54
+
55
+ def initialize(options = {})
56
+ requires!(options, :login)
57
+ super
58
+ end
59
+
60
+ def purchase(money, payment_method, options = {})
61
+ requires!(options, :order_id)
62
+
63
+ post = {}
64
+
65
+ add_amount(post, money, options)
66
+ add_invoice(post, options)
67
+ add_payment_method(post, payment_method, options)
68
+ add_address(post, options)
69
+ add_customer_data(post, options)
70
+ add_optional_data(post, options)
71
+
72
+ commit(:purchase, post)
73
+ end
74
+
75
+ def authorize(money, payment_method, options = {})
76
+ requires!(options, :order_id)
77
+
78
+ post = {}
79
+
80
+ add_amount(post, money, options)
81
+ add_invoice(post, options)
82
+ add_payment_method(post, payment_method, options)
83
+ add_address(post, options)
84
+ add_customer_data(post, options)
85
+ add_optional_data(post, options)
86
+
87
+ commit(:authorization, post)
88
+ end
89
+
90
+ # You can only capture a transaction once, even if you didn't capture the full amount the first time.
91
+ def capture(money, identification, options = {})
92
+ post = {}
93
+
94
+ add_reference(post, identification)
95
+ add_release_amount(post, money, options)
96
+
97
+ commit(:capture, post)
98
+ end
99
+
100
+ def void(identification, options = {})
101
+ post = {}
102
+
103
+ add_reference(post, identification)
104
+ action = abort_or_void_from(identification)
105
+
106
+ commit(action, post)
107
+ end
108
+
109
+ # Refunding requires a new order_id to passed in, as well as a description
110
+ def refund(money, identification, options = {})
111
+ requires!(options, :order_id, :description)
112
+
113
+ post = {}
114
+
115
+ add_credit_reference(post, identification)
116
+ add_amount(post, money, options)
117
+ add_invoice(post, options)
118
+
119
+ commit(:credit, post)
120
+ end
121
+
122
+ def credit(money, identification, options = {})
123
+ ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE
124
+ refund(money, identification, options)
125
+ end
126
+
127
+ def store(credit_card, options = {})
128
+ post = {}
129
+ add_credit_card(post, credit_card)
130
+ add_currency(post, 0, options)
131
+
132
+ commit(:store, post)
133
+ end
134
+
135
+ def unstore(token, options = {})
136
+ post = {}
137
+ add_token(post, token)
138
+ commit(:unstore, post)
139
+ end
140
+
141
+ private
142
+ def add_reference(post, identification)
143
+ order_id, transaction_id, authorization, security_key = identification.split(';')
144
+
145
+ add_pair(post, :VendorTxCode, order_id)
146
+ add_pair(post, :VPSTxId, transaction_id)
147
+ add_pair(post, :TxAuthNo, authorization)
148
+ add_pair(post, :SecurityKey, security_key)
149
+ end
150
+
151
+ def add_credit_reference(post, identification)
152
+ order_id, transaction_id, authorization, security_key = identification.split(';')
153
+
154
+ add_pair(post, :RelatedVendorTxCode, order_id)
155
+ add_pair(post, :RelatedVPSTxId, transaction_id)
156
+ add_pair(post, :RelatedTxAuthNo, authorization)
157
+ add_pair(post, :RelatedSecurityKey, security_key)
158
+ end
159
+
160
+ def add_amount(post, money, options)
161
+ currency = options[:currency] || currency(money)
162
+ add_pair(post, :Amount, localized_amount(money, currency), :required => true)
163
+ add_pair(post, :Currency, currency, :required => true)
164
+ end
165
+
166
+ def add_currency(post, money, options)
167
+ currency = options[:currency] || currency(money)
168
+ add_pair(post, :Currency, currency, :required => true)
169
+ end
170
+
171
+ # doesn't actually use the currency -- dodgy!
172
+ def add_release_amount(post, money, options)
173
+ add_pair(post, :ReleaseAmount, amount(money), :required => true)
174
+ end
175
+
176
+ def add_customer_data(post, options)
177
+ add_pair(post, :CustomerEMail, truncate(options[:email], 255)) unless options[:email].blank?
178
+ add_pair(post, :ClientIPAddress, options[:ip])
179
+ end
180
+
181
+ def add_optional_data(post, options)
182
+ add_pair(post, :GiftAidPayment, options[:gift_aid_payment]) unless options[:gift_aid_payment].blank?
183
+ add_pair(post, :Apply3DSecure, options[:apply_3d_secure]) unless options[:apply_3d_secure].blank?
184
+ add_pair(post, :CreateToken, 1) unless options[:store].blank?
185
+ add_pair(post, :FIRecipientAcctNumber, options[:recipient_account_number])
186
+ add_pair(post, :FIRecipientSurname, options[:recipient_surname])
187
+ add_pair(post, :FIRecipientPostcode, options[:recipient_postcode])
188
+ add_pair(post, :FIRecipientDoB, options[:recipient_dob])
189
+ end
190
+
191
+ def add_address(post, options)
192
+ if billing_address = options[:billing_address] || options[:address]
193
+ first_name, last_name = parse_first_and_last_name(billing_address[:name])
194
+ add_pair(post, :BillingSurname, last_name)
195
+ add_pair(post, :BillingFirstnames, first_name)
196
+ add_pair(post, :BillingAddress1, truncate(billing_address[:address1], 100))
197
+ add_pair(post, :BillingAddress2, truncate(billing_address[:address2], 100))
198
+ add_pair(post, :BillingCity, truncate(billing_address[:city], 40))
199
+ add_pair(post, :BillingState, truncate(billing_address[:state], 2)) if is_usa(billing_address[:country])
200
+ add_pair(post, :BillingCountry, truncate(billing_address[:country], 2))
201
+ add_pair(post, :BillingPhone, sanitize_phone(billing_address[:phone]))
202
+ add_pair(post, :BillingPostCode, truncate(billing_address[:zip], 10))
203
+ end
204
+
205
+ if shipping_address = options[:shipping_address] || billing_address
206
+ first_name, last_name = parse_first_and_last_name(shipping_address[:name])
207
+ add_pair(post, :DeliverySurname, last_name)
208
+ add_pair(post, :DeliveryFirstnames, first_name)
209
+ add_pair(post, :DeliveryAddress1, truncate(shipping_address[:address1], 100))
210
+ add_pair(post, :DeliveryAddress2, truncate(shipping_address[:address2], 100))
211
+ add_pair(post, :DeliveryCity, truncate(shipping_address[:city], 40))
212
+ add_pair(post, :DeliveryState, truncate(shipping_address[:state], 2)) if is_usa(shipping_address[:country])
213
+ add_pair(post, :DeliveryCountry, truncate(shipping_address[:country], 2))
214
+ add_pair(post, :DeliveryPhone, sanitize_phone(shipping_address[:phone]))
215
+ add_pair(post, :DeliveryPostCode, truncate(shipping_address[:zip], 10))
216
+ end
217
+ end
218
+
219
+ def add_invoice(post, options)
220
+ add_pair(post, :VendorTxCode, sanitize_order_id(options[:order_id]), :required => true)
221
+ add_pair(post, :Description, truncate(options[:description] || options[:order_id], 100))
222
+ end
223
+
224
+ def add_payment_method(post, payment_method, options)
225
+ if payment_method.respond_to?(:number)
226
+ add_credit_card(post, payment_method)
227
+ else
228
+ add_token_details(post, payment_method, options)
229
+ end
230
+ end
231
+
232
+ def add_credit_card(post, credit_card)
233
+ add_pair(post, :CardHolder, truncate(credit_card.name, 50), :required => true)
234
+ add_pair(post, :CardNumber, credit_card.number, :required => true)
235
+
236
+ add_pair(post, :ExpiryDate, format_date(credit_card.month, credit_card.year), :required => true)
237
+
238
+ if requires_start_date_or_issue_number?(credit_card)
239
+ add_pair(post, :StartDate, format_date(credit_card.start_month, credit_card.start_year))
240
+ add_pair(post, :IssueNumber, credit_card.issue_number)
241
+ end
242
+ add_pair(post, :CardType, map_card_type(credit_card))
243
+
244
+ add_pair(post, :CV2, credit_card.verification_value)
245
+ end
246
+
247
+ def add_token_details(post, token, options)
248
+ add_token(post, token)
249
+ add_pair(post, :StoreToken, options[:customer])
250
+ end
251
+
252
+ def add_token(post, token)
253
+ add_pair(post, :Token, token)
254
+ end
255
+
256
+ def sanitize_order_id(order_id)
257
+ cleansed = order_id.to_s.gsub(/[^-a-zA-Z0-9._]/, '')
258
+ truncate(cleansed, 40)
259
+ end
260
+
261
+ def sanitize_phone(phone)
262
+ return nil unless phone
263
+ cleansed = phone.to_s.gsub(/[^0-9+]/, '')
264
+ truncate(cleansed, 20)
265
+ end
266
+
267
+ def truncate(value, max_size)
268
+ return nil unless value
269
+ value[0, max_size]
270
+ end
271
+
272
+ def is_usa(country)
273
+ truncate(country, 2) == 'US'
274
+ end
275
+
276
+ def map_card_type(credit_card)
277
+ raise ArgumentError, "The credit card type must be provided" if card_brand(credit_card).blank?
278
+
279
+ card_type = card_brand(credit_card).to_sym
280
+
281
+ # Check if it is an electron card
282
+ if card_type == :visa && credit_card.number =~ ELECTRON
283
+ CREDIT_CARDS[:electron]
284
+ else
285
+ CREDIT_CARDS[card_type]
286
+ end
287
+ end
288
+
289
+ # MMYY format
290
+ def format_date(month, year)
291
+ return nil if year.blank? || month.blank?
292
+
293
+ year = sprintf("%.4i", year)
294
+ month = sprintf("%.2i", month)
295
+
296
+ "#{month}#{year[-2..-1]}"
297
+ end
298
+
299
+ def commit(action, parameters)
300
+ response = parse( ssl_post(url_for(action), post_data(action, parameters)) )
301
+
302
+ Response.new(response["Status"] == APPROVED, message_from(response), response,
303
+ :test => test?,
304
+ :authorization => authorization_from(response, parameters, action),
305
+ :avs_result => {
306
+ :street_match => AVS_CVV_CODE[ response["AddressResult"] ],
307
+ :postal_match => AVS_CVV_CODE[ response["PostCodeResult"] ],
308
+ },
309
+ :cvv_result => AVS_CVV_CODE[ response["CV2Result"] ]
310
+ )
311
+ end
312
+
313
+ def authorization_from(response, params, action)
314
+ case action
315
+ when :store
316
+ response['Token']
317
+ else
318
+ [ params[:VendorTxCode],
319
+ response["VPSTxId"],
320
+ response["TxAuthNo"],
321
+ response["SecurityKey"],
322
+ action ].join(";")
323
+ end
324
+ end
325
+
326
+ def abort_or_void_from(identification)
327
+ original_transaction = identification.split(';').last
328
+ original_transaction == 'authorization' ? :abort : :void
329
+ end
330
+
331
+ def url_for(action)
332
+ simulate ? build_simulator_url(action) : build_url(action)
333
+ end
334
+
335
+ def build_url(action)
336
+ endpoint = case action
337
+ when :purchase, :authorization then "vspdirect-register"
338
+ when :store then 'directtoken'
339
+ else TRANSACTIONS[action].downcase
340
+ end
341
+ "#{test? ? self.test_url : self.live_url}/#{endpoint}.vsp"
342
+ end
343
+
344
+ def build_simulator_url(action)
345
+ endpoint = [ :purchase, :authorization ].include?(action) ? "VSPDirectGateway.asp" : "VSPServerGateway.asp?Service=Vendor#{TRANSACTIONS[action].capitalize}Tx"
346
+ "#{self.simulator_url}/#{endpoint}"
347
+ end
348
+
349
+ def message_from(response)
350
+ response['Status'] == APPROVED ? 'Success' : (response['StatusDetail'] || 'Unspecified error') # simonr 20080207 can't actually get non-nil blanks, so this is shorter
351
+ end
352
+
353
+ def post_data(action, parameters = {})
354
+ parameters.update(
355
+ :Vendor => @options[:login],
356
+ :TxType => TRANSACTIONS[action],
357
+ :VPSProtocol => "3.00"
358
+ )
359
+
360
+ if(application_id && (application_id != Gateway.application_id))
361
+ parameters.update(:ReferrerID => application_id)
362
+ end
363
+
364
+ parameters.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
365
+ end
366
+
367
+ # SagePay returns data in the following format
368
+ # Key1=value1
369
+ # Key2=value2
370
+ def parse(body)
371
+ result = {}
372
+ body.to_s.each_line do |pair|
373
+ result[$1] = $2 if pair.strip =~ /\A([^=]+)=(.+)\Z/im
374
+ end
375
+ result
376
+ end
377
+
378
+ def add_pair(post, key, value, options = {})
379
+ post[key] = value if !value.blank? || options[:required]
380
+ end
381
+
382
+ def parse_first_and_last_name(value)
383
+ name = value.to_s.split(' ')
384
+
385
+ last_name = name.pop || ''
386
+ first_name = name.join(' ')
387
+ [ truncate(first_name, 20), truncate(last_name, 20) ]
388
+ end
389
+
390
+ def localized_amount(money, currency)
391
+ amount = amount(money)
392
+ CURRENCIES_WITHOUT_FRACTIONS.include?(currency.to_s) ? amount.split('.').first : amount
393
+ end
394
+ end
395
+
396
+ end
397
+ end
398
+
@@ -0,0 +1,143 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class SallieMaeGateway < Gateway
4
+ self.live_url = self.test_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
+ super
21
+ end
22
+
23
+ def test?
24
+ @options[:login] == "TEST0"
25
+ end
26
+
27
+ def authorize(money, creditcard, options = {})
28
+ post = PostData.new
29
+ add_invoice(post, options)
30
+ add_creditcard(post, creditcard)
31
+ add_address(post, creditcard, options)
32
+ add_customer_data(post, options)
33
+
34
+ commit(:authonly, money, post)
35
+ end
36
+
37
+ def purchase(money, creditcard, options = {})
38
+ post = PostData.new
39
+ add_invoice(post, options)
40
+ add_creditcard(post, creditcard)
41
+ add_address(post, creditcard, options)
42
+ add_customer_data(post, options)
43
+
44
+ commit(:sale, money, post)
45
+ end
46
+
47
+ def capture(money, authorization, options = {})
48
+ post = PostData.new
49
+ post[:postonly] = authorization
50
+ commit(:capture, money, post)
51
+ end
52
+
53
+ private
54
+
55
+ def add_customer_data(post, options)
56
+ if address = options[:billing_address] || options[:shipping_address] || options[:address]
57
+ post[:ci_phone] = address[:phone].to_s
58
+ end
59
+
60
+ post[:ci_email] = options[:email].to_s unless options[:email].blank?
61
+ post[:ci_IP] = options[:ip].to_s unless options[:ip].blank?
62
+ end
63
+
64
+ def add_address(post, creditcard, options)
65
+ if address = options[:billing_address] || options[:address]
66
+ post[:ci_billaddr1] = address[:address1].to_s
67
+ post[:ci_billaddr2] = address[:address2].to_s unless address[:address2].blank?
68
+ post[:ci_billcity] = address[:city].to_s
69
+ post[:ci_billstate] = address[:state].to_s
70
+ post[:ci_billzip] = address[:zip].to_s
71
+ end
72
+
73
+ if shipping_address = options[:shipping_address] || options[:address]
74
+ post[:ci_shipaddr1] = shipping_address[:address1].to_s
75
+ post[:ci_shipaddr2] = shipping_address[:address2].to_s unless shipping_address[:address2].blank?
76
+ post[:ci_shipcity] = shipping_address[:city].to_s
77
+ post[:ci_shipstate] = shipping_address[:state].to_s
78
+ post[:ci_shipzip] = shipping_address[:zip].to_s
79
+ end
80
+ end
81
+
82
+ def add_invoice(post, options)
83
+ memo = "OrderID: #{options[:order_id]}\nDescription: #{options[:description]}"
84
+ post[:ci_memo] = memo
85
+ end
86
+
87
+ def add_creditcard(post, creditcard)
88
+ post[:ccnum] = creditcard.number.to_s
89
+ post[:ccname] = creditcard.name.to_s
90
+ post[:cvv2] = creditcard.verification_value.to_s if creditcard.verification_value?
91
+ post[:expmon] = creditcard.month.to_s
92
+ post[:expyear] = creditcard.year.to_s
93
+ end
94
+
95
+ def parse(body)
96
+ h = {}
97
+ body.gsub!("<html><body><plaintext>", "")
98
+ body.
99
+ split("\r\n").
100
+ map do |i|
101
+ a = i.split("=")
102
+ h[a.first] = a.last unless a.first.nil?
103
+ end
104
+ h
105
+ end
106
+
107
+ def commit(action, money, parameters)
108
+ parameters[:acctid] = @options[:login].to_s
109
+ parameters[:subid] = @options[:sub_id].to_s unless @options[:sub_id].blank?
110
+ parameters[:amount] = amount(money)
111
+
112
+ case action
113
+ when :sale
114
+ parameters[:action] = "ns_quicksale_cc"
115
+ when :authonly
116
+ parameters[:action] = "ns_quicksale_cc"
117
+ parameters[:authonly] = 1
118
+ when :capture
119
+ parameters[:action] = "ns_quicksale_cc"
120
+ end
121
+
122
+ response = parse(ssl_post(self.live_url, parameters.to_post_data) || "")
123
+ Response.new(successful?(response), message_from(response), response,
124
+ :test => test?,
125
+ :authorization => response["refcode"]
126
+ )
127
+ end
128
+
129
+ def successful?(response)
130
+ response["Status"] == "Accepted"
131
+ end
132
+
133
+ def message_from(response)
134
+ if successful?(response)
135
+ "Accepted"
136
+ else
137
+ response["Reason"].split(":")[2].capitalize unless response["Reason"].nil?
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+