fishman-activemerchant 1.18.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 (188) hide show
  1. data/CHANGELOG +733 -0
  2. data/CONTRIBUTORS +257 -0
  3. data/MIT-LICENSE +20 -0
  4. data/gem-public_cert.pem +20 -0
  5. data/lib/active_merchant.rb +47 -0
  6. data/lib/active_merchant/billing.rb +9 -0
  7. data/lib/active_merchant/billing/avs_result.rb +98 -0
  8. data/lib/active_merchant/billing/base.rb +57 -0
  9. data/lib/active_merchant/billing/check.rb +68 -0
  10. data/lib/active_merchant/billing/credit_card.rb +260 -0
  11. data/lib/active_merchant/billing/credit_card_formatting.rb +21 -0
  12. data/lib/active_merchant/billing/credit_card_methods.rb +125 -0
  13. data/lib/active_merchant/billing/cvv_result.rb +38 -0
  14. data/lib/active_merchant/billing/expiry_date.rb +34 -0
  15. data/lib/active_merchant/billing/gateway.rb +170 -0
  16. data/lib/active_merchant/billing/gateways.rb +18 -0
  17. data/lib/active_merchant/billing/gateways/authorize_net.rb +693 -0
  18. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +858 -0
  19. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +308 -0
  20. data/lib/active_merchant/billing/gateways/beanstream.rb +139 -0
  21. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +282 -0
  22. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
  23. data/lib/active_merchant/billing/gateways/blue_pay.rb +11 -0
  24. data/lib/active_merchant/billing/gateways/bogus.rb +142 -0
  25. data/lib/active_merchant/billing/gateways/braintree.rb +17 -0
  26. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
  27. data/lib/active_merchant/billing/gateways/braintree_blue.rb +303 -0
  28. data/lib/active_merchant/billing/gateways/braintree_orange.rb +17 -0
  29. data/lib/active_merchant/billing/gateways/card_save.rb +23 -0
  30. data/lib/active_merchant/billing/gateways/card_stream.rb +230 -0
  31. data/lib/active_merchant/billing/gateways/cyber_source.rb +430 -0
  32. data/lib/active_merchant/billing/gateways/data_cash.rb +597 -0
  33. data/lib/active_merchant/billing/gateways/efsnet.rb +235 -0
  34. data/lib/active_merchant/billing/gateways/elavon.rb +134 -0
  35. data/lib/active_merchant/billing/gateways/epay.rb +274 -0
  36. data/lib/active_merchant/billing/gateways/eway.rb +277 -0
  37. data/lib/active_merchant/billing/gateways/eway_managed.rb +264 -0
  38. data/lib/active_merchant/billing/gateways/exact.rb +222 -0
  39. data/lib/active_merchant/billing/gateways/federated_canada.rb +168 -0
  40. data/lib/active_merchant/billing/gateways/first_pay.rb +177 -0
  41. data/lib/active_merchant/billing/gateways/garanti.rb +262 -0
  42. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +250 -0
  43. data/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem +13 -0
  44. data/lib/active_merchant/billing/gateways/ideal/ideal_response.rb +29 -0
  45. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +55 -0
  46. data/lib/active_merchant/billing/gateways/inspire.rb +221 -0
  47. data/lib/active_merchant/billing/gateways/instapay.rb +164 -0
  48. data/lib/active_merchant/billing/gateways/iridium.rb +258 -0
  49. data/lib/active_merchant/billing/gateways/jetpay.rb +276 -0
  50. data/lib/active_merchant/billing/gateways/linkpoint.rb +454 -0
  51. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +156 -0
  52. data/lib/active_merchant/billing/gateways/merchant_ware.rb +289 -0
  53. data/lib/active_merchant/billing/gateways/modern_payments.rb +36 -0
  54. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +220 -0
  55. data/lib/active_merchant/billing/gateways/moneris.rb +209 -0
  56. data/lib/active_merchant/billing/gateways/net_registry.rb +189 -0
  57. data/lib/active_merchant/billing/gateways/netaxept.rb +239 -0
  58. data/lib/active_merchant/billing/gateways/netbilling.rb +168 -0
  59. data/lib/active_merchant/billing/gateways/nmi.rb +13 -0
  60. data/lib/active_merchant/billing/gateways/ogone.rb +292 -0
  61. data/lib/active_merchant/billing/gateways/optimal_payment.rb +274 -0
  62. data/lib/active_merchant/billing/gateways/orbital.rb +321 -0
  63. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +46 -0
  64. data/lib/active_merchant/billing/gateways/pay_junction.rb +392 -0
  65. data/lib/active_merchant/billing/gateways/pay_secure.rb +120 -0
  66. data/lib/active_merchant/billing/gateways/paybox_direct.rb +207 -0
  67. data/lib/active_merchant/billing/gateways/payflow.rb +253 -0
  68. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +207 -0
  69. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
  70. data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
  71. data/lib/active_merchant/billing/gateways/payflow_express.rb +222 -0
  72. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
  73. data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
  74. data/lib/active_merchant/billing/gateways/payment_express.rb +235 -0
  75. data/lib/active_merchant/billing/gateways/paypal.rb +121 -0
  76. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +354 -0
  77. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +49 -0
  78. data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
  79. data/lib/active_merchant/billing/gateways/paypal_express.rb +184 -0
  80. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +25 -0
  81. data/lib/active_merchant/billing/gateways/paypal_express_de.rb +14 -0
  82. data/lib/active_merchant/billing/gateways/paystation.rb +201 -0
  83. data/lib/active_merchant/billing/gateways/plugnpay.rb +298 -0
  84. data/lib/active_merchant/billing/gateways/psigate.rb +219 -0
  85. data/lib/active_merchant/billing/gateways/psl_card.rb +304 -0
  86. data/lib/active_merchant/billing/gateways/qbms.rb +297 -0
  87. data/lib/active_merchant/billing/gateways/quantum.rb +282 -0
  88. data/lib/active_merchant/billing/gateways/quickpay.rb +297 -0
  89. data/lib/active_merchant/billing/gateways/realex.rb +311 -0
  90. data/lib/active_merchant/billing/gateways/sage.rb +146 -0
  91. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +88 -0
  92. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +116 -0
  93. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
  94. data/lib/active_merchant/billing/gateways/sage_pay.rb +320 -0
  95. data/lib/active_merchant/billing/gateways/sallie_mae.rb +144 -0
  96. data/lib/active_merchant/billing/gateways/secure_net.rb +330 -0
  97. data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
  98. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +193 -0
  99. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +113 -0
  100. data/lib/active_merchant/billing/gateways/skip_jack.rb +453 -0
  101. data/lib/active_merchant/billing/gateways/smart_ps.rb +271 -0
  102. data/lib/active_merchant/billing/gateways/stripe.rb +212 -0
  103. data/lib/active_merchant/billing/gateways/trans_first.rb +127 -0
  104. data/lib/active_merchant/billing/gateways/transax.rb +25 -0
  105. data/lib/active_merchant/billing/gateways/trust_commerce.rb +423 -0
  106. data/lib/active_merchant/billing/gateways/usa_epay.rb +194 -0
  107. data/lib/active_merchant/billing/gateways/verifi.rb +233 -0
  108. data/lib/active_merchant/billing/gateways/viaklix.rb +189 -0
  109. data/lib/active_merchant/billing/gateways/wirecard.rb +318 -0
  110. data/lib/active_merchant/billing/gateways/worldpay.rb +280 -0
  111. data/lib/active_merchant/billing/integrations.rb +17 -0
  112. data/lib/active_merchant/billing/integrations/action_view_helper.rb +68 -0
  113. data/lib/active_merchant/billing/integrations/bogus.rb +23 -0
  114. data/lib/active_merchant/billing/integrations/bogus/helper.rb +17 -0
  115. data/lib/active_merchant/billing/integrations/bogus/notification.rb +11 -0
  116. data/lib/active_merchant/billing/integrations/bogus/return.rb +10 -0
  117. data/lib/active_merchant/billing/integrations/chronopay.rb +23 -0
  118. data/lib/active_merchant/billing/integrations/chronopay/helper.rb +120 -0
  119. data/lib/active_merchant/billing/integrations/chronopay/notification.rb +158 -0
  120. data/lib/active_merchant/billing/integrations/chronopay/return.rb +10 -0
  121. data/lib/active_merchant/billing/integrations/direc_pay.rb +41 -0
  122. data/lib/active_merchant/billing/integrations/direc_pay/helper.rb +200 -0
  123. data/lib/active_merchant/billing/integrations/direc_pay/notification.rb +76 -0
  124. data/lib/active_merchant/billing/integrations/direc_pay/return.rb +32 -0
  125. data/lib/active_merchant/billing/integrations/direc_pay/status.rb +37 -0
  126. data/lib/active_merchant/billing/integrations/directebanking.rb +47 -0
  127. data/lib/active_merchant/billing/integrations/directebanking/helper.rb +90 -0
  128. data/lib/active_merchant/billing/integrations/directebanking/notification.rb +120 -0
  129. data/lib/active_merchant/billing/integrations/directebanking/return.rb +11 -0
  130. data/lib/active_merchant/billing/integrations/dwolla.rb +30 -0
  131. data/lib/active_merchant/billing/integrations/dwolla/helper.rb +28 -0
  132. data/lib/active_merchant/billing/integrations/dwolla/notification.rb +50 -0
  133. data/lib/active_merchant/billing/integrations/dwolla/return.rb +38 -0
  134. data/lib/active_merchant/billing/integrations/e_payment_plans.rb +48 -0
  135. data/lib/active_merchant/billing/integrations/e_payment_plans/helper.rb +34 -0
  136. data/lib/active_merchant/billing/integrations/e_payment_plans/notification.rb +84 -0
  137. data/lib/active_merchant/billing/integrations/gestpay.rb +25 -0
  138. data/lib/active_merchant/billing/integrations/gestpay/common.rb +42 -0
  139. data/lib/active_merchant/billing/integrations/gestpay/helper.rb +70 -0
  140. data/lib/active_merchant/billing/integrations/gestpay/notification.rb +85 -0
  141. data/lib/active_merchant/billing/integrations/gestpay/return.rb +10 -0
  142. data/lib/active_merchant/billing/integrations/helper.rb +96 -0
  143. data/lib/active_merchant/billing/integrations/hi_trust.rb +27 -0
  144. data/lib/active_merchant/billing/integrations/hi_trust/helper.rb +58 -0
  145. data/lib/active_merchant/billing/integrations/hi_trust/notification.rb +59 -0
  146. data/lib/active_merchant/billing/integrations/hi_trust/return.rb +67 -0
  147. data/lib/active_merchant/billing/integrations/moneybookers.rb +26 -0
  148. data/lib/active_merchant/billing/integrations/moneybookers/helper.rb +59 -0
  149. data/lib/active_merchant/billing/integrations/moneybookers/notification.rb +129 -0
  150. data/lib/active_merchant/billing/integrations/nochex.rb +88 -0
  151. data/lib/active_merchant/billing/integrations/nochex/helper.rb +68 -0
  152. data/lib/active_merchant/billing/integrations/nochex/notification.rb +94 -0
  153. data/lib/active_merchant/billing/integrations/nochex/return.rb +10 -0
  154. data/lib/active_merchant/billing/integrations/notification.rb +62 -0
  155. data/lib/active_merchant/billing/integrations/payflow_link.rb +21 -0
  156. data/lib/active_merchant/billing/integrations/payflow_link/helper.rb +58 -0
  157. data/lib/active_merchant/billing/integrations/payflow_link/notification.rb +78 -0
  158. data/lib/active_merchant/billing/integrations/paypal.rb +39 -0
  159. data/lib/active_merchant/billing/integrations/paypal/helper.rb +119 -0
  160. data/lib/active_merchant/billing/integrations/paypal/notification.rb +154 -0
  161. data/lib/active_merchant/billing/integrations/paypal/return.rb +10 -0
  162. data/lib/active_merchant/billing/integrations/quickpay.rb +21 -0
  163. data/lib/active_merchant/billing/integrations/quickpay/helper.rb +72 -0
  164. data/lib/active_merchant/billing/integrations/quickpay/notification.rb +74 -0
  165. data/lib/active_merchant/billing/integrations/return.rb +42 -0
  166. data/lib/active_merchant/billing/integrations/sage_pay_form.rb +37 -0
  167. data/lib/active_merchant/billing/integrations/sage_pay_form/encryption.rb +33 -0
  168. data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +111 -0
  169. data/lib/active_merchant/billing/integrations/sage_pay_form/notification.rb +210 -0
  170. data/lib/active_merchant/billing/integrations/sage_pay_form/return.rb +31 -0
  171. data/lib/active_merchant/billing/integrations/two_checkout.rb +23 -0
  172. data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +59 -0
  173. data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +114 -0
  174. data/lib/active_merchant/billing/integrations/two_checkout/return.rb +17 -0
  175. data/lib/active_merchant/billing/integrations/valitor.rb +33 -0
  176. data/lib/active_merchant/billing/integrations/valitor/helper.rb +86 -0
  177. data/lib/active_merchant/billing/integrations/valitor/notification.rb +13 -0
  178. data/lib/active_merchant/billing/integrations/valitor/response_fields.rb +97 -0
  179. data/lib/active_merchant/billing/integrations/valitor/return.rb +13 -0
  180. data/lib/active_merchant/billing/integrations/world_pay.rb +27 -0
  181. data/lib/active_merchant/billing/integrations/world_pay/helper.rb +100 -0
  182. data/lib/active_merchant/billing/integrations/world_pay/notification.rb +160 -0
  183. data/lib/active_merchant/billing/response.rb +32 -0
  184. data/lib/active_merchant/version.rb +3 -0
  185. data/lib/activemerchant.rb +1 -0
  186. data/lib/support/gateway_support.rb +58 -0
  187. data/lib/support/outbound_hosts.rb +25 -0
  188. metadata +335 -0
