mattbauer-activemerchant 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (292) hide show
  1. data/CHANGELOG +459 -0
  2. data/CONTRIBUTERS +118 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README +134 -0
  5. data/Rakefile +153 -0
  6. data/gem-public_cert.pem +20 -0
  7. data/init.rb +3 -0
  8. data/lib/active_merchant.rb +60 -0
  9. data/lib/active_merchant/billing/avs_result.rb +98 -0
  10. data/lib/active_merchant/billing/base.rb +57 -0
  11. data/lib/active_merchant/billing/check.rb +68 -0
  12. data/lib/active_merchant/billing/credit_card.rb +159 -0
  13. data/lib/active_merchant/billing/credit_card_formatting.rb +21 -0
  14. data/lib/active_merchant/billing/credit_card_methods.rb +125 -0
  15. data/lib/active_merchant/billing/cvv_result.rb +38 -0
  16. data/lib/active_merchant/billing/expiry_date.rb +34 -0
  17. data/lib/active_merchant/billing/gateway.rb +158 -0
  18. data/lib/active_merchant/billing/gateways.rb +3 -0
  19. data/lib/active_merchant/billing/gateways/authorize_net.rb +657 -0
  20. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +703 -0
  21. data/lib/active_merchant/billing/gateways/beanstream.rb +102 -0
  22. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +233 -0
  23. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
  24. data/lib/active_merchant/billing/gateways/bogus.rb +98 -0
  25. data/lib/active_merchant/billing/gateways/braintree.rb +17 -0
  26. data/lib/active_merchant/billing/gateways/card_stream.rb +230 -0
  27. data/lib/active_merchant/billing/gateways/cyber_source.rb +406 -0
  28. data/lib/active_merchant/billing/gateways/data_cash.rb +595 -0
  29. data/lib/active_merchant/billing/gateways/efsnet.rb +229 -0
  30. data/lib/active_merchant/billing/gateways/elavon.rb +106 -0
  31. data/lib/active_merchant/billing/gateways/eway.rb +277 -0
  32. data/lib/active_merchant/billing/gateways/exact.rb +222 -0
  33. data/lib/active_merchant/billing/gateways/first_pay.rb +172 -0
  34. data/lib/active_merchant/billing/gateways/instapay.rb +164 -0
  35. data/lib/active_merchant/billing/gateways/linkpoint.rb +396 -0
  36. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +154 -0
  37. data/lib/active_merchant/billing/gateways/merchant_ware.rb +283 -0
  38. data/lib/active_merchant/billing/gateways/modern_payments.rb +36 -0
  39. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +214 -0
  40. data/lib/active_merchant/billing/gateways/moneris.rb +295 -0
  41. data/lib/active_merchant/billing/gateways/moneris_usa.rb +258 -0
  42. data/lib/active_merchant/billing/gateways/net_registry.rb +189 -0
  43. data/lib/active_merchant/billing/gateways/netbilling.rb +168 -0
  44. data/lib/active_merchant/billing/gateways/ogone.rb +259 -0
  45. data/lib/active_merchant/billing/gateways/pay_junction.rb +392 -0
  46. data/lib/active_merchant/billing/gateways/pay_secure.rb +120 -0
  47. data/lib/active_merchant/billing/gateways/payflow.rb +236 -0
  48. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +207 -0
  49. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
  50. data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
  51. data/lib/active_merchant/billing/gateways/payflow_express.rb +138 -0
  52. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
  53. data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
  54. data/lib/active_merchant/billing/gateways/payment_express.rb +230 -0
  55. data/lib/active_merchant/billing/gateways/paypal.rb +121 -0
  56. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +325 -0
  57. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +38 -0
  58. data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
  59. data/lib/active_merchant/billing/gateways/paypal_express.rb +130 -0
  60. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +20 -0
  61. data/lib/active_merchant/billing/gateways/plugnpay.rb +292 -0
  62. data/lib/active_merchant/billing/gateways/psigate.rb +214 -0
  63. data/lib/active_merchant/billing/gateways/psl_card.rb +306 -0
  64. data/lib/active_merchant/billing/gateways/quickpay.rb +213 -0
  65. data/lib/active_merchant/billing/gateways/realex.rb +200 -0
  66. data/lib/active_merchant/billing/gateways/sage.rb +146 -0
  67. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +88 -0
  68. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +110 -0
  69. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
  70. data/lib/active_merchant/billing/gateways/sage_pay.rb +308 -0
  71. data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
  72. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +157 -0
  73. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +113 -0
  74. data/lib/active_merchant/billing/gateways/skip_jack.rb +442 -0
  75. data/lib/active_merchant/billing/gateways/smart_ps.rb +254 -0
  76. data/lib/active_merchant/billing/gateways/trans_first.rb +127 -0
  77. data/lib/active_merchant/billing/gateways/transax.rb +26 -0
  78. data/lib/active_merchant/billing/gateways/trust_commerce.rb +418 -0
  79. data/lib/active_merchant/billing/gateways/usa_epay.rb +194 -0
  80. data/lib/active_merchant/billing/gateways/verifi.rb +228 -0
  81. data/lib/active_merchant/billing/gateways/viaklix.rb +189 -0
  82. data/lib/active_merchant/billing/gateways/wirecard.rb +318 -0
  83. data/lib/active_merchant/billing/integrations.rb +22 -0
  84. data/lib/active_merchant/billing/integrations/action_view_helper.rb +79 -0
  85. data/lib/active_merchant/billing/integrations/bogus.rb +22 -0
  86. data/lib/active_merchant/billing/integrations/bogus/helper.rb +17 -0
  87. data/lib/active_merchant/billing/integrations/bogus/notification.rb +11 -0
  88. data/lib/active_merchant/billing/integrations/bogus/return.rb +10 -0
  89. data/lib/active_merchant/billing/integrations/chronopay.rb +22 -0
  90. data/lib/active_merchant/billing/integrations/chronopay/helper.rb +81 -0
  91. data/lib/active_merchant/billing/integrations/chronopay/notification.rb +156 -0
  92. data/lib/active_merchant/billing/integrations/chronopay/return.rb +10 -0
  93. data/lib/active_merchant/billing/integrations/gestpay.rb +26 -0
  94. data/lib/active_merchant/billing/integrations/gestpay/common.rb +42 -0
  95. data/lib/active_merchant/billing/integrations/gestpay/helper.rb +70 -0
  96. data/lib/active_merchant/billing/integrations/gestpay/notification.rb +83 -0
  97. data/lib/active_merchant/billing/integrations/gestpay/return.rb +10 -0
  98. data/lib/active_merchant/billing/integrations/helper.rb +93 -0
  99. data/lib/active_merchant/billing/integrations/hi_trust.rb +26 -0
  100. data/lib/active_merchant/billing/integrations/hi_trust/helper.rb +58 -0
  101. data/lib/active_merchant/billing/integrations/hi_trust/notification.rb +59 -0
  102. data/lib/active_merchant/billing/integrations/hi_trust/return.rb +67 -0
  103. data/lib/active_merchant/billing/integrations/nochex.rb +87 -0
  104. data/lib/active_merchant/billing/integrations/nochex/helper.rb +68 -0
  105. data/lib/active_merchant/billing/integrations/nochex/notification.rb +94 -0
  106. data/lib/active_merchant/billing/integrations/nochex/return.rb +10 -0
  107. data/lib/active_merchant/billing/integrations/notification.rb +62 -0
  108. data/lib/active_merchant/billing/integrations/paypal.rb +40 -0
  109. data/lib/active_merchant/billing/integrations/paypal/helper.rb +119 -0
  110. data/lib/active_merchant/billing/integrations/paypal/notification.rb +154 -0
  111. data/lib/active_merchant/billing/integrations/paypal/return.rb +10 -0
  112. data/lib/active_merchant/billing/integrations/quickpay.rb +18 -0
  113. data/lib/active_merchant/billing/integrations/quickpay/helper.rb +72 -0
  114. data/lib/active_merchant/billing/integrations/quickpay/notification.rb +74 -0
  115. data/lib/active_merchant/billing/integrations/return.rb +35 -0
  116. data/lib/active_merchant/billing/integrations/two_checkout.rb +23 -0
  117. data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +59 -0
  118. data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +114 -0
  119. data/lib/active_merchant/billing/integrations/two_checkout/return.rb +17 -0
  120. data/lib/active_merchant/billing/response.rb +32 -0
  121. data/lib/active_merchant/lib/connection.rb +170 -0
  122. data/lib/active_merchant/lib/country.rb +319 -0
  123. data/lib/active_merchant/lib/error.rb +4 -0
  124. data/lib/active_merchant/lib/post_data.rb +22 -0
  125. data/lib/active_merchant/lib/posts_data.rb +47 -0
  126. data/lib/active_merchant/lib/requires_parameters.rb +16 -0
  127. data/lib/active_merchant/lib/utils.rb +18 -0
  128. data/lib/active_merchant/lib/validateable.rb +76 -0
  129. data/lib/certs/cacert.pem +7815 -0
  130. data/lib/support/gateway_support.rb +58 -0
  131. data/script/destroy +14 -0
  132. data/script/generate +14 -0
  133. data/test/fixtures.yml +353 -0
  134. data/test/remote/gateways/remote_authorize_net_cim_test.rb +459 -0
  135. data/test/remote/gateways/remote_authorize_net_test.rb +145 -0
  136. data/test/remote/gateways/remote_beanstream_interac_test.rb +53 -0
  137. data/test/remote/gateways/remote_beanstream_test.rb +150 -0
  138. data/test/remote/gateways/remote_braintree_test.rb +154 -0
  139. data/test/remote/gateways/remote_card_stream_test.rb +148 -0
  140. data/test/remote/gateways/remote_cyber_source_test.rb +144 -0
  141. data/test/remote/gateways/remote_data_cash_test.rb +357 -0
  142. data/test/remote/gateways/remote_efsnet_test.rb +81 -0
  143. data/test/remote/gateways/remote_elavon_test.rb +66 -0
  144. data/test/remote/gateways/remote_eway_test.rb +74 -0
  145. data/test/remote/gateways/remote_exact_test.rb +60 -0
  146. data/test/remote/gateways/remote_first_pay_test.rb +87 -0
  147. data/test/remote/gateways/remote_instapay_test.rb +61 -0
  148. data/test/remote/gateways/remote_linkpoint_test.rb +112 -0
  149. data/test/remote/gateways/remote_merchant_e_solutions_test.rb +173 -0
  150. data/test/remote/gateways/remote_merchant_ware_test.rb +113 -0
  151. data/test/remote/gateways/remote_modern_payments_cim_test.rb +58 -0
  152. data/test/remote/gateways/remote_modern_payments_test.rb +43 -0
  153. data/test/remote/gateways/remote_moneris_test.rb +118 -0
  154. data/test/remote/gateways/remote_moneris_usa_test.rb +115 -0
  155. data/test/remote/gateways/remote_net_registry_test.rb +85 -0
  156. data/test/remote/gateways/remote_netbilling_test.rb +70 -0
  157. data/test/remote/gateways/remote_ogone_test.rb +108 -0
  158. data/test/remote/gateways/remote_pay_junction_test.rb +143 -0
  159. data/test/remote/gateways/remote_pay_secure_test.rb +39 -0
  160. data/test/remote/gateways/remote_payflow_express_test.rb +50 -0
  161. data/test/remote/gateways/remote_payflow_test.rb +237 -0
  162. data/test/remote/gateways/remote_payflow_uk_test.rb +173 -0
  163. data/test/remote/gateways/remote_payment_express_test.rb +136 -0
  164. data/test/remote/gateways/remote_paypal_express_test.rb +49 -0
  165. data/test/remote/gateways/remote_paypal_test.rb +178 -0
  166. data/test/remote/gateways/remote_plugnpay_test.rb +72 -0
  167. data/test/remote/gateways/remote_psigate_test.rb +50 -0
  168. data/test/remote/gateways/remote_psl_card_test.rb +125 -0
  169. data/test/remote/gateways/remote_quickpay_test.rb +190 -0
  170. data/test/remote/gateways/remote_realex_test.rb +224 -0
  171. data/test/remote/gateways/remote_sage_bankcard_test.rb +109 -0
  172. data/test/remote/gateways/remote_sage_pay_test.rb +219 -0
  173. data/test/remote/gateways/remote_sage_test.rb +87 -0
  174. data/test/remote/gateways/remote_sage_virtual_check_test.rb +62 -0
  175. data/test/remote/gateways/remote_secure_pay_au_test.rb +40 -0
  176. data/test/remote/gateways/remote_secure_pay_tech_test.rb +37 -0
  177. data/test/remote/gateways/remote_secure_pay_test.rb +28 -0
  178. data/test/remote/gateways/remote_skipjack_test.rb +105 -0
  179. data/test/remote/gateways/remote_trans_first_test.rb +34 -0
  180. data/test/remote/gateways/remote_transax_test.rb +112 -0
  181. data/test/remote/gateways/remote_trust_commerce_test.rb +152 -0
  182. data/test/remote/gateways/remote_usa_epay_test.rb +46 -0
  183. data/test/remote/gateways/remote_verifi_test.rb +107 -0
  184. data/test/remote/gateways/remote_viaklix_test.rb +43 -0
  185. data/test/remote/gateways/remote_wirecard_test.rb +111 -0
  186. data/test/remote/integrations/remote_gestpay_integration_test.rb +37 -0
  187. data/test/remote/integrations/remote_paypal_integration_test.rb +26 -0
  188. data/test/test_helper.rb +182 -0
  189. data/test/unit/avs_result_test.rb +59 -0
  190. data/test/unit/base_test.rb +55 -0
  191. data/test/unit/check_test.rb +88 -0
  192. data/test/unit/connection_test.rb +129 -0
  193. data/test/unit/country_code_test.rb +33 -0
  194. data/test/unit/country_test.rb +64 -0
  195. data/test/unit/credit_card_formatting_test.rb +19 -0
  196. data/test/unit/credit_card_methods_test.rb +179 -0
  197. data/test/unit/credit_card_test.rb +318 -0
  198. data/test/unit/cvv_result_test.rb +33 -0
  199. data/test/unit/expiry_date_test.rb +32 -0
  200. data/test/unit/gateways/authorize_net_cim_test.rb +638 -0
  201. data/test/unit/gateways/authorize_net_test.rb +290 -0
  202. data/test/unit/gateways/beanstream_interac_test.rb +51 -0
  203. data/test/unit/gateways/beanstream_test.rb +108 -0
  204. data/test/unit/gateways/bogus_test.rb +46 -0
  205. data/test/unit/gateways/braintree_test.rb +126 -0
  206. data/test/unit/gateways/card_stream_test.rb +90 -0
  207. data/test/unit/gateways/cyber_source_test.rb +188 -0
  208. data/test/unit/gateways/data_cash_test.rb +133 -0
  209. data/test/unit/gateways/efsnet_test.rb +123 -0
  210. data/test/unit/gateways/elavon_test.rb +139 -0
  211. data/test/unit/gateways/eway_test.rb +118 -0
  212. data/test/unit/gateways/exact_test.rb +156 -0
  213. data/test/unit/gateways/first_pay_test.rb +125 -0
  214. data/test/unit/gateways/gateway_test.rb +48 -0
  215. data/test/unit/gateways/instapay_test.rb +102 -0
  216. data/test/unit/gateways/linkpoint_test.rb +167 -0
  217. data/test/unit/gateways/merchant_e_solutions_test.rb +169 -0
  218. data/test/unit/gateways/merchant_ware_test.rb +188 -0
  219. data/test/unit/gateways/modern_payments_cim_test.rb +171 -0
  220. data/test/unit/gateways/moneris_test.rb +185 -0
  221. data/test/unit/gateways/moneris_usa_test.rb +183 -0
  222. data/test/unit/gateways/net_registry_test.rb +416 -0
  223. data/test/unit/gateways/netbilling_test.rb +54 -0
  224. data/test/unit/gateways/ogone_test.rb +256 -0
  225. data/test/unit/gateways/pay_junction_test.rb +123 -0
  226. data/test/unit/gateways/pay_secure_test.rb +71 -0
  227. data/test/unit/gateways/payflow_express_test.rb +173 -0
  228. data/test/unit/gateways/payflow_express_uk_test.rb +86 -0
  229. data/test/unit/gateways/payflow_test.rb +305 -0
  230. data/test/unit/gateways/payflow_uk_test.rb +30 -0
  231. data/test/unit/gateways/payment_express_test.rb +195 -0
  232. data/test/unit/gateways/paypal_express_test.rb +382 -0
  233. data/test/unit/gateways/paypal_test.rb +569 -0
  234. data/test/unit/gateways/plugnpay_test.rb +86 -0
  235. data/test/unit/gateways/psigate_test.rb +169 -0
  236. data/test/unit/gateways/psl_card_test.rb +64 -0
  237. data/test/unit/gateways/quickpay_test.rb +112 -0
  238. data/test/unit/gateways/realex_test.rb +151 -0
  239. data/test/unit/gateways/sage_bankcard_test.rb +162 -0
  240. data/test/unit/gateways/sage_pay_test.rb +139 -0
  241. data/test/unit/gateways/sage_virtual_check_test.rb +71 -0
  242. data/test/unit/gateways/secure_pay_au_test.rb +207 -0
  243. data/test/unit/gateways/secure_pay_tech_test.rb +44 -0
  244. data/test/unit/gateways/secure_pay_test.rb +87 -0
  245. data/test/unit/gateways/skip_jack_test.rb +133 -0
  246. data/test/unit/gateways/trans_first_test.rb +112 -0
  247. data/test/unit/gateways/trust_commerce_test.rb +90 -0
  248. data/test/unit/gateways/usa_epay_test.rb +128 -0
  249. data/test/unit/gateways/verifi_test.rb +96 -0
  250. data/test/unit/gateways/viaklix_test.rb +78 -0
  251. data/test/unit/gateways/wirecard_test.rb +250 -0
  252. data/test/unit/generators/test_gateway_generator.rb +46 -0
  253. data/test/unit/generators/test_generator_helper.rb +20 -0
  254. data/test/unit/generators/test_integration_generator.rb +53 -0
  255. data/test/unit/integrations/action_view_helper_test.rb +50 -0
  256. data/test/unit/integrations/bogus_module_test.rb +20 -0
  257. data/test/unit/integrations/chronopay_module_test.rb +13 -0
  258. data/test/unit/integrations/gestpay_module_test.rb +14 -0
  259. data/test/unit/integrations/helpers/bogus_helper_test.rb +28 -0
  260. data/test/unit/integrations/helpers/chronopay_helper_test.rb +67 -0
  261. data/test/unit/integrations/helpers/gestpay_helper_test.rb +100 -0
  262. data/test/unit/integrations/helpers/hi_trust_helper_test.rb +16 -0
  263. data/test/unit/integrations/helpers/nochex_helper_test.rb +53 -0
  264. data/test/unit/integrations/helpers/paypal_helper_test.rb +171 -0
  265. data/test/unit/integrations/helpers/quickpay_helper_test.rb +40 -0
  266. data/test/unit/integrations/helpers/two_checkout_helper_test.rb +92 -0
  267. data/test/unit/integrations/hi_trust_module_test.rb +13 -0
  268. data/test/unit/integrations/nochex_module_test.rb +13 -0
  269. data/test/unit/integrations/notifications/chronopay_notification_test.rb +66 -0
  270. data/test/unit/integrations/notifications/gestpay_notification_test.rb +60 -0
  271. data/test/unit/integrations/notifications/hi_trust_notification_test.rb +59 -0
  272. data/test/unit/integrations/notifications/nochex_notification_test.rb +51 -0
  273. data/test/unit/integrations/notifications/notification_test.rb +54 -0
  274. data/test/unit/integrations/notifications/paypal_notification_test.rb +85 -0
  275. data/test/unit/integrations/notifications/quickpay_notification_test.rb +69 -0
  276. data/test/unit/integrations/notifications/two_checkout_notification_test.rb +55 -0
  277. data/test/unit/integrations/paypal_module_test.rb +28 -0
  278. data/test/unit/integrations/quickpay_module_test.rb +9 -0
  279. data/test/unit/integrations/returns/chronopay_return_test.rb +11 -0
  280. data/test/unit/integrations/returns/gestpay_return_test.rb +10 -0
  281. data/test/unit/integrations/returns/hi_trust_return_test.rb +24 -0
  282. data/test/unit/integrations/returns/nochex_return_test.rb +10 -0
  283. data/test/unit/integrations/returns/paypal_return_test.rb +10 -0
  284. data/test/unit/integrations/returns/return_test.rb +11 -0
  285. data/test/unit/integrations/returns/two_checkout_return_test.rb +24 -0
  286. data/test/unit/integrations/two_checkout_module_test.rb +13 -0
  287. data/test/unit/post_data_test.rb +55 -0
  288. data/test/unit/posts_data_test.rb +48 -0
  289. data/test/unit/response_test.rb +28 -0
  290. data/test/unit/utils_test.rb +7 -0
  291. data/test/unit/validateable_test.rb +60 -0
  292. metadata +396 -0
