merb_merchant 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (273) hide show
  1. data/CHANGELOG +421 -0
  2. data/CONTRIBUTERS +90 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README +131 -0
  5. data/Rakefile +129 -0
  6. data/init.rb +3 -0
  7. data/lib/certs/cacert.pem +7815 -0
  8. data/lib/core_ext/delegating_attributes.rb +76 -0
  9. data/lib/core_ext/hash_ext.rb +35 -0
  10. data/lib/core_ext/inheritable_attributes.rb +145 -0
  11. data/lib/core_ext/time_dsl.rb +59 -0
  12. data/lib/merb_merchant.rb +96 -0
  13. data/lib/merb_merchant/billing/avs_result.rb +95 -0
  14. data/lib/merb_merchant/billing/base.rb +71 -0
  15. data/lib/merb_merchant/billing/check.rb +68 -0
  16. data/lib/merb_merchant/billing/credit_card.rb +157 -0
  17. data/lib/merb_merchant/billing/credit_card_formatting.rb +21 -0
  18. data/lib/merb_merchant/billing/credit_card_methods.rb +125 -0
  19. data/lib/merb_merchant/billing/cvv_result.rb +38 -0
  20. data/lib/merb_merchant/billing/expiry_date.rb +28 -0
  21. data/lib/merb_merchant/billing/gateway.rb +162 -0
  22. data/lib/merb_merchant/billing/gateways.rb +3 -0
  23. data/lib/merb_merchant/billing/gateways/authorize_net.rb +646 -0
  24. data/lib/merb_merchant/billing/gateways/authorize_net_cim.rb +702 -0
  25. data/lib/merb_merchant/billing/gateways/beanstream.rb +102 -0
  26. data/lib/merb_merchant/billing/gateways/beanstream/beanstream_core.rb +233 -0
  27. data/lib/merb_merchant/billing/gateways/beanstream_interac.rb +54 -0
  28. data/lib/merb_merchant/billing/gateways/bogus.rb +98 -0
  29. data/lib/merb_merchant/billing/gateways/braintree.rb +222 -0
  30. data/lib/merb_merchant/billing/gateways/card_stream.rb +229 -0
  31. data/lib/merb_merchant/billing/gateways/cyber_source.rb +406 -0
  32. data/lib/merb_merchant/billing/gateways/data_cash.rb +595 -0
  33. data/lib/merb_merchant/billing/gateways/efsnet.rb +229 -0
  34. data/lib/merb_merchant/billing/gateways/eway.rb +272 -0
  35. data/lib/merb_merchant/billing/gateways/exact.rb +222 -0
  36. data/lib/merb_merchant/billing/gateways/linkpoint.rb +396 -0
  37. data/lib/merb_merchant/billing/gateways/modern_payments.rb +36 -0
  38. data/lib/merb_merchant/billing/gateways/modern_payments_cim.rb +214 -0
  39. data/lib/merb_merchant/billing/gateways/moneris.rb +205 -0
  40. data/lib/merb_merchant/billing/gateways/net_registry.rb +189 -0
  41. data/lib/merb_merchant/billing/gateways/netbilling.rb +168 -0
  42. data/lib/merb_merchant/billing/gateways/pay_junction.rb +392 -0
  43. data/lib/merb_merchant/billing/gateways/pay_secure.rb +120 -0
  44. data/lib/merb_merchant/billing/gateways/payflow.rb +236 -0
  45. data/lib/merb_merchant/billing/gateways/payflow/payflow_common_api.rb +207 -0
  46. data/lib/merb_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
  47. data/lib/merb_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
  48. data/lib/merb_merchant/billing/gateways/payflow_express.rb +138 -0
  49. data/lib/merb_merchant/billing/gateways/payflow_express_uk.rb +15 -0
  50. data/lib/merb_merchant/billing/gateways/payflow_uk.rb +21 -0
  51. data/lib/merb_merchant/billing/gateways/payment_express.rb +241 -0
  52. data/lib/merb_merchant/billing/gateways/paypal.rb +108 -0
  53. data/lib/merb_merchant/billing/gateways/paypal/paypal_common_api.rb +325 -0
  54. data/lib/merb_merchant/billing/gateways/paypal/paypal_express_response.rb +38 -0
  55. data/lib/merb_merchant/billing/gateways/paypal_ca.rb +13 -0
  56. data/lib/merb_merchant/billing/gateways/paypal_express.rb +130 -0
  57. data/lib/merb_merchant/billing/gateways/paypal_express_common.rb +20 -0
  58. data/lib/merb_merchant/billing/gateways/plugnpay.rb +292 -0
  59. data/lib/merb_merchant/billing/gateways/protx.rb +284 -0
  60. data/lib/merb_merchant/billing/gateways/psigate.rb +214 -0
  61. data/lib/merb_merchant/billing/gateways/psl_card.rb +306 -0
  62. data/lib/merb_merchant/billing/gateways/quickpay.rb +213 -0
  63. data/lib/merb_merchant/billing/gateways/realex.rb +200 -0
  64. data/lib/merb_merchant/billing/gateways/sage.rb +146 -0
  65. data/lib/merb_merchant/billing/gateways/sage/sage_bankcard.rb +88 -0
  66. data/lib/merb_merchant/billing/gateways/sage/sage_core.rb +110 -0
  67. data/lib/merb_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
  68. data/lib/merb_merchant/billing/gateways/secure_pay.rb +31 -0
  69. data/lib/merb_merchant/billing/gateways/secure_pay_au.rb +157 -0
  70. data/lib/merb_merchant/billing/gateways/secure_pay_tech.rb +113 -0
  71. data/lib/merb_merchant/billing/gateways/skip_jack.rb +439 -0
  72. data/lib/merb_merchant/billing/gateways/trans_first.rb +127 -0
  73. data/lib/merb_merchant/billing/gateways/trust_commerce.rb +418 -0
  74. data/lib/merb_merchant/billing/gateways/usa_epay.rb +194 -0
  75. data/lib/merb_merchant/billing/gateways/verifi.rb +228 -0
  76. data/lib/merb_merchant/billing/gateways/viaklix.rb +165 -0
  77. data/lib/merb_merchant/billing/gateways/wirecard.rb +305 -0
  78. data/lib/merb_merchant/billing/integrations.rb +22 -0
  79. data/lib/merb_merchant/billing/integrations/action_view_helper.rb +79 -0
  80. data/lib/merb_merchant/billing/integrations/bogus.rb +29 -0
  81. data/lib/merb_merchant/billing/integrations/bogus/helper.rb +17 -0
  82. data/lib/merb_merchant/billing/integrations/bogus/notification.rb +11 -0
  83. data/lib/merb_merchant/billing/integrations/bogus/return.rb +10 -0
  84. data/lib/merb_merchant/billing/integrations/chronopay.rb +29 -0
  85. data/lib/merb_merchant/billing/integrations/chronopay/helper.rb +81 -0
  86. data/lib/merb_merchant/billing/integrations/chronopay/notification.rb +156 -0
  87. data/lib/merb_merchant/billing/integrations/chronopay/return.rb +10 -0
  88. data/lib/merb_merchant/billing/integrations/gestpay.rb +32 -0
  89. data/lib/merb_merchant/billing/integrations/gestpay/common.rb +42 -0
  90. data/lib/merb_merchant/billing/integrations/gestpay/helper.rb +70 -0
  91. data/lib/merb_merchant/billing/integrations/gestpay/notification.rb +83 -0
  92. data/lib/merb_merchant/billing/integrations/gestpay/return.rb +10 -0
  93. data/lib/merb_merchant/billing/integrations/helper.rb +93 -0
  94. data/lib/merb_merchant/billing/integrations/hi_trust.rb +26 -0
  95. data/lib/merb_merchant/billing/integrations/hi_trust/helper.rb +58 -0
  96. data/lib/merb_merchant/billing/integrations/hi_trust/notification.rb +59 -0
  97. data/lib/merb_merchant/billing/integrations/hi_trust/return.rb +67 -0
  98. data/lib/merb_merchant/billing/integrations/nochex.rb +101 -0
  99. data/lib/merb_merchant/billing/integrations/nochex/helper.rb +68 -0
  100. data/lib/merb_merchant/billing/integrations/nochex/notification.rb +94 -0
  101. data/lib/merb_merchant/billing/integrations/nochex/return.rb +10 -0
  102. data/lib/merb_merchant/billing/integrations/notification.rb +62 -0
  103. data/lib/merb_merchant/billing/integrations/paypal.rb +54 -0
  104. data/lib/merb_merchant/billing/integrations/paypal/helper.rb +118 -0
  105. data/lib/merb_merchant/billing/integrations/paypal/notification.rb +154 -0
  106. data/lib/merb_merchant/billing/integrations/paypal/return.rb +10 -0
  107. data/lib/merb_merchant/billing/integrations/quickpay.rb +25 -0
  108. data/lib/merb_merchant/billing/integrations/quickpay/helper.rb +72 -0
  109. data/lib/merb_merchant/billing/integrations/quickpay/notification.rb +74 -0
  110. data/lib/merb_merchant/billing/integrations/return.rb +35 -0
  111. data/lib/merb_merchant/billing/integrations/two_checkout.rb +30 -0
  112. data/lib/merb_merchant/billing/integrations/two_checkout/helper.rb +59 -0
  113. data/lib/merb_merchant/billing/integrations/two_checkout/notification.rb +114 -0
  114. data/lib/merb_merchant/billing/integrations/two_checkout/return.rb +17 -0
  115. data/lib/merb_merchant/billing/response.rb +32 -0
  116. data/lib/merb_merchant/lib/country.rb +298 -0
  117. data/lib/merb_merchant/lib/error.rb +4 -0
  118. data/lib/merb_merchant/lib/inflector.rb +69 -0
  119. data/lib/merb_merchant/lib/post_data.rb +22 -0
  120. data/lib/merb_merchant/lib/posts_data.rb +109 -0
  121. data/lib/merb_merchant/lib/requires_parameters.rb +16 -0
  122. data/lib/merb_merchant/lib/utils.rb +18 -0
  123. data/lib/merb_merchant/lib/validateable.rb +76 -0
  124. data/lib/support/gateway_support.rb +57 -0
  125. data/lib/tasks/cia.rb +90 -0
  126. data/script/destroy +14 -0
  127. data/script/generate +14 -0
  128. data/test/extra/binding_of_caller.rb +80 -0
  129. data/test/extra/breakpoint.rb +547 -0
  130. data/test/fixtures.yml +317 -0
  131. data/test/remote/gateways/remote_authorize_net_cim_test.rb +459 -0
  132. data/test/remote/gateways/remote_authorize_net_test.rb +145 -0
  133. data/test/remote/gateways/remote_beanstream_interac_test.rb +53 -0
  134. data/test/remote/gateways/remote_beanstream_test.rb +150 -0
  135. data/test/remote/gateways/remote_braintree_test.rb +154 -0
  136. data/test/remote/gateways/remote_card_stream_test.rb +148 -0
  137. data/test/remote/gateways/remote_cyber_source_test.rb +144 -0
  138. data/test/remote/gateways/remote_data_cash_test.rb +357 -0
  139. data/test/remote/gateways/remote_efsnet_test.rb +81 -0
  140. data/test/remote/gateways/remote_eway_test.rb +74 -0
  141. data/test/remote/gateways/remote_exact_test.rb +60 -0
  142. data/test/remote/gateways/remote_linkpoint_test.rb +112 -0
  143. data/test/remote/gateways/remote_modern_payments_cim_test.rb +58 -0
  144. data/test/remote/gateways/remote_modern_payments_test.rb +43 -0
  145. data/test/remote/gateways/remote_moneris_test.rb +82 -0
  146. data/test/remote/gateways/remote_net_registry_test.rb +85 -0
  147. data/test/remote/gateways/remote_netbilling_test.rb +70 -0
  148. data/test/remote/gateways/remote_pay_junction_test.rb +143 -0
  149. data/test/remote/gateways/remote_pay_secure_test.rb +39 -0
  150. data/test/remote/gateways/remote_payflow_express_test.rb +50 -0
  151. data/test/remote/gateways/remote_payflow_test.rb +237 -0
  152. data/test/remote/gateways/remote_payflow_uk_test.rb +173 -0
  153. data/test/remote/gateways/remote_payment_express_test.rb +126 -0
  154. data/test/remote/gateways/remote_paypal_express_test.rb +49 -0
  155. data/test/remote/gateways/remote_paypal_test.rb +167 -0
  156. data/test/remote/gateways/remote_plugnpay_test.rb +72 -0
  157. data/test/remote/gateways/remote_protx_test.rb +184 -0
  158. data/test/remote/gateways/remote_psigate_test.rb +50 -0
  159. data/test/remote/gateways/remote_psl_card_test.rb +125 -0
  160. data/test/remote/gateways/remote_quickpay_test.rb +190 -0
  161. data/test/remote/gateways/remote_realex_test.rb +224 -0
  162. data/test/remote/gateways/remote_sage_bankcard_test.rb +109 -0
  163. data/test/remote/gateways/remote_sage_test.rb +87 -0
  164. data/test/remote/gateways/remote_sage_virtual_check_test.rb +62 -0
  165. data/test/remote/gateways/remote_secure_pay_au_test.rb +40 -0
  166. data/test/remote/gateways/remote_secure_pay_tech_test.rb +37 -0
  167. data/test/remote/gateways/remote_secure_pay_test.rb +28 -0
  168. data/test/remote/gateways/remote_skipjack_test.rb +105 -0
  169. data/test/remote/gateways/remote_trans_first_test.rb +34 -0
  170. data/test/remote/gateways/remote_trust_commerce_test.rb +152 -0
  171. data/test/remote/gateways/remote_usa_epay_test.rb +46 -0
  172. data/test/remote/gateways/remote_verifi_test.rb +107 -0
  173. data/test/remote/gateways/remote_viaklix_test.rb +43 -0
  174. data/test/remote/gateways/remote_wirecard_test.rb +77 -0
  175. data/test/remote/integrations/remote_gestpay_integration_test.rb +37 -0
  176. data/test/remote/integrations/remote_paypal_integration_test.rb +26 -0
  177. data/test/test_helper.rb +208 -0
  178. data/test/unit/avs_result_test.rb +59 -0
  179. data/test/unit/base_test.rb +55 -0
  180. data/test/unit/check_test.rb +88 -0
  181. data/test/unit/country_code_test.rb +33 -0
  182. data/test/unit/country_test.rb +64 -0
  183. data/test/unit/credit_card_formatting_test.rb +19 -0
  184. data/test/unit/credit_card_methods_test.rb +170 -0
  185. data/test/unit/credit_card_test.rb +311 -0
  186. data/test/unit/cvv_result_test.rb +33 -0
  187. data/test/unit/expiry_date_test.rb +21 -0
  188. data/test/unit/gateways/authorize_net_cim_test.rb +638 -0
  189. data/test/unit/gateways/authorize_net_test.rb +290 -0
  190. data/test/unit/gateways/beanstream_interac_test.rb +51 -0
  191. data/test/unit/gateways/beanstream_test.rb +108 -0
  192. data/test/unit/gateways/bogus_test.rb +46 -0
  193. data/test/unit/gateways/braintree_test.rb +116 -0
  194. data/test/unit/gateways/card_stream_test.rb +91 -0
  195. data/test/unit/gateways/cyber_source_test.rb +188 -0
  196. data/test/unit/gateways/data_cash_test.rb +132 -0
  197. data/test/unit/gateways/efsnet_test.rb +124 -0
  198. data/test/unit/gateways/eway_test.rb +118 -0
  199. data/test/unit/gateways/exact_test.rb +156 -0
  200. data/test/unit/gateways/gateway_test.rb +48 -0
  201. data/test/unit/gateways/linkpoint_test.rb +167 -0
  202. data/test/unit/gateways/modern_payments_cim_test.rb +171 -0
  203. data/test/unit/gateways/moneris_test.rb +158 -0
  204. data/test/unit/gateways/net_registry_test.rb +416 -0
  205. data/test/unit/gateways/netbilling_test.rb +54 -0
  206. data/test/unit/gateways/pay_junction_test.rb +123 -0
  207. data/test/unit/gateways/pay_secure_test.rb +71 -0
  208. data/test/unit/gateways/payflow_express_test.rb +173 -0
  209. data/test/unit/gateways/payflow_express_uk_test.rb +14 -0
  210. data/test/unit/gateways/payflow_test.rb +305 -0
  211. data/test/unit/gateways/payflow_uk_test.rb +30 -0
  212. data/test/unit/gateways/payment_express_test.rb +195 -0
  213. data/test/unit/gateways/paypal_express_test.rb +382 -0
  214. data/test/unit/gateways/paypal_test.rb +520 -0
  215. data/test/unit/gateways/plugnpay_test.rb +86 -0
  216. data/test/unit/gateways/protx_test.rb +122 -0
  217. data/test/unit/gateways/psigate_test.rb +169 -0
  218. data/test/unit/gateways/psl_card_test.rb +64 -0
  219. data/test/unit/gateways/quickpay_test.rb +112 -0
  220. data/test/unit/gateways/realex_test.rb +151 -0
  221. data/test/unit/gateways/sage_bankcard_test.rb +162 -0
  222. data/test/unit/gateways/sage_virtual_check_test.rb +71 -0
  223. data/test/unit/gateways/secure_pay_au_test.rb +207 -0
  224. data/test/unit/gateways/secure_pay_tech_test.rb +44 -0
  225. data/test/unit/gateways/secure_pay_test.rb +87 -0
  226. data/test/unit/gateways/skip_jack_test.rb +133 -0
  227. data/test/unit/gateways/trans_first_test.rb +112 -0
  228. data/test/unit/gateways/trust_commerce_test.rb +90 -0
  229. data/test/unit/gateways/usa_epay_test.rb +128 -0
  230. data/test/unit/gateways/verifi_test.rb +96 -0
  231. data/test/unit/gateways/viaklix_test.rb +78 -0
  232. data/test/unit/gateways/wirecard_test.rb +232 -0
  233. data/test/unit/generators/test_gateway_generator.rb +46 -0
  234. data/test/unit/generators/test_generator_helper.rb +20 -0
  235. data/test/unit/generators/test_integration_generator.rb +53 -0
  236. data/test/unit/integrations/action_view_helper_test.rb +50 -0
  237. data/test/unit/integrations/bogus_module_test.rb +20 -0
  238. data/test/unit/integrations/chronopay_module_test.rb +13 -0
  239. data/test/unit/integrations/gestpay_module_test.rb +14 -0
  240. data/test/unit/integrations/helpers/bogus_helper_test.rb +28 -0
  241. data/test/unit/integrations/helpers/chronopay_helper_test.rb +67 -0
  242. data/test/unit/integrations/helpers/gestpay_helper_test.rb +100 -0
  243. data/test/unit/integrations/helpers/hi_trust_helper_test.rb +16 -0
  244. data/test/unit/integrations/helpers/nochex_helper_test.rb +53 -0
  245. data/test/unit/integrations/helpers/paypal_helper_test.rb +162 -0
  246. data/test/unit/integrations/helpers/quickpay_helper_test.rb +40 -0
  247. data/test/unit/integrations/helpers/two_checkout_helper_test.rb +92 -0
  248. data/test/unit/integrations/hi_trust_module_test.rb +13 -0
  249. data/test/unit/integrations/nochex_module_test.rb +13 -0
  250. data/test/unit/integrations/notifications/chronopay_notification_test.rb +66 -0
  251. data/test/unit/integrations/notifications/gestpay_notification_test.rb +60 -0
  252. data/test/unit/integrations/notifications/hi_trust_notification_test.rb +59 -0
  253. data/test/unit/integrations/notifications/nochex_notification_test.rb +51 -0
  254. data/test/unit/integrations/notifications/notification_test.rb +54 -0
  255. data/test/unit/integrations/notifications/paypal_notification_test.rb +85 -0
  256. data/test/unit/integrations/notifications/quickpay_notification_test.rb +69 -0
  257. data/test/unit/integrations/notifications/two_checkout_notification_test.rb +55 -0
  258. data/test/unit/integrations/paypal_module_test.rb +28 -0
  259. data/test/unit/integrations/quickpay_module_test.rb +9 -0
  260. data/test/unit/integrations/returns/chronopay_return_test.rb +11 -0
  261. data/test/unit/integrations/returns/gestpay_return_test.rb +10 -0
  262. data/test/unit/integrations/returns/hi_trust_return_test.rb +24 -0
  263. data/test/unit/integrations/returns/nochex_return_test.rb +10 -0
  264. data/test/unit/integrations/returns/paypal_return_test.rb +10 -0
  265. data/test/unit/integrations/returns/return_test.rb +11 -0
  266. data/test/unit/integrations/returns/two_checkout_return_test.rb +24 -0
  267. data/test/unit/integrations/two_checkout_module_test.rb +13 -0
  268. data/test/unit/post_data_test.rb +55 -0
  269. data/test/unit/posts_data_test.rb +100 -0
  270. data/test/unit/response_test.rb +28 -0
  271. data/test/unit/utils_test.rb +7 -0
  272. data/test/unit/validateable_test.rb +60 -0
  273. metadata +379 -0