@@ -0,0 +1,88 @@
1
+ require File.dirname(__FILE__) + '/sage_core'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class SageBankcardGateway < Gateway #:nodoc:
6
+ include SageCore
7
+ self.url = 'https://www.sagepayments.net/cgi-bin/eftBankcard.dll?transaction'
8
+ self.source = 'bankcard'
9
+
10
+ # Credit cards supported by Sage
11
+ # * VISA
12
+ # * MasterCard
13
+ # * AMEX
14
+ # * Diners
15
+ # * Carte Blanche
16
+ # * Discover
17
+ # * JCB
18
+ # * Sears
19
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club]
20
+
21
+ def authorize(money, credit_card, options = {})
22
+ post = {}
23
+ add_credit_card(post, credit_card)
24
+ add_transaction_data(post, money, options)
25
+ commit(:authorization, post)
26
+ end
27
+
28
+ def purchase(money, credit_card, options = {})
29
+ post = {}
30
+ add_credit_card(post, credit_card)
31
+ add_transaction_data(post, money, options)
32
+ commit(:purchase, post)
33
+ end
34
+
35
+ # The +money+ amount is not used. The entire amount of the
36
+ # initial authorization will be captured.
37
+ def capture(money, reference, options = {})
38
+ post = {}
39
+ add_reference(post, reference)
40
+ commit(:capture, post)
41
+ end
42
+
43
+ def void(reference, options = {})
44
+ post = {}
45
+ add_reference(post, reference)
46
+ commit(:void, post)
47
+ end
48
+
49
+ def credit(money, credit_card, options = {})
50
+ post = {}
51
+ add_credit_card(post, credit_card)
52
+ add_transaction_data(post, money, options)
53
+ commit(:credit, post)
54
+ end
55
+
56
+ private
57
+ def exp_date(credit_card)
58
+ year = sprintf("%.4i", credit_card.year)
59
+ month = sprintf("%.2i", credit_card.month)
60
+
61
+ "#{month}#{year[-2..-1]}"
62
+ end
63
+
64
+ def add_credit_card(post, credit_card)
65
+ post[:C_name] = credit_card.name
66
+ post[:C_cardnumber] = credit_card.number
67
+ post[:C_exp] = exp_date(credit_card)
68
+ post[:C_cvv] = credit_card.verification_value if credit_card.verification_value?
69
+ end
70
+
71
+ def parse(data)
72
+ response = {}
73
+ response[:success] = data[1,1]
74
+ response[:code] = data[2,6]
75
+ response[:message] = data[8,32].strip
76
+ response[:front_end] = data[40, 2]
77
+ response[:cvv_result] = data[42, 1]
78
+ response[:avs_result] = data[43, 1].strip
79
+ response[:risk] = data[44, 2]
80
+ response[:reference] = data[46, 10]
81
+
82
+ response[:order_number], response[:recurring] = data[57...-1].split("\034")
83
+ response
84
+ end
85
+ end
86
+ end
87
+ end
88
+
@@ -0,0 +1,116 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module SageCore #:nodoc:
4
+ def self.included(base)
5
+ base.cattr_accessor :url
6
+ base.cattr_accessor :source
7
+ base.supported_countries = ['US', 'CA']
8
+ base.homepage_url = 'http://www.sagepayments.com'
9
+ base.display_name = 'Sage Payment Solutions'
10
+ end
11
+
12
+ # Transactions types:
13
+ # <tt>01</tt> - Sale
14
+ # <tt>02</tt> - AuthOnly
15
+ # <tt>03</tt> - Force/PriorAuthSale
16
+ # <tt>04</tt> - Void
17
+ # <tt>06</tt> - Credit
18
+ # <tt>11</tt> - PriorAuthSale by Reference*
19
+ TRANSACTIONS = {
20
+ :purchase => '01',
21
+ :authorization => '02',
22
+ :capture => '11',
23
+ :void => '04',
24
+ :credit => '06'
25
+ }
26
+
27
+ def initialize(options = {})
28
+ requires!(options, :login, :password)
29
+ @options = options
30
+ super
31
+ end
32
+
33
+ private
34
+ def add_invoice(post, options)
35
+ post[:T_ordernum] = options[:order_id].slice(0, 20)
36
+ post[:T_tax] = amount(options[:tax]) unless options[:tax].blank?
37
+ post[:T_shipping] = amount(options[:shipping]) unless options[:shipping].blank?
38
+ end
39
+
40
+ def add_reference(post, reference)
41
+ ref, source = reference.to_s.split(";")
42
+ post[:T_reference] = ref
43
+ end
44
+
45
+ def add_amount(post, money)
46
+ post[:T_amt] = amount(money)
47
+ end
48
+
49
+ def add_customer_data(post, options)
50
+ post[:T_customer_number] = options[:customer]
51
+ end
52
+
53
+ def add_addresses(post, options)
54
+ billing_address = options[:billing_address] || options[:address] || {}
55
+
56
+ post[:C_address] = billing_address[:address1]
57
+ post[:C_city] = billing_address[:city]
58
+
59
+ if ['US', 'CA'].include?(billing_address[:country])
60
+ post[:C_state] = billing_address[:state]
61
+ else
62
+ post[:C_state] = "Outside of United States"
63
+ end
64
+
65
+ post[:C_zip] = billing_address[:zip]
66
+ post[:C_country] = billing_address[:country]
67
+ post[:C_telephone] = billing_address[:phone]
68
+ post[:C_fax] = billing_address[:fax]
69
+ post[:C_email] = options[:email]
70
+
71
+ if shipping_address = options[:shipping_address]
72
+ post[:C_ship_name] = shipping_address[:name]
73
+ post[:C_ship_address] = shipping_address[:address1]
74
+ post[:C_ship_city] = shipping_address[:city]
75
+ post[:C_ship_state] = shipping_address[:state]
76
+ post[:C_ship_zip] = shipping_address[:zip]
77
+ post[:C_ship_country] = shipping_address[:country]
78
+ end
79
+ end
80
+
81
+ def add_transaction_data(post, money, options)
82
+ add_amount(post, money)
83
+ add_invoice(post, options)
84
+ add_addresses(post, options)
85
+ add_customer_data(post, options)
86
+ end
87
+
88
+ def commit(action, params)
89
+ response = parse(ssl_post(url, post_data(action, params)))
90
+
91
+ Response.new(success?(response), response[:message], response,
92
+ :test => test?,
93
+ :authorization => authorization_from(response),
94
+ :avs_result => { :code => response[:avs_result] },
95
+ :cvv_result => response[:cvv_result]
96
+ )
97
+ end
98
+
99
+ def authorization_from(response)
100
+ "#{response[:reference]};#{source}"
101
+ end
102
+
103
+ def success?(response)
104
+ response[:success] == 'A'
105
+ end
106
+
107
+ def post_data(action, params = {})
108
+ params[:M_id] = @options[:login]
109
+ params[:M_key] = @options[:password]
110
+ params[:T_code] = TRANSACTIONS[action]
111
+
112
+ params.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
113
+ end
114
+ end
115
+ end
116
+ end
@@ -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,320 @@
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
+ # Refunding requires a new order_id to passed in, as well as a description
109
+ def refund(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
+ def credit(money, identification, options = {})
122
+ deprecated CREDIT_DEPRECATION_MESSAGE
123
+ refund(money, identification, options)
124
+ end
125
+
126
+ private
127
+ def add_reference(post, identification)
128
+ order_id, transaction_id, authorization, security_key = identification.split(';')
129
+
130
+ add_pair(post, :VendorTxCode, order_id)
131
+ add_pair(post, :VPSTxId, transaction_id)
132
+ add_pair(post, :TxAuthNo, authorization)
133
+ add_pair(post, :SecurityKey, security_key)
134
+ end
135
+
136
+ def add_credit_reference(post, identification)
137
+ order_id, transaction_id, authorization, security_key = identification.split(';')
138
+
139
+ add_pair(post, :RelatedVendorTxCode, order_id)
140
+ add_pair(post, :RelatedVPSTxId, transaction_id)
141
+ add_pair(post, :RelatedTxAuthNo, authorization)
142
+ add_pair(post, :RelatedSecurityKey, security_key)
143
+ end
144
+
145
+ def add_amount(post, money, options)
146
+ currency = options[:currency] || currency(money)
147
+ add_pair(post, :Amount, localized_amount(money, currency), :required => true)
148
+ add_pair(post, :Currency, currency, :required => true)
149
+ end
150
+
151
+ # doesn't actually use the currency -- dodgy!
152
+ def add_release_amount(post, money, options)
153
+ add_pair(post, :ReleaseAmount, amount(money), :required => true)
154
+ end
155
+
156
+ def add_customer_data(post, options)
157
+ add_pair(post, :CustomerEMail, options[:email][0,255]) unless options[:email].blank?
158
+ add_pair(post, :BillingPhone, options[:phone].gsub(/[^0-9+]/, '')[0,20]) unless options[:phone].blank?
159
+ add_pair(post, :ClientIPAddress, options[:ip])
160
+ end
161
+
162
+ def add_address(post, options)
163
+ if billing_address = options[:billing_address] || options[:address]
164
+ first_name, last_name = parse_first_and_last_name(billing_address[:name])
165
+ add_pair(post, :BillingSurname, last_name)
166
+ add_pair(post, :BillingFirstnames, first_name)
167
+ add_pair(post, :BillingAddress1, billing_address[:address1])
168
+ add_pair(post, :BillingAddress2, billing_address[:address2])
169
+ add_pair(post, :BillingCity, billing_address[:city])
170
+ add_pair(post, :BillingState, billing_address[:state]) if billing_address[:country] == 'US'
171
+ add_pair(post, :BillingCountry, billing_address[:country])
172
+ add_pair(post, :BillingPostCode, billing_address[:zip])
173
+ end
174
+
175
+ if shipping_address = options[:shipping_address] || billing_address
176
+ first_name, last_name = parse_first_and_last_name(shipping_address[:name])
177
+ add_pair(post, :DeliverySurname, last_name)
178
+ add_pair(post, :DeliveryFirstnames, first_name)
179
+ add_pair(post, :DeliveryAddress1, shipping_address[:address1])
180
+ add_pair(post, :DeliveryAddress2, shipping_address[:address2])
181
+ add_pair(post, :DeliveryCity, shipping_address[:city])
182
+ add_pair(post, :DeliveryState, shipping_address[:state]) if shipping_address[:country] == 'US'
183
+ add_pair(post, :DeliveryCountry, shipping_address[:country])
184
+ add_pair(post, :DeliveryPostCode, shipping_address[:zip])
185
+ end
186
+ end
187
+
188
+ def add_invoice(post, options)
189
+ add_pair(post, :VendorTxCode, sanitize_order_id(options[:order_id]), :required => true)
190
+ add_pair(post, :Description, options[:description] || options[:order_id])
191
+ end
192
+
193
+ def add_credit_card(post, credit_card)
194
+ add_pair(post, :CardHolder, credit_card.name, :required => true)
195
+ add_pair(post, :CardNumber, credit_card.number, :required => true)
196
+
197
+ add_pair(post, :ExpiryDate, format_date(credit_card.month, credit_card.year), :required => true)
198
+
199
+ if requires_start_date_or_issue_number?(credit_card)
200
+ add_pair(post, :StartDate, format_date(credit_card.start_month, credit_card.start_year))
201
+ add_pair(post, :IssueNumber, credit_card.issue_number)
202
+ end
203
+ add_pair(post, :CardType, map_card_type(credit_card))
204
+
205
+ add_pair(post, :CV2, credit_card.verification_value)
206
+ end
207
+
208
+ def sanitize_order_id(order_id)
209
+ order_id.to_s.gsub(/[^-a-zA-Z0-9._]/, '')
210
+ end
211
+
212
+ def map_card_type(credit_card)
213
+ raise ArgumentError, "The credit card type must be provided" if card_brand(credit_card).blank?
214
+
215
+ card_type = card_brand(credit_card).to_sym
216
+
217
+ # Check if it is an electron card
218
+ if card_type == :visa && credit_card.number =~ ELECTRON
219
+ CREDIT_CARDS[:electron]
220
+ else
221
+ CREDIT_CARDS[card_type]
222
+ end
223
+ end
224
+
225
+ # MMYY format
226
+ def format_date(month, year)
227
+ return nil if year.blank? || month.blank?
228
+
229
+ year = sprintf("%.4i", year)
230
+ month = sprintf("%.2i", month)
231
+
232
+ "#{month}#{year[-2..-1]}"
233
+ end
234
+
235
+ def commit(action, parameters)
236
+ response = parse( ssl_post(url_for(action), post_data(action, parameters)) )
237
+
238
+ Response.new(response["Status"] == APPROVED, message_from(response), response,
239
+ :test => test?,
240
+ :authorization => authorization_from(response, parameters, action),
241
+ :avs_result => {
242
+ :street_match => AVS_CVV_CODE[ response["AddressResult"] ],
243
+ :postal_match => AVS_CVV_CODE[ response["PostCodeResult"] ],
244
+ },
245
+ :cvv_result => AVS_CVV_CODE[ response["CV2Result"] ]
246
+ )
247
+ end
248
+
249
+ def authorization_from(response, params, action)
250
+ [ params[:VendorTxCode],
251
+ response["VPSTxId"],
252
+ response["TxAuthNo"],
253
+ response["SecurityKey"],
254
+ action ].join(";")
255
+ end
256
+
257
+ def abort_or_void_from(identification)
258
+ original_transaction = identification.split(';').last
259
+ original_transaction == 'authorization' ? :abort : :void
260
+ end
261
+
262
+ def url_for(action)
263
+ simulate ? build_simulator_url(action) : build_url(action)
264
+ end
265
+
266
+ def build_url(action)
267
+ endpoint = [ :purchase, :authorization ].include?(action) ? "vspdirect-register" : TRANSACTIONS[action].downcase
268
+ "#{test? ? TEST_URL : LIVE_URL}/#{endpoint}.vsp"
269
+ end
270
+
271
+ def build_simulator_url(action)
272
+ endpoint = [ :purchase, :authorization ].include?(action) ? "VSPDirectGateway.asp" : "VSPServerGateway.asp?Service=Vendor#{TRANSACTIONS[action].capitalize}Tx"
273
+ "#{SIMULATOR_URL}/#{endpoint}"
274
+ end
275
+
276
+ def message_from(response)
277
+ response['Status'] == APPROVED ? 'Success' : (response['StatusDetail'] || 'Unspecified error') # simonr 20080207 can't actually get non-nil blanks, so this is shorter
278
+ end
279
+
280
+ def post_data(action, parameters = {})
281
+ parameters.update(
282
+ :Vendor => @options[:login],
283
+ :TxType => TRANSACTIONS[action],
284
+ :VPSProtocol => "2.23"
285
+ )
286
+
287
+ parameters.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
288
+ end
289
+
290
+ # SagePay returns data in the following format
291
+ # Key1=value1
292
+ # Key2=value2
293
+ def parse(body)
294
+ result = {}
295
+ body.to_s.each_line do |pair|
296
+ result[$1] = $2 if pair.strip =~ /\A([^=]+)=(.+)\Z/im
297
+ end
298
+ result
299
+ end
300
+
301
+ def add_pair(post, key, value, options = {})
302
+ post[key] = value if !value.blank? || options[:required]
303
+ end
304
+
305
+ def parse_first_and_last_name(value)
306
+ name = value.to_s.split(' ')
307
+
308
+ last_name = name.pop || ''
309
+ first_name = name.join(' ')
310
+ [ first_name[0,20], last_name[0,20] ]
311
+ end
312
+
313
+ def localized_amount(money, currency)
314
+ amount = amount(money)
315
+ CURRENCIES_WITHOUT_FRACTIONS.include?(currency.to_s) ? amount.split('.').first : amount
316
+ end
317
+ end
318
+ end
319
+ end
320
+