@@ -0,0 +1,21 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module CreditCardFormatting
4
+
5
+ # This method is used to format numerical information pertaining to credit cards.
6
+ #
7
+ # format(2005, :two_digits) # => "05"
8
+ # format(05, :four_digits) # => "0005"
9
+ def format(number, option)
10
+ return '' if number.blank?
11
+
12
+ case option
13
+ when :two_digits ; sprintf("%.2i", number)[-2..-1]
14
+ when :four_digits ; sprintf("%.4i", number)[-4..-1]
15
+ else number
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,125 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ # Convenience methods that can be included into a custom Credit Card object, such as an ActiveRecord based Credit Card object.
4
+ module CreditCardMethods
5
+ CARD_COMPANIES = {
6
+ 'visa' => /^4\d{12}(\d{3})?$/,
7
+ 'master' => /^(5[1-5]\d{4}|677189)\d{10}$/,
8
+ 'discover' => /^(6011|65\d{2})\d{12}$/,
9
+ 'american_express' => /^3[47]\d{13}$/,
10
+ 'diners_club' => /^3(0[0-5]|[68]\d)\d{11}$/,
11
+ 'jcb' => /^3528\d{12}$/,
12
+ 'switch' => /^6759\d{12}(\d{2,3})?$/,
13
+ 'solo' => /^6767\d{12}(\d{2,3})?$/,
14
+ 'dankort' => /^5019\d{12}$/,
15
+ 'maestro' => /^(5[06-8]|6\d)\d{10,17}$/,
16
+ 'forbrugsforeningen' => /^600722\d{10}$/,
17
+ 'laser' => /^(6304|6706|6771|6709)\d{8}(\d{4}|\d{6,7})?$/
18
+ }
19
+
20
+ def self.included(base)
21
+ base.extend(ClassMethods)
22
+ end
23
+
24
+ def valid_month?(month)
25
+ (1..12).include?(month)
26
+ end
27
+
28
+ def valid_expiry_year?(year)
29
+ (Time.now.year..Time.now.year + 20).include?(year)
30
+ end
31
+
32
+ def valid_start_year?(year)
33
+ year.to_s =~ /^\d{4}$/ && year.to_i > 1987
34
+ end
35
+
36
+ def valid_issue_number?(number)
37
+ number.to_s =~ /^\d{1,2}$/
38
+ end
39
+
40
+ module ClassMethods
41
+ # Returns true if it validates. Optionally, you can pass a card type as an argument and
42
+ # make sure it is of the correct type.
43
+ #
44
+ # References:
45
+ # - http://perl.about.com/compute/perl/library/nosearch/P073000.htm
46
+ # - http://www.beachnet.com/~hstiles/cardtype.html
47
+ def valid_number?(number)
48
+ valid_test_mode_card_number?(number) ||
49
+ valid_card_number_length?(number) &&
50
+ valid_checksum?(number)
51
+ end
52
+
53
+ # Regular expressions for the known card companies.
54
+ #
55
+ # References:
56
+ # - http://en.wikipedia.org/wiki/Credit_card_number
57
+ # - http://www.barclaycardbusiness.co.uk/information_zone/processing/bin_rules.html
58
+ def card_companies
59
+ CARD_COMPANIES
60
+ end
61
+
62
+ # Returns a string containing the type of card from the list of known information below.
63
+ # Need to check the cards in a particular order, as there is some overlap of the allowable ranges
64
+ #--
65
+ # TODO Refactor this method. We basically need to tighten up the Maestro Regexp.
66
+ #
67
+ # Right now the Maestro regexp overlaps with the MasterCard regexp (IIRC). If we can tighten
68
+ # things up, we can boil this whole thing down to something like...
69
+ #
70
+ # def type?(number)
71
+ # return 'visa' if valid_test_mode_card_number?(number)
72
+ # card_companies.find([nil]) { |type, regexp| number =~ regexp }.first.dup
73
+ # end
74
+ #
75
+ def type?(number)
76
+ return 'bogus' if valid_test_mode_card_number?(number)
77
+
78
+ card_companies.reject { |c,p| c == 'maestro' }.each do |company, pattern|
79
+ return company.dup if number =~ pattern
80
+ end
81
+
82
+ return 'maestro' if number =~ card_companies['maestro']
83
+
84
+ return nil
85
+ end
86
+
87
+ def last_digits(number)
88
+ number.to_s.length <= 4 ? number : number.to_s.slice(-4..-1)
89
+ end
90
+
91
+ def mask(number)
92
+ "XXXX-XXXX-XXXX-#{last_digits(number)}"
93
+ end
94
+
95
+ # Checks to see if the calculated type matches the specified type
96
+ def matching_type?(number, type)
97
+ type?(number) == type
98
+ end
99
+
100
+ private
101
+
102
+ def valid_card_number_length?(number) #:nodoc:
103
+ number.to_s.length >= 12
104
+ end
105
+
106
+ def valid_test_mode_card_number?(number) #:nodoc:
107
+ ActiveMerchant::Billing::Base.test? &&
108
+ %w[1 2 3 success failure error].include?(number.to_s)
109
+ end
110
+
111
+ # Checks the validity of a card number by use of the the Luhn Algorithm.
112
+ # Please see http://en.wikipedia.org/wiki/Luhn_algorithm for details.
113
+ def valid_checksum?(number) #:nodoc:
114
+ sum = 0
115
+ for i in 0..number.length
116
+ weight = number[-1 * (i + 2), 1].to_i * (2 - (i % 2))
117
+ sum += (weight < 10) ? weight : weight - 9
118
+ end
119
+
120
+ (number[-1,1].to_i == (10 - sum % 10) % 10)
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,38 @@
1
+ module ActiveMerchant
2
+ module Billing
3
+ # Result of the Card Verification Value check
4
+ # http://www.bbbonline.org/eExport/doc/MerchantGuide_cvv2.pdf
5
+ # Check additional codes from cybersource website
6
+ class CVVResult
7
+
8
+ MESSAGES = {
9
+ 'D' => 'Suspicious transaction',
10
+ 'I' => 'Failed data validation check',
11
+ 'M' => 'Match',
12
+ 'N' => 'No Match',
13
+ 'P' => 'Not Processed',
14
+ 'S' => 'Should have been present',
15
+ 'U' => 'Issuer unable to process request',
16
+ 'X' => 'Card does not support verification'
17
+ }
18
+
19
+ def self.messages
20
+ MESSAGES
21
+ end
22
+
23
+ attr_reader :code, :message
24
+
25
+ def initialize(code)
26
+ @code = code.upcase unless code.blank?
27
+ @message = MESSAGES[@code]
28
+ end
29
+
30
+ def to_hash
31
+ {
32
+ 'code' => code,
33
+ 'message' => message
34
+ }
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,34 @@
1
+ require 'date'
2
+
3
+ module ActiveMerchant
4
+ module Billing
5
+ class CreditCard
6
+ class ExpiryDate #:nodoc:
7
+ attr_reader :month, :year
8
+ def initialize(month, year)
9
+ @month = month.to_i
10
+ @year = year.to_i
11
+ end
12
+
13
+ def expired? #:nodoc:
14
+ Time.now.utc > expiration
15
+ end
16
+
17
+ def expiration #:nodoc:
18
+ begin
19
+ Time.utc(year, month, month_days, 23, 59, 59)
20
+ rescue ArgumentError
21
+ Time.at(0).utc
22
+ end
23
+ end
24
+
25
+ private
26
+ def month_days
27
+ mdays = [nil,31,28,31,30,31,30,31,31,30,31,30,31]
28
+ mdays[2] = 29 if Date.leap?(year)
29
+ mdays[month]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,158 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'active_merchant/billing/response'
4
+
5
+ module ActiveMerchant #:nodoc:
6
+ module Billing #:nodoc:
7
+ #
8
+ # == Description
9
+ # The Gateway class is the base class for all ActiveMerchant gateway implementations.
10
+ #
11
+ # The standard list of gateway functions that most concrete gateway subclasses implement is:
12
+ #
13
+ # * <tt>purchase(money, creditcard, options = {})</tt>
14
+ # * <tt>authorize(money, creditcard, options = {})</tt>
15
+ # * <tt>capture(money, authorization, options = {})</tt>
16
+ # * <tt>void(identification, options = {})</tt>
17
+ # * <tt>credit(money, identification, options = {})</tt>
18
+ #
19
+ # Some gateways include features for recurring billing
20
+ #
21
+ # * <tt>recurring(money, creditcard, options = {})</tt>
22
+ #
23
+ # Some gateways also support features for storing credit cards:
24
+ #
25
+ # * <tt>store(creditcard, options = {})</tt>
26
+ # * <tt>unstore(identification, options = {})</tt>
27
+ #
28
+ # === Gateway Options
29
+ # The options hash consists of the following options:
30
+ #
31
+ # * <tt>:order_id</tt> - The order number
32
+ # * <tt>:ip</tt> - The IP address of the customer making the purchase
33
+ # * <tt>:customer</tt> - The name, customer number, or other information that identifies the customer
34
+ # * <tt>:invoice</tt> - The invoice number
35
+ # * <tt>:merchant</tt> - The name or description of the merchant offering the product
36
+ # * <tt>:description</tt> - A description of the transaction
37
+ # * <tt>:email</tt> - The email address of the customer
38
+ # * <tt>:currency</tt> - The currency of the transaction. Only important when you are using a currency that is not the default with a gateway that supports multiple currencies.
39
+ # * <tt>:billing_address</tt> - A hash containing the billing address of the customer.
40
+ # * <tt>:shipping_address</tt> - A hash containing the shipping address of the customer.
41
+ #
42
+ # The <tt>:billing_address</tt>, and <tt>:shipping_address</tt> hashes can have the following keys:
43
+ #
44
+ # * <tt>:name</tt> - The full name of the customer.
45
+ # * <tt>:company</tt> - The company name of the customer.
46
+ # * <tt>:address1</tt> - The primary street address of the customer.
47
+ # * <tt>:address2</tt> - Additional line of address information.
48
+ # * <tt>:city</tt> - The city of the customer.
49
+ # * <tt>:state</tt> - The state of the customer. The 2 digit code for US and Canadian addresses. The full name of the state or province for foreign addresses.
50
+ # * <tt>:country</tt> - The [ISO 3166-1-alpha-2 code](http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm) for the customer.
51
+ # * <tt>:zip</tt> - The zip or postal code of the customer.
52
+ # * <tt>:phone</tt> - The phone number of the customer.
53
+ #
54
+ # == Implmenting new gateways
55
+ #
56
+ # See the {ActiveMerchant Guide to Contributing}[http://code.google.com/p/activemerchant/wiki/Contributing]
57
+ #
58
+ class Gateway
59
+ include PostsData
60
+ include RequiresParameters
61
+ include CreditCardFormatting
62
+ include Utils
63
+
64
+ DEBIT_CARDS = [ :switch, :solo ]
65
+
66
+ cattr_reader :implementations
67
+ @@implementations = []
68
+
69
+ def self.inherited(subclass)
70
+ super
71
+ @@implementations << subclass
72
+ end
73
+
74
+ # The format of the amounts used by the gateway
75
+ # :dollars => '12.50'
76
+ # :cents => '1250'
77
+ class_inheritable_accessor :money_format
78
+ self.money_format = :dollars
79
+
80
+ # The default currency for the transactions if no currency is provided
81
+ class_inheritable_accessor :default_currency
82
+
83
+ # The countries of merchants the gateway supports
84
+ class_inheritable_accessor :supported_countries
85
+ self.supported_countries = []
86
+
87
+ # The supported card types for the gateway
88
+ class_inheritable_accessor :supported_cardtypes
89
+ self.supported_cardtypes = []
90
+
91
+ class_inheritable_accessor :homepage_url
92
+ class_inheritable_accessor :display_name
93
+
94
+ # The application making the calls to the gateway
95
+ # Useful for things like the PayPal build notation (BN) id fields
96
+ superclass_delegating_accessor :application_id
97
+ self.application_id = 'ActiveMerchant'
98
+
99
+ attr_reader :options
100
+
101
+ # Use this method to check if your gateway of interest supports a credit card of some type
102
+ def self.supports?(card_type)
103
+ supported_cardtypes.include?(card_type.to_sym)
104
+ end
105
+
106
+ def self.card_brand(source)
107
+ result = source.respond_to?(:brand) ? source.brand : source.type
108
+ result.to_s.downcase
109
+ end
110
+
111
+ def card_brand(source)
112
+ self.class.card_brand(source)
113
+ end
114
+
115
+ # Initialize a new gateway.
116
+ #
117
+ # See the documentation for the gateway you will be using to make sure there are no other
118
+ # required options.
119
+ def initialize(options = {})
120
+ end
121
+
122
+ # Are we running in test mode?
123
+ def test?
124
+ Base.gateway_mode == :test
125
+ end
126
+
127
+ private # :nodoc: all
128
+
129
+ def name
130
+ self.class.name.scan(/\:\:(\w+)Gateway/).flatten.first
131
+ end
132
+
133
+ def amount(money)
134
+ return nil if money.nil?
135
+ cents = money.respond_to?(:cents) ? money.cents : money
136
+
137
+ if money.is_a?(String) or cents.to_i < 0
138
+ raise ArgumentError, 'money amount must be either a Money object or a positive integer in cents.'
139
+ end
140
+
141
+ if self.money_format == :cents
142
+ cents.to_s
143
+ else
144
+ sprintf("%.2f", cents.to_f / 100)
145
+ end
146
+ end
147
+
148
+ def currency(money)
149
+ money.respond_to?(:currency) ? money.currency : self.default_currency
150
+ end
151
+
152
+ def requires_start_date_or_issue_number?(credit_card)
153
+ return false if card_brand(credit_card).blank?
154
+ DEBIT_CARDS.include?(card_brand(credit_card).to_sym)
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,3 @@
1
+ require 'active_merchant/billing/gateway'
2
+
3
+ Dir[File.dirname(__FILE__) + '/gateways/*.rb'].each{|g| require g}
@@ -0,0 +1,657 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ # For more information on the Authorize.Net Gateway please visit their {Integration Center}[http://developer.authorize.net/]
4
+ #
5
+ # The login and password are not the username and password you use to
6
+ # login to the Authorize.Net Merchant Interface. Instead, you will
7
+ # use the API Login ID as the login and Transaction Key as the
8
+ # password.
9
+ #
10
+ # ==== How to Get Your API Login ID and Transaction Key
11
+ #
12
+ # 1. Log into the Merchant Interface
13
+ # 2. Select Settings from the Main Menu
14
+ # 3. Click on API Login ID and Transaction Key in the Security section
15
+ # 4. Type in the answer to the secret question configured on setup
16
+ # 5. Click Submit
17
+ #
18
+ # ==== Automated Recurring Billing (ARB)
19
+ #
20
+ # Automated Recurring Billing (ARB) is an optional service for submitting and managing recurring, or subscription-based, transactions.
21
+ #
22
+ # To use recurring, update_recurring, and cancel_recurring ARB must be enabled for your account.
23
+ #
24
+ # Information about ARB is available on the {Authorize.Net website}[http://www.authorize.net/solutions/merchantsolutions/merchantservices/automatedrecurringbilling/].
25
+ # Information about the ARB API is available at the {Authorize.Net Integration Center}[http://developer.authorize.net/]
26
+ class AuthorizeNetGateway < Gateway
27
+ API_VERSION = '3.1'
28
+
29
+ class_inheritable_accessor :test_url, :live_url, :arb_test_url, :arb_live_url
30
+
31
+ self.test_url = "https://test.authorize.net/gateway/transact.dll"
32
+ self.live_url = "https://secure.authorize.net/gateway/transact.dll"
33
+
34
+ self.arb_test_url = 'https://apitest.authorize.net/xml/v1/request.api'
35
+ self.arb_live_url = 'https://api.authorize.net/xml/v1/request.api'
36
+
37
+ class_inheritable_accessor :duplicate_window
38
+
39
+ APPROVED, DECLINED, ERROR, FRAUD_REVIEW = 1, 2, 3, 4
40
+
41
+ RESPONSE_CODE, RESPONSE_REASON_CODE, RESPONSE_REASON_TEXT = 0, 2, 3
42
+ AVS_RESULT_CODE, TRANSACTION_ID, CARD_CODE_RESPONSE_CODE = 5, 6, 38
43
+
44
+ self.supported_countries = ['US']
45
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
46
+ self.homepage_url = 'http://www.authorize.net/'
47
+ self.display_name = 'Authorize.Net'
48
+
49
+ CARD_CODE_ERRORS = %w( N S )
50
+ AVS_ERRORS = %w( A E N R W Z )
51
+
52
+ AUTHORIZE_NET_ARB_NAMESPACE = 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'
53
+
54
+ RECURRING_ACTIONS = {
55
+ :create => 'ARBCreateSubscription',
56
+ :update => 'ARBUpdateSubscription',
57
+ :cancel => 'ARBCancelSubscription'
58
+ }
59
+
60
+ # Creates a new AuthorizeNetGateway
61
+ #
62
+ # The gateway requires that a valid login and password be passed
63
+ # in the +options+ hash.
64
+ #
65
+ # ==== Options
66
+ #
67
+ # * <tt>:login</tt> -- The Authorize.Net API Login ID (REQUIRED)
68
+ # * <tt>:password</tt> -- The Authorize.Net Transaction Key. (REQUIRED)
69
+ # * <tt>:test</tt> -- +true+ or +false+. If true, perform transactions against the test server.
70
+ # Otherwise, perform transactions against the production server.
71
+ def initialize(options = {})
72
+ requires!(options, :login, :password)
73
+ @options = options
74
+ super
75
+ end
76
+
77
+ # Performs an authorization, which reserves the funds on the customer's credit card, but does not
78
+ # charge the card.
79
+ #
80
+ # ==== Parameters
81
+ #
82
+ # * <tt>money</tt> -- The amount to be authorized. Either an Integer value in cents or a Money object.
83
+ # * <tt>creditcard</tt> -- The CreditCard details for the transaction.
84
+ # * <tt>options</tt> -- A hash of optional parameters.
85
+ def authorize(money, creditcard, options = {})
86
+ post = {}
87
+ add_invoice(post, options)
88
+ add_creditcard(post, creditcard)
89
+ add_address(post, options)
90
+ add_customer_data(post, options)
91
+ add_duplicate_window(post)
92
+
93
+ commit('AUTH_ONLY', money, post)
94
+ end
95
+
96
+ # Perform a purchase, which is essentially an authorization and capture in a single operation.
97
+ #
98
+ # ==== Parameters
99
+ #
100
+ # * <tt>money</tt> -- The amount to be purchased. Either an Integer value in cents or a Money object.
101
+ # * <tt>creditcard</tt> -- The CreditCard details for the transaction.
102
+ # * <tt>options</tt> -- A hash of optional parameters.
103
+ def purchase(money, creditcard, options = {})
104
+ post = {}
105
+ add_invoice(post, options)
106
+ add_creditcard(post, creditcard)
107
+ add_address(post, options)
108
+ add_customer_data(post, options)
109
+ add_duplicate_window(post)
110
+
111
+ commit('AUTH_CAPTURE', money, post)
112
+ end
113
+
114
+ # Captures the funds from an authorized transaction.
115
+ #
116
+ # ==== Parameters
117
+ #
118
+ # * <tt>money</tt> -- The amount to be captured. Either an Integer value in cents or a Money object.
119
+ # * <tt>authorization</tt> -- The authorization returned from the previous authorize request.
120
+ def capture(money, authorization, options = {})
121
+ post = {:trans_id => authorization}
122
+ add_customer_data(post, options)
123
+ commit('PRIOR_AUTH_CAPTURE', money, post)
124
+ end
125
+
126
+ # Void a previous transaction
127
+ #
128
+ # ==== Parameters
129
+ #
130
+ # * <tt>authorization</tt> - The authorization returned from the previous authorize request.
131
+ def void(authorization, options = {})
132
+ post = {:trans_id => authorization}
133
+ commit('VOID', nil, post)
134
+ end
135
+
136
+ # Credit an account.
137
+ #
138
+ # This transaction is also referred to as a Refund and indicates to the gateway that
139
+ # money should flow from the merchant to the customer.
140
+ #
141
+ # ==== Parameters
142
+ #
143
+ # * <tt>money</tt> -- The amount to be credited to the customer. Either an Integer value in cents or a Money object.
144
+ # * <tt>identification</tt> -- The ID of the original transaction against which the credit is being issued.
145
+ # * <tt>options</tt> -- A hash of parameters.
146
+ #
147
+ # ==== Options
148
+ #
149
+ # * <tt>:card_number</tt> -- The credit card number the credit is being issued to. (REQUIRED)
150
+ def credit(money, identification, options = {})
151
+ requires!(options, :card_number)
152
+
153
+ post = { :trans_id => identification,
154
+ :card_num => options[:card_number]
155
+ }
156
+ add_invoice(post, options)
157
+
158
+ commit('CREDIT', money, post)
159
+ end
160
+
161
+ # Create a recurring payment.
162
+ #
163
+ # This transaction creates a new Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled.
164
+ #
165
+ # ==== Parameters
166
+ #
167
+ # * <tt>money</tt> -- The amount to be charged to the customer at each interval. Either an Integer value in cents or
168
+ # a Money object.
169
+ # * <tt>creditcard</tt> -- The CreditCard details for the transaction.
170
+ # * <tt>options</tt> -- A hash of parameters.
171
+ #
172
+ # ==== Options
173
+ #
174
+ # * <tt>:interval</tt> -- A hash containing information about the interval of time between payments. Must
175
+ # contain the keys <tt>:length</tt> and <tt>:unit</tt>. <tt>:unit</tt> can be either <tt>:months</tt> or <tt>:days</tt>.
176
+ # If <tt>:unit</tt> is <tt>:months</tt> then <tt>:interval</tt> must be an integer between 1 and 12 inclusive.
177
+ # If <tt>:unit</tt> is <tt>:days</tt> then <tt>:interval</tt> must be an integer between 7 and 365 inclusive.
178
+ # For example, to charge the customer once every three months the hash would be
179
+ # +{ :unit => :months, :interval => 3 }+ (REQUIRED)
180
+ # * <tt>:duration</tt> -- A hash containing keys for the <tt>:start_date</tt> the subscription begins (also the date the
181
+ # initial billing occurs) and the total number of billing <tt>:occurences</tt> or payments for the subscription. (REQUIRED)
182
+ def recurring(money, creditcard, options={})
183
+ requires!(options, :interval, :duration, :billing_address)
184
+ requires!(options[:interval], :length, [:unit, :days, :months])
185
+ requires!(options[:duration], :start_date, :occurrences)
186
+ requires!(options[:billing_address], :first_name, :last_name)
187
+
188
+ options[:credit_card] = creditcard
189
+ options[:amount] = money
190
+
191
+ request = build_recurring_request(:create, options)
192
+ recurring_commit(:create, request)
193
+ end
194
+
195
+ # Update a recurring payment's details.
196
+ #
197
+ # This transaction updates an existing Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled
198
+ # and the subscription must have already been created previously by calling +recurring()+. The ability to change certain
199
+ # details about a recurring payment is dependent on transaction history and cannot be determined until after calling
200
+ # +update_recurring()+. See the ARB XML Guide for such conditions.
201
+ #
202
+ # ==== Parameters
203
+ #
204
+ # * <tt>options</tt> -- A hash of parameters.
205
+ #
206
+ # ==== Options
207
+ #
208
+ # * <tt>:subscription_id</tt> -- A string containing the <tt>:subscription_id</tt> of the recurring payment already in place
209
+ # for a given credit card. (REQUIRED)
210
+ def update_recurring(options={})
211
+ requires!(options, :subscription_id)
212
+ request = build_recurring_request(:update, options)
213
+ recurring_commit(:update, request)
214
+ end
215
+
216
+ # Cancel a recurring payment.
217
+ #
218
+ # This transaction cancels an existing Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled
219
+ # and the subscription must have already been created previously by calling recurring()
220
+ #
221
+ # ==== Parameters
222
+ #
223
+ # * <tt>subscription_id</tt> -- A string containing the +subscription_id+ of the recurring payment already in place
224
+ # for a given credit card. (REQUIRED)
225
+ def cancel_recurring(subscription_id)
226
+ request = build_recurring_request(:cancel, :subscription_id => subscription_id)
227
+ recurring_commit(:cancel, request)
228
+ end
229
+
230
+ private
231
+
232
+ def commit(action, money, parameters)
233
+ parameters[:amount] = amount(money) unless action == 'VOID'
234
+
235
+ # Only activate the test_request when the :test option is passed in
236
+ parameters[:test_request] = @options[:test] ? 'TRUE' : 'FALSE'
237
+
238
+ url = test? ? self.test_url : self.live_url
239
+ data = ssl_post url, post_data(action, parameters)
240
+
241
+ response = parse(data)
242
+
243
+ message = message_from(response)
244
+
245
+ # Return the response. The authorization can be taken out of the transaction_id
246
+ # Test Mode on/off is something we have to parse from the response text.
247
+ # It usually looks something like this
248
+ #
249
+ # (TESTMODE) Successful Sale
250
+ test_mode = test? || message =~ /TESTMODE/
251
+
252
+ Response.new(success?(response), message, response,
253
+ :test => test_mode,
254
+ :authorization => response[:transaction_id],
255
+ :fraud_review => fraud_review?(response),
256
+ :avs_result => { :code => response[:avs_result_code] },
257
+ :cvv_result => response[:card_code]
258
+ )
259
+ end
260
+
261
+ def success?(response)
262
+ response[:response_code] == APPROVED
263
+ end
264
+
265
+ def fraud_review?(response)
266
+ response[:response_code] == FRAUD_REVIEW
267
+ end
268
+
269
+ def parse(body)
270
+ fields = split(body)
271
+
272
+ results = {
273
+ :response_code => fields[RESPONSE_CODE].to_i,
274
+ :response_reason_code => fields[RESPONSE_REASON_CODE],
275
+ :response_reason_text => fields[RESPONSE_REASON_TEXT],
276
+ :avs_result_code => fields[AVS_RESULT_CODE],
277
+ :transaction_id => fields[TRANSACTION_ID],
278
+ :card_code => fields[CARD_CODE_RESPONSE_CODE]
279
+ }
280
+ results
281
+ end
282
+
283
+ def post_data(action, parameters = {})
284
+ post = {}
285
+
286
+ post[:version] = API_VERSION
287
+ post[:login] = @options[:login]
288
+ post[:tran_key] = @options[:password]
289
+ post[:relay_response] = "FALSE"
290
+ post[:type] = action
291
+ post[:delim_data] = "TRUE"
292
+ post[:delim_char] = ","
293
+ post[:encap_char] = "$"
294
+
295
+ request = post.merge(parameters).collect { |key, value| "x_#{key}=#{CGI.escape(value.to_s)}" }.join("&")
296
+ request
297
+ end
298
+
299
+ def add_invoice(post, options)
300
+ post[:invoice_num] = options[:order_id]
301
+ post[:description] = options[:description]
302
+ end
303
+
304
+ def add_creditcard(post, creditcard)
305
+ post[:card_num] = creditcard.number
306
+ post[:card_code] = creditcard.verification_value if creditcard.verification_value?
307
+ post[:exp_date] = expdate(creditcard)
308
+ post[:first_name] = creditcard.first_name
309
+ post[:last_name] = creditcard.last_name
310
+ end
311
+
312
+ def add_customer_data(post, options)
313
+ if options.has_key? :email
314
+ post[:email] = options[:email]
315
+ post[:email_customer] = false
316
+ end
317
+
318
+ if options.has_key? :customer
319
+ post[:cust_id] = options[:customer]
320
+ end
321
+
322
+ if options.has_key? :ip
323
+ post[:customer_ip] = options[:ip]
324
+ end
325
+ end
326
+
327
+ # x_duplicate_window won't be sent by default, because sending it changes the response.
328
+ # "If this field is present in the request with or without a value, an enhanced duplicate transaction response will be sent."
329
+ # (as of 2008-12-30) http://www.authorize.net/support/AIM_guide_SCC.pdf
330
+ def add_duplicate_window(post)
331
+ unless duplicate_window.nil?
332
+ post[:duplicate_window] = duplicate_window
333
+ end
334
+ end
335
+
336
+ def add_address(post, options)
337
+ if address = options[:billing_address] || options[:address]
338
+ post[:address] = address[:address1].to_s
339
+ post[:company] = address[:company].to_s
340
+ post[:phone] = address[:phone].to_s
341
+ post[:zip] = address[:zip].to_s
342
+ post[:city] = address[:city].to_s
343
+ post[:country] = address[:country].to_s
344
+ post[:state] = address[:state].blank? ? 'n/a' : address[:state]
345
+ end
346
+
347
+ if address = options[:shipping_address]
348
+ post[:ship_to_first_name] = address[:first_name].to_s
349
+ post[:ship_to_last_name] = address[:last_name].to_s
350
+ post[:ship_to_address] = address[:address1].to_s
351
+ post[:ship_to_company] = address[:company].to_s
352
+ post[:ship_to_phone] = address[:phone].to_s
353
+ post[:ship_to_zip] = address[:zip].to_s
354
+ post[:ship_to_city] = address[:city].to_s
355
+ post[:ship_to_country] = address[:country].to_s
356
+ post[:ship_to_state] = address[:state].blank? ? 'n/a' : address[:state]
357
+ end
358
+ end
359
+
360
+ # Make a ruby type out of the response string
361
+ def normalize(field)
362
+ case field
363
+ when "true" then true
364
+ when "false" then false
365
+ when "" then nil
366
+ when "null" then nil
367
+ else field
368
+ end
369
+ end
370
+
371
+ def message_from(results)
372
+ if results[:response_code] == DECLINED
373
+ return CVVResult.messages[ results[:card_code] ] if CARD_CODE_ERRORS.include?(results[:card_code])
374
+ return AVSResult.messages[ results[:avs_result_code] ] if AVS_ERRORS.include?(results[:avs_result_code])
375
+ end
376
+
377
+ return results[:response_reason_text].nil? ? '' : results[:response_reason_text][0..-2]
378
+ end
379
+
380
+ def expdate(creditcard)
381
+ year = sprintf("%.4i", creditcard.year)
382
+ month = sprintf("%.2i", creditcard.month)
383
+
384
+ "#{month}#{year[-2..-1]}"
385
+ end
386
+
387
+ def split(response)
388
+ response[1..-2].split(/\$,\$/)
389
+ end
390
+
391
+ # ARB
392
+
393
+ # Builds recurring billing request
394
+ def build_recurring_request(action, options = {})
395
+ unless RECURRING_ACTIONS.include?(action)
396
+ raise StandardError, "Invalid Automated Recurring Billing Action: #{action}"
397
+ end
398
+
399
+ xml = Builder::XmlMarkup.new(:indent => 2)
400
+ xml.instruct!(:xml, :version => '1.0', :encoding => 'utf-8')
401
+ xml.tag!("#{RECURRING_ACTIONS[action]}Request", :xmlns => AUTHORIZE_NET_ARB_NAMESPACE) do
402
+ add_arb_merchant_authentication(xml)
403
+ # Merchant-assigned reference ID for the request
404
+ xml.tag!('refId', options[:ref_id]) if options[:ref_id]
405
+ send("build_arb_#{action}_subscription_request", xml, options)
406
+ end
407
+ end
408
+
409
+ # Contains the merchant’s payment gateway account authentication information
410
+ def add_arb_merchant_authentication(xml)
411
+ xml.tag!('merchantAuthentication') do
412
+ xml.tag!('name', @options[:login])
413
+ xml.tag!('transactionKey', @options[:password])
414
+ end
415
+ end
416
+
417
+ # Builds body for ARBCreateSubscriptionRequest
418
+ def build_arb_create_subscription_request(xml, options)
419
+ # Subscription
420
+ add_arb_subscription(xml, options)
421
+
422
+ xml.target!
423
+ end
424
+
425
+ # Builds body for ARBUpdateSubscriptionRequest
426
+ def build_arb_update_subscription_request(xml, options)
427
+ xml.tag!('subscriptionId', options[:subscription_id])
428
+ # Adds Subscription
429
+ add_arb_subscription(xml, options)
430
+
431
+ xml.target!
432
+ end
433
+
434
+ # Builds body for ARBCancelSubscriptionRequest
435
+ def build_arb_cancel_subscription_request(xml, options)
436
+ xml.tag!('subscriptionId', options[:subscription_id])
437
+
438
+ xml.target!
439
+ end
440
+
441
+ # Adds subscription information
442
+ def add_arb_subscription(xml, options)
443
+ xml.tag!('subscription') do
444
+ # Merchant-assigned name for the subscription (optional)
445
+ xml.tag!('name', options[:subscription_name]) if options[:subscription_name]
446
+ # Contains information about the payment schedule
447
+ add_arb_payment_schedule(xml, options)
448
+ # The amount to be billed to the customer
449
+ # for each payment in the subscription
450
+ xml.tag!('amount', amount(options[:amount])) if options[:amount]
451
+ if trial = options[:trial]
452
+ # The amount to be charged for each payment during a trial period (conditional)
453
+ xml.tag!('trialAmount', amount(trial[:amount])) if trial[:amount]
454
+ end
455
+ # Contains either the customer’s credit card
456
+ # or bank account payment information
457
+ add_arb_payment(xml, options)
458
+ # Contains order information (optional)
459
+ add_arb_order(xml, options)
460
+ # Contains information about the customer
461
+ add_arb_customer(xml, options)
462
+ # Contains the customer's billing address information
463
+ add_arb_address(xml, 'billTo', options[:billing_address])
464
+ # Contains the customer's shipping address information (optional)
465
+ add_arb_address(xml, 'shipTo', options[:shipping_address])
466
+ end
467
+ end
468
+
469
+ # Adds information about the interval of time between payments
470
+ def add_arb_interval(xml, options)
471
+ interval = options[:interval]
472
+ return unless interval
473
+ xml.tag!('interval') do
474
+ # The measurement of time, in association with the Interval Unit,
475
+ # that is used to define the frequency of the billing occurrences
476
+ xml.tag!('length', interval[:length])
477
+ # The unit of time, in association with the Interval Length,
478
+ # between each billing occurrence
479
+ xml.tag!('unit', interval[:unit].to_s)
480
+ end
481
+ end
482
+
483
+ # Adds information about the subscription duration
484
+ def add_arb_duration(xml, options)
485
+ duration = options[:duration]
486
+ return unless duration
487
+ # The date the subscription begins
488
+ # (also the date the initial billing occurs)
489
+ xml.tag!('startDate', duration[:start_date]) if duration[:start_date]
490
+ # Number of billing occurrences or payments for the subscription
491
+ xml.tag!('totalOccurrences', duration[:occurrences]) if duration[:occurrences]
492
+ end
493
+
494
+ def add_arb_payment_schedule(xml, options)
495
+ return unless options[:interval] || options[:duration]
496
+ xml.tag!('paymentSchedule') do
497
+ # Contains information about the interval of time between payments
498
+ add_arb_interval(xml, options)
499
+ add_arb_duration(xml, options)
500
+ if trial = options[:trial]
501
+ # Number of billing occurrences or payments in the trial period (optional)
502
+ xml.tag!('trialOccurrences', trial[:occurrences]) if trial[:occurrences]
503
+ end
504
+ end
505
+ end
506
+
507
+ # Adds customer's credit card or bank account payment information
508
+ def add_arb_payment(xml, options)
509
+ return unless options[:credit_card] || options[:bank_account]
510
+ xml.tag!('payment') do
511
+ # Contains the customer’s credit card information
512
+ add_arb_credit_card(xml, options)
513
+ # Contains the customer’s bank account information
514
+ add_arb_bank_account(xml, options)
515
+ end
516
+ end
517
+
518
+ # Adds customer’s credit card information
519
+ # Note: This element should only be included
520
+ # when the payment method is credit card.
521
+ def add_arb_credit_card(xml, options)
522
+ credit_card = options[:credit_card]
523
+ return unless credit_card
524
+ xml.tag!('creditCard') do
525
+ # The credit card number used for payment of the subscription
526
+ xml.tag!('cardNumber', credit_card.number)
527
+ # The expiration date of the credit card used for the subscription
528
+ xml.tag!('expirationDate', arb_expdate(credit_card))
529
+ end
530
+ end
531
+
532
+ # Adds customer’s bank account information
533
+ # Note: This element should only be included
534
+ # when the payment method is bank account.
535
+ def add_arb_bank_account(xml, options)
536
+ bank_account = options[:bank_account]
537
+ return unless bank_account
538
+ xml.tag!('bankAccount') do
539
+ # The type of bank account used for payment of the subscription
540
+ xml.tag!('accountType', bank_account[:account_type])
541
+ # The routing number of the customer’s bank
542
+ xml.tag!('routingNumber', bank_account[:routing_number])
543
+ # The bank account number used for payment of the subscription
544
+ xml.tag!('accountNumber', bank_account[:account_number])
545
+ # The full name of the individual associated
546
+ # with the bank account number
547
+ xml.tag!('nameOfAccount', bank_account[:name_of_account])
548
+ # The full name of the individual associated
549
+ # with the bank account number (optional)
550
+ xml.tag!('bankName', bank_account[:bank_name]) if bank_account[:bank_name]
551
+ # The type of electronic check transaction used for the subscription
552
+ xml.tag!('echeckType', bank_account[:echeck_type])
553
+ end
554
+ end
555
+
556
+ # Adds order information (optional)
557
+ def add_arb_order(xml, options)
558
+ order = options[:order]
559
+ return unless order
560
+ xml.tag!('order') do
561
+ # Merchant-assigned invoice number for the subscription (optional)
562
+ xml.tag!('invoiceNumber', order[:invoice_number])
563
+ # Description of the subscription (optional)
564
+ xml.tag!('description', order[:description])
565
+ end
566
+ end
567
+
568
+ # Adds information about the customer
569
+ def add_arb_customer(xml, options)
570
+ customer = options[:customer]
571
+ return unless customer
572
+ xml.tag!('customer') do
573
+ xml.tag!('type', customer[:type]) if customer[:type]
574
+ xml.tag!('id', customer[:id]) if customer[:id]
575
+ xml.tag!('email', customer[:email]) if customer[:email]
576
+ xml.tag!('phoneNumber', customer[:phone_number]) if customer[:phone_number]
577
+ xml.tag!('faxNumber', customer[:fax_number]) if customer[:fax_number]
578
+ add_arb_drivers_license(xml, options)
579
+ xml.tag!('taxId', customer[:tax_id]) if customer[:tax_id]
580
+ end
581
+ end
582
+
583
+ # Adds the customer's driver's license information (conditional)
584
+ def add_arb_drivers_license(xml, options)
585
+ return unless customer = options[:customer]
586
+ return unless drivers_license = customer[:drivers_license]
587
+ xml.tag!('driversLicense') do
588
+ # The customer's driver's license number
589
+ xml.tag!('number', drivers_license[:number])
590
+ # The customer's driver's license state
591
+ xml.tag!('state', drivers_license[:state])
592
+ # The customer's driver's license date of birth
593
+ xml.tag!('dateOfBirth', drivers_license[:date_of_birth])
594
+ end
595
+ end
596
+
597
+ # Adds address information
598
+ def add_arb_address(xml, container_name, address)
599
+ return if address.blank?
600
+ xml.tag!(container_name) do
601
+ xml.tag!('firstName', address[:first_name])
602
+ xml.tag!('lastName', address[:last_name])
603
+ xml.tag!('company', address[:company])
604
+ xml.tag!('address', address[:address1])
605
+ xml.tag!('city', address[:city])
606
+ xml.tag!('state', address[:state])
607
+ xml.tag!('zip', address[:zip])
608
+ xml.tag!('country', address[:country])
609
+ end
610
+ end
611
+
612
+ def arb_expdate(credit_card)
613
+ sprintf('%04d-%02d', credit_card.year, credit_card.month)
614
+ end
615
+
616
+ def recurring_commit(action, request)
617
+ url = test? ? arb_test_url : arb_live_url
618
+ xml = ssl_post(url, request, "Content-Type" => "text/xml")
619
+
620
+ response = recurring_parse(action, xml)
621
+
622
+ message = response[:message] || response[:text]
623
+ test_mode = test? || message =~ /Test Mode/
624
+ success = response[:result_code] == 'Ok'
625
+
626
+ Response.new(success, message, response,
627
+ :test => test_mode,
628
+ :authorization => response[:subscription_id]
629
+ )
630
+ end
631
+
632
+ def recurring_parse(action, xml)
633
+ response = {}
634
+ xml = REXML::Document.new(xml)
635
+ root = REXML::XPath.first(xml, "//#{RECURRING_ACTIONS[action]}Response") ||
636
+ REXML::XPath.first(xml, "//ErrorResponse")
637
+ if root
638
+ root.elements.to_a.each do |node|
639
+ recurring_parse_element(response, node)
640
+ end
641
+ end
642
+
643
+ response
644
+ end
645
+
646
+ def recurring_parse_element(response, node)
647
+ if node.has_elements?
648
+ node.elements.each{|e| recurring_parse_element(response, e) }
649
+ else
650
+ response[node.name.underscore.to_sym] = node.text
651
+ end
652
+ end
653
+ end
654
+
655
+ AuthorizedNetGateway = AuthorizeNetGateway
656
+ end
657
+ end