@@ -0,0 +1,108 @@
1
+ require File.dirname(__FILE__) + '/paypal/paypal_common_api'
2
+ require File.dirname(__FILE__) + '/paypal_express'
3
+
4
+ module MerbMerchant #:nodoc:
5
+ module Billing #:nodoc:
6
+ class PaypalGateway < Gateway
7
+ include PaypalCommonAPI
8
+
9
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
10
+ self.supported_countries = ['US']
11
+ self.homepage_url = 'https://www.paypal.com/cgi-bin/webscr?cmd=_wp-pro-overview-outside'
12
+ self.display_name = 'PayPal Website Payments Pro (US)'
13
+
14
+ def authorize(money, credit_card, options = {})
15
+ requires!(options, :ip)
16
+ commit 'DoDirectPayment', build_sale_or_authorization_request('Authorization', money, credit_card, options)
17
+ end
18
+
19
+ def purchase(money, credit_card, options = {})
20
+ requires!(options, :ip)
21
+ commit 'DoDirectPayment', build_sale_or_authorization_request('Sale', money, credit_card, options)
22
+ end
23
+
24
+ def express
25
+ @express ||= PaypalExpressGateway.new(@options)
26
+ end
27
+
28
+ private
29
+ def build_sale_or_authorization_request(action, money, credit_card, options)
30
+ billing_address = options[:billing_address] || options[:address]
31
+ currency_code = options[:currency] || currency(money)
32
+
33
+ xml = Builder::XmlMarkup.new :indent => 2
34
+ xml.tag! 'DoDirectPaymentReq', 'xmlns' => PAYPAL_NAMESPACE do
35
+ xml.tag! 'DoDirectPaymentRequest', 'xmlns:n2' => EBAY_NAMESPACE do
36
+ xml.tag! 'n2:Version', API_VERSION
37
+ xml.tag! 'n2:DoDirectPaymentRequestDetails' do
38
+ xml.tag! 'n2:PaymentAction', action
39
+ xml.tag! 'n2:PaymentDetails' do
40
+ xml.tag! 'n2:OrderTotal', amount(money), 'currencyID' => currency_code
41
+
42
+ # All of the values must be included together and add up to the order total
43
+ if [:subtotal, :shipping, :handling, :tax].all?{ |o| options.has_key?(o) }
44
+ xml.tag! 'n2:ItemTotal', amount(options[:subtotal]), 'currencyID' => currency_code
45
+ xml.tag! 'n2:ShippingTotal', amount(options[:shipping]),'currencyID' => currency_code
46
+ xml.tag! 'n2:HandlingTotal', amount(options[:handling]),'currencyID' => currency_code
47
+ xml.tag! 'n2:TaxTotal', amount(options[:tax]), 'currencyID' => currency_code
48
+ end
49
+
50
+ xml.tag! 'n2:NotifyURL', options[:notify_url]
51
+ xml.tag! 'n2:OrderDescription', options[:description]
52
+ xml.tag! 'n2:InvoiceID', options[:order_id]
53
+ xml.tag! 'n2:ButtonSource', application_id.to_s.slice(0,32) unless application_id.blank?
54
+
55
+ add_address(xml, 'n2:ShipToAddress', options[:shipping_address]) if options[:shipping_address]
56
+ end
57
+ add_credit_card(xml, credit_card, billing_address, options)
58
+ xml.tag! 'n2:IPAddress', options[:ip]
59
+ end
60
+ end
61
+ end
62
+
63
+ xml.target!
64
+ end
65
+
66
+ def add_credit_card(xml, credit_card, address, options)
67
+ xml.tag! 'n2:CreditCard' do
68
+ xml.tag! 'n2:CreditCardType', credit_card_type(card_brand(credit_card))
69
+ xml.tag! 'n2:CreditCardNumber', credit_card.number
70
+ xml.tag! 'n2:ExpMonth', format(credit_card.month, :two_digits)
71
+ xml.tag! 'n2:ExpYear', format(credit_card.year, :four_digits)
72
+ xml.tag! 'n2:CVV2', credit_card.verification_value
73
+
74
+ if [ 'switch', 'solo' ].include?(card_brand(credit_card).to_s)
75
+ xml.tag! 'n2:StartMonth', format(credit_card.start_month, :two_digits) unless credit_card.start_month.blank?
76
+ xml.tag! 'n2:StartYear', format(credit_card.start_year, :four_digits) unless credit_card.start_year.blank?
77
+ xml.tag! 'n2:IssueNumber', format(credit_card.issue_number, :two_digits) unless credit_card.issue_number.blank?
78
+ end
79
+
80
+ xml.tag! 'n2:CardOwner' do
81
+ xml.tag! 'n2:PayerName' do
82
+ xml.tag! 'n2:FirstName', credit_card.first_name
83
+ xml.tag! 'n2:LastName', credit_card.last_name
84
+ end
85
+
86
+ xml.tag! 'n2:Payer', options[:email]
87
+ add_address(xml, 'n2:Address', address)
88
+ end
89
+ end
90
+ end
91
+
92
+ def credit_card_type(type)
93
+ case type
94
+ when 'visa' then 'Visa'
95
+ when 'master' then 'MasterCard'
96
+ when 'discover' then 'Discover'
97
+ when 'american_express' then 'Amex'
98
+ when 'switch' then 'Switch'
99
+ when 'solo' then 'Solo'
100
+ end
101
+ end
102
+
103
+ def build_response(success, message, response, options = {})
104
+ Response.new(success, message, response, options)
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,325 @@
1
+ module MerbMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ # This module is included in both PaypalGateway and PaypalExpressGateway
4
+ module PaypalCommonAPI
5
+ def self.included(base)
6
+ base.default_currency = 'USD'
7
+ base.cattr_accessor :pem_file
8
+ base.cattr_accessor :signature
9
+ end
10
+
11
+ API_VERSION = '52.0'
12
+
13
+ URLS = {
14
+ :test => { :certificate => 'https://api.sandbox.paypal.com/2.0/',
15
+ :signature => 'https://api-3t.sandbox.paypal.com/2.0/' },
16
+ :live => { :certificate => 'https://api-aa.paypal.com/2.0/',
17
+ :signature => 'https://api-3t.paypal.com/2.0/' }
18
+ }
19
+
20
+ PAYPAL_NAMESPACE = 'urn:ebay:api:PayPalAPI'
21
+ EBAY_NAMESPACE = 'urn:ebay:apis:eBLBaseComponents'
22
+
23
+ ENVELOPE_NAMESPACES = { 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
24
+ 'xmlns:env' => 'http://schemas.xmlsoap.org/soap/envelope/',
25
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
26
+ }
27
+ CREDENTIALS_NAMESPACES = { 'xmlns' => PAYPAL_NAMESPACE,
28
+ 'xmlns:n1' => EBAY_NAMESPACE,
29
+ 'env:mustUnderstand' => '0'
30
+ }
31
+
32
+ AUSTRALIAN_STATES = {
33
+ 'ACT' => 'Australian Capital Territory',
34
+ 'NSW' => 'New South Wales',
35
+ 'NT' => 'Northern Territory',
36
+ 'QLD' => 'Queensland',
37
+ 'SA' => 'South Australia',
38
+ 'TAS' => 'Tasmania',
39
+ 'VIC' => 'Victoria',
40
+ 'WA' => 'Western Australia'
41
+ }
42
+
43
+ SUCCESS_CODES = [ 'Success', 'SuccessWithWarning' ]
44
+
45
+ FRAUD_REVIEW_CODE = "11610"
46
+
47
+ # The gateway must be configured with either your PayPal PEM file
48
+ # or your PayPal API Signature. Only one is required.
49
+ #
50
+ # <tt>:pem</tt> The text of your PayPal PEM file. Note
51
+ # this is not the path to file, but its
52
+ # contents. If you are only using one PEM
53
+ # file on your site you can declare it
54
+ # globally and then you won't need to
55
+ # include this option
56
+ #
57
+ # <tt>:signature</tt> The text of your PayPal signature.
58
+ # If you are only using one API Signature
59
+ # on your site you can declare it
60
+ # globally and then you won't need to
61
+ # include this option
62
+
63
+ def initialize(options = {})
64
+ requires!(options, :login, :password)
65
+
66
+ @options = {
67
+ :pem => pem_file,
68
+ :signature => signature
69
+ }.update(options)
70
+
71
+ if @options[:pem].blank? && @options[:signature].blank?
72
+ raise ArgumentError, "An API Certificate or API Signature is required to make requests to PayPal"
73
+ end
74
+
75
+ super
76
+ end
77
+
78
+ def test?
79
+ @options[:test] || Base.gateway_mode == :test
80
+ end
81
+
82
+ def reauthorize(money, authorization, options = {})
83
+ commit 'DoReauthorization', build_reauthorize_request(money, authorization, options)
84
+ end
85
+
86
+ def capture(money, authorization, options = {})
87
+ commit 'DoCapture', build_capture_request(money, authorization, options)
88
+ end
89
+
90
+ # Transfer money to one or more recipients.
91
+ #
92
+ # gateway.transfer 1000, 'bob@example.com',
93
+ # :subject => "The money I owe you", :note => "Sorry it's so late"
94
+ #
95
+ # gateway.transfer [1000, 'fred@example.com'],
96
+ # [2450, 'wilma@example.com', :note => 'You will receive another payment on 3/24'],
97
+ # [2000, 'barney@example.com'],
98
+ # :subject => "Your Earnings", :note => "Thanks for your business."
99
+ #
100
+ def transfer(*args)
101
+ commit 'MassPay', build_mass_pay_request(*args)
102
+ end
103
+
104
+ def void(authorization, options = {})
105
+ commit 'DoVoid', build_void_request(authorization, options)
106
+ end
107
+
108
+ def credit(money, identification, options = {})
109
+ commit 'RefundTransaction', build_credit_request(money, identification, options)
110
+ end
111
+
112
+ private
113
+ def build_reauthorize_request(money, authorization, options)
114
+ xml = Builder::XmlMarkup.new
115
+
116
+ xml.tag! 'DoReauthorizationReq', 'xmlns' => PAYPAL_NAMESPACE do
117
+ xml.tag! 'DoReauthorizationRequest', 'xmlns:n2' => EBAY_NAMESPACE do
118
+ xml.tag! 'n2:Version', API_VERSION
119
+ xml.tag! 'AuthorizationID', authorization
120
+ xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
121
+ end
122
+ end
123
+
124
+ xml.target!
125
+ end
126
+
127
+ def build_capture_request(money, authorization, options)
128
+ xml = Builder::XmlMarkup.new
129
+
130
+ xml.tag! 'DoCaptureReq', 'xmlns' => PAYPAL_NAMESPACE do
131
+ xml.tag! 'DoCaptureRequest', 'xmlns:n2' => EBAY_NAMESPACE do
132
+ xml.tag! 'n2:Version', API_VERSION
133
+ xml.tag! 'AuthorizationID', authorization
134
+ xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
135
+ xml.tag! 'CompleteType', 'Complete'
136
+ xml.tag! 'Note', options[:description]
137
+ end
138
+ end
139
+
140
+ xml.target!
141
+ end
142
+
143
+ def build_credit_request(money, identification, options)
144
+ xml = Builder::XmlMarkup.new
145
+
146
+ xml.tag! 'RefundTransactionReq', 'xmlns' => PAYPAL_NAMESPACE do
147
+ xml.tag! 'RefundTransactionRequest', 'xmlns:n2' => EBAY_NAMESPACE do
148
+ xml.tag! 'n2:Version', API_VERSION
149
+ xml.tag! 'TransactionID', identification
150
+ xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
151
+ xml.tag! 'RefundType', 'Partial'
152
+ xml.tag! 'Memo', options[:note] unless options[:note].blank?
153
+ end
154
+ end
155
+
156
+ xml.target!
157
+ end
158
+
159
+ def build_void_request(authorization, options)
160
+ xml = Builder::XmlMarkup.new
161
+
162
+ xml.tag! 'DoVoidReq', 'xmlns' => PAYPAL_NAMESPACE do
163
+ xml.tag! 'DoVoidRequest', 'xmlns:n2' => EBAY_NAMESPACE do
164
+ xml.tag! 'n2:Version', API_VERSION
165
+ xml.tag! 'AuthorizationID', authorization
166
+ xml.tag! 'Note', options[:description]
167
+ end
168
+ end
169
+
170
+ xml.target!
171
+ end
172
+
173
+ def build_mass_pay_request(*args)
174
+ default_options = args.last.is_a?(Hash) ? args.pop : {}
175
+ recipients = args.first.is_a?(Array) ? args : [args]
176
+
177
+ xml = Builder::XmlMarkup.new
178
+
179
+ xml.tag! 'MassPayReq', 'xmlns' => PAYPAL_NAMESPACE do
180
+ xml.tag! 'MassPayRequest', 'xmlns:n2' => EBAY_NAMESPACE do
181
+ xml.tag! 'n2:Version', API_VERSION
182
+ xml.tag! 'EmailSubject', default_options[:subject] if default_options[:subject]
183
+ recipients.each do |money, recipient, options|
184
+ options ||= default_options
185
+ xml.tag! 'MassPayItem' do
186
+ xml.tag! 'ReceiverEmail', recipient
187
+ xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
188
+ xml.tag! 'Note', options[:note] if options[:note]
189
+ xml.tag! 'UniqueId', options[:unique_id] if options[:unique_id]
190
+ end
191
+ end
192
+ end
193
+ end
194
+
195
+ xml.target!
196
+ end
197
+
198
+ def parse(action, xml)
199
+ response = {}
200
+
201
+ error_messages = []
202
+ error_codes = []
203
+
204
+ xml = REXML::Document.new(xml)
205
+ if root = REXML::XPath.first(xml, "//#{action}Response")
206
+ root.elements.each do |node|
207
+ case node.name
208
+ when 'Errors'
209
+ short_message = nil
210
+ long_message = nil
211
+
212
+ node.elements.each do |child|
213
+ case child.name
214
+ when "LongMessage"
215
+ long_message = child.text unless child.text.blank?
216
+ when "ShortMessage"
217
+ short_message = child.text unless child.text.blank?
218
+ when "ErrorCode"
219
+ error_codes << child.text unless child.text.blank?
220
+ end
221
+ end
222
+
223
+ if message = long_message || short_message
224
+ error_messages << message
225
+ end
226
+ else
227
+ parse_element(response, node)
228
+ end
229
+ end
230
+ response[:message] = error_messages.uniq.join(". ") unless error_messages.empty?
231
+ response[:error_codes] = error_codes.uniq.join(",") unless error_codes.empty?
232
+ elsif root = REXML::XPath.first(xml, "//SOAP-ENV:Fault")
233
+ parse_element(response, root)
234
+ response[:message] = "#{response[:faultcode]}: #{response[:faultstring]} - #{response[:detail]}"
235
+ end
236
+
237
+ response
238
+ end
239
+
240
+ def parse_element(response, node)
241
+ if node.has_elements?
242
+ node.elements.each{|e| parse_element(response, e) }
243
+ else
244
+ response[MerbMerchant::Inflector.underscore(node.name).to_sym] = node.text
245
+ node.attributes.each do |k, v|
246
+ response["#{MerbMerchant::Inflector.underscore(node.name)}_#{MerbMerchant::Inflector.underscore(k)}".to_sym] = v if k == 'currencyID'
247
+ end
248
+ end
249
+ end
250
+
251
+ def build_request(body)
252
+ xml = Builder::XmlMarkup.new
253
+
254
+ xml.instruct!
255
+ xml.tag! 'env:Envelope', ENVELOPE_NAMESPACES do
256
+ xml.tag! 'env:Header' do
257
+ add_credentials(xml)
258
+ end
259
+
260
+ xml.tag! 'env:Body' do
261
+ xml << body
262
+ end
263
+ end
264
+ xml.target!
265
+ end
266
+
267
+ def add_credentials(xml)
268
+ xml.tag! 'RequesterCredentials', CREDENTIALS_NAMESPACES do
269
+ xml.tag! 'n1:Credentials' do
270
+ xml.tag! 'Username', @options[:login]
271
+ xml.tag! 'Password', @options[:password]
272
+ xml.tag! 'Subject', @options[:subject]
273
+ xml.tag! 'Signature', @options[:signature] unless @options[:signature].blank?
274
+ end
275
+ end
276
+ end
277
+
278
+ def add_address(xml, element, address)
279
+ return if address.nil?
280
+ xml.tag! element do
281
+ xml.tag! 'n2:Name', address[:name]
282
+ xml.tag! 'n2:Street1', address[:address1]
283
+ xml.tag! 'n2:Street2', address[:address2]
284
+ xml.tag! 'n2:CityName', address[:city]
285
+ xml.tag! 'n2:StateOrProvince', address[:state].blank? ? 'N/A' : address[:state]
286
+ xml.tag! 'n2:Country', address[:country]
287
+ xml.tag! 'n2:PostalCode', address[:zip]
288
+ xml.tag! 'n2:Phone', address[:phone]
289
+ end
290
+ end
291
+
292
+ def endpoint_url
293
+ URLS[test? ? :test : :live][@options[:signature].blank? ? :certificate : :signature]
294
+ end
295
+
296
+ def commit(action, request)
297
+ response = parse(action, ssl_post(endpoint_url, build_request(request)))
298
+
299
+ build_response(successful?(response), message_from(response), response,
300
+ :test => test?,
301
+ :authorization => authorization_from(response),
302
+ :fraud_review => fraud_review?(response),
303
+ :avs_result => { :code => response[:avs_code] },
304
+ :cvv_result => response[:cvv2_code]
305
+ )
306
+ end
307
+
308
+ def fraud_review?(response)
309
+ response[:error_codes] == FRAUD_REVIEW_CODE
310
+ end
311
+
312
+ def authorization_from(response)
313
+ response[:transaction_id] || response[:authorization_id] || response[:refund_transaction_id] # middle one is from reauthorization
314
+ end
315
+
316
+ def successful?(response)
317
+ SUCCESS_CODES.include?(response[:ack])
318
+ end
319
+
320
+ def message_from(response)
321
+ response[:message] || response[:ack]
322
+ end
323
+ end
324
+ end
325
+ end
@@ -0,0 +1,38 @@
1
+ module MerbMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class PaypalExpressResponse < Response
4
+ def email
5
+ @params['payer']
6
+ end
7
+
8
+ def name
9
+ [@params['first_name'], @params['middle_name'], @params['last_name']].compact.join(' ')
10
+ end
11
+
12
+ def token
13
+ @params['token']
14
+ end
15
+
16
+ def payer_id
17
+ @params['payer_id']
18
+ end
19
+
20
+ def payer_country
21
+ @params['payer_country']
22
+ end
23
+
24
+ def address
25
+ { 'name' => @params['name'],
26
+ 'company' => @params['payer_business'],
27
+ 'address1' => @params['street1'],
28
+ 'address2' => @params['street2'],
29
+ 'city' => @params['city_name'],
30
+ 'state' => @params['state_or_province'],
31
+ 'country' => @params['country'],
32
+ 'zip' => @params['postal_code'],
33
+ 'phone' => nil
34
+ }
35
+ end
36
+ end
37
+ end
38
+ end