activemerchant-nsp 1.27.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (252) hide show
  1. data/CHANGELOG +952 -0
  2. data/CONTRIBUTORS +347 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +204 -0
  5. data/gem-public_cert.pem +20 -0
  6. data/lib/active_merchant.rb +63 -0
  7. data/lib/active_merchant/billing.rb +9 -0
  8. data/lib/active_merchant/billing/avs_result.rb +98 -0
  9. data/lib/active_merchant/billing/base.rb +56 -0
  10. data/lib/active_merchant/billing/check.rb +68 -0
  11. data/lib/active_merchant/billing/credit_card.rb +274 -0
  12. data/lib/active_merchant/billing/credit_card_formatting.rb +21 -0
  13. data/lib/active_merchant/billing/credit_card_methods.rb +143 -0
  14. data/lib/active_merchant/billing/cvv_result.rb +38 -0
  15. data/lib/active_merchant/billing/expiry_date.rb +34 -0
  16. data/lib/active_merchant/billing/gateway.rb +176 -0
  17. data/lib/active_merchant/billing/gateways.rb +18 -0
  18. data/lib/active_merchant/billing/gateways/authorize_net.rb +695 -0
  19. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +943 -0
  20. data/lib/active_merchant/billing/gateways/balanced.rb +462 -0
  21. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +308 -0
  22. data/lib/active_merchant/billing/gateways/beanstream.rb +167 -0
  23. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +389 -0
  24. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
  25. data/lib/active_merchant/billing/gateways/blue_pay.rb +492 -0
  26. data/lib/active_merchant/billing/gateways/bogus.rb +142 -0
  27. data/lib/active_merchant/billing/gateways/braintree.rb +19 -0
  28. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
  29. data/lib/active_merchant/billing/gateways/braintree_blue.rb +389 -0
  30. data/lib/active_merchant/billing/gateways/braintree_orange.rb +19 -0
  31. data/lib/active_merchant/billing/gateways/card_save.rb +23 -0
  32. data/lib/active_merchant/billing/gateways/card_stream.rb +230 -0
  33. data/lib/active_merchant/billing/gateways/certo_direct.rb +278 -0
  34. data/lib/active_merchant/billing/gateways/cyber_source.rb +576 -0
  35. data/lib/active_merchant/billing/gateways/data_cash.rb +597 -0
  36. data/lib/active_merchant/billing/gateways/efsnet.rb +235 -0
  37. data/lib/active_merchant/billing/gateways/elavon.rb +137 -0
  38. data/lib/active_merchant/billing/gateways/epay.rb +276 -0
  39. data/lib/active_merchant/billing/gateways/eway.rb +287 -0
  40. data/lib/active_merchant/billing/gateways/eway_managed.rb +265 -0
  41. data/lib/active_merchant/billing/gateways/exact.rb +227 -0
  42. data/lib/active_merchant/billing/gateways/fat_zebra.rb +152 -0
  43. data/lib/active_merchant/billing/gateways/federated_canada.rb +168 -0
  44. data/lib/active_merchant/billing/gateways/first_pay.rb +177 -0
  45. data/lib/active_merchant/billing/gateways/garanti.rb +262 -0
  46. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +252 -0
  47. data/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem +13 -0
  48. data/lib/active_merchant/billing/gateways/ideal/ideal_response.rb +29 -0
  49. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +55 -0
  50. data/lib/active_merchant/billing/gateways/inspire.rb +221 -0
  51. data/lib/active_merchant/billing/gateways/instapay.rb +164 -0
  52. data/lib/active_merchant/billing/gateways/iridium.rb +257 -0
  53. data/lib/active_merchant/billing/gateways/itransact.rb +450 -0
  54. data/lib/active_merchant/billing/gateways/jetpay.rb +276 -0
  55. data/lib/active_merchant/billing/gateways/linkpoint.rb +451 -0
  56. data/lib/active_merchant/billing/gateways/litle.rb +290 -0
  57. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +157 -0
  58. data/lib/active_merchant/billing/gateways/merchant_ware.rb +289 -0
  59. data/lib/active_merchant/billing/gateways/metrics_global.rb +323 -0
  60. data/lib/active_merchant/billing/gateways/migs.rb +263 -0
  61. data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +100 -0
  62. data/lib/active_merchant/billing/gateways/modern_payments.rb +38 -0
  63. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +220 -0
  64. data/lib/active_merchant/billing/gateways/moneris.rb +250 -0
  65. data/lib/active_merchant/billing/gateways/moneris_us.rb +211 -0
  66. data/lib/active_merchant/billing/gateways/nab_transact.rb +257 -0
  67. data/lib/active_merchant/billing/gateways/net_registry.rb +189 -0
  68. data/lib/active_merchant/billing/gateways/netaxept.rb +239 -0
  69. data/lib/active_merchant/billing/gateways/netbilling.rb +196 -0
  70. data/lib/active_merchant/billing/gateways/nmi.rb +13 -0
  71. data/lib/active_merchant/billing/gateways/ogone.rb +427 -0
  72. data/lib/active_merchant/billing/gateways/optimal_payment.rb +277 -0
  73. data/lib/active_merchant/billing/gateways/orbital.rb +351 -0
  74. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +46 -0
  75. data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +266 -0
  76. data/lib/active_merchant/billing/gateways/pay_junction.rb +397 -0
  77. data/lib/active_merchant/billing/gateways/pay_secure.rb +120 -0
  78. data/lib/active_merchant/billing/gateways/paybox_direct.rb +201 -0
  79. data/lib/active_merchant/billing/gateways/payflow.rb +268 -0
  80. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +212 -0
  81. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
  82. data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
  83. data/lib/active_merchant/billing/gateways/payflow_express.rb +224 -0
  84. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
  85. data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
  86. data/lib/active_merchant/billing/gateways/payment_express.rb +282 -0
  87. data/lib/active_merchant/billing/gateways/paypal.rb +106 -0
  88. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +653 -0
  89. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +49 -0
  90. data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +245 -0
  91. data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
  92. data/lib/active_merchant/billing/gateways/paypal_digital_goods.rb +43 -0
  93. data/lib/active_merchant/billing/gateways/paypal_express.rb +178 -0
  94. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +30 -0
  95. data/lib/active_merchant/billing/gateways/paystation.rb +201 -0
  96. data/lib/active_merchant/billing/gateways/payway.rb +214 -0
  97. data/lib/active_merchant/billing/gateways/plugnpay.rb +295 -0
  98. data/lib/active_merchant/billing/gateways/psigate.rb +219 -0
  99. data/lib/active_merchant/billing/gateways/psl_card.rb +304 -0
  100. data/lib/active_merchant/billing/gateways/qbms.rb +297 -0
  101. data/lib/active_merchant/billing/gateways/quantum.rb +282 -0
  102. data/lib/active_merchant/billing/gateways/quickpay.rb +298 -0
  103. data/lib/active_merchant/billing/gateways/realex.rb +313 -0
  104. data/lib/active_merchant/billing/gateways/sage.rb +148 -0
  105. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +88 -0
  106. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +115 -0
  107. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
  108. data/lib/active_merchant/billing/gateways/sage_pay.rb +322 -0
  109. data/lib/active_merchant/billing/gateways/sallie_mae.rb +144 -0
  110. data/lib/active_merchant/billing/gateways/samurai.rb +121 -0
  111. data/lib/active_merchant/billing/gateways/secure_net.rb +330 -0
  112. data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
  113. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +284 -0
  114. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +113 -0
  115. data/lib/active_merchant/billing/gateways/skip_jack.rb +458 -0
  116. data/lib/active_merchant/billing/gateways/smart_ps.rb +273 -0
  117. data/lib/active_merchant/billing/gateways/stripe.rb +236 -0
  118. data/lib/active_merchant/billing/gateways/trans_first.rb +127 -0
  119. data/lib/active_merchant/billing/gateways/transax.rb +23 -0
  120. data/lib/active_merchant/billing/gateways/trust_commerce.rb +423 -0
  121. data/lib/active_merchant/billing/gateways/usa_epay.rb +25 -0
  122. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +1497 -0
  123. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +206 -0
  124. data/lib/active_merchant/billing/gateways/verifi.rb +233 -0
  125. data/lib/active_merchant/billing/gateways/viaklix.rb +194 -0
  126. data/lib/active_merchant/billing/gateways/vindicia.rb +359 -0
  127. data/lib/active_merchant/billing/gateways/wirecard.rb +313 -0
  128. data/lib/active_merchant/billing/gateways/worldpay.rb +271 -0
  129. data/lib/active_merchant/billing/integrations.rb +17 -0
  130. data/lib/active_merchant/billing/integrations/action_view_helper.rb +73 -0
  131. data/lib/active_merchant/billing/integrations/authorize_net_sim.rb +38 -0
  132. data/lib/active_merchant/billing/integrations/authorize_net_sim/helper.rb +228 -0
  133. data/lib/active_merchant/billing/integrations/authorize_net_sim/notification.rb +340 -0
  134. data/lib/active_merchant/billing/integrations/bogus.rb +23 -0
  135. data/lib/active_merchant/billing/integrations/bogus/helper.rb +17 -0
  136. data/lib/active_merchant/billing/integrations/bogus/notification.rb +11 -0
  137. data/lib/active_merchant/billing/integrations/bogus/return.rb +10 -0
  138. data/lib/active_merchant/billing/integrations/chronopay.rb +23 -0
  139. data/lib/active_merchant/billing/integrations/chronopay/helper.rb +120 -0
  140. data/lib/active_merchant/billing/integrations/chronopay/notification.rb +158 -0
  141. data/lib/active_merchant/billing/integrations/chronopay/return.rb +10 -0
  142. data/lib/active_merchant/billing/integrations/direc_pay.rb +41 -0
  143. data/lib/active_merchant/billing/integrations/direc_pay/helper.rb +200 -0
  144. data/lib/active_merchant/billing/integrations/direc_pay/notification.rb +76 -0
  145. data/lib/active_merchant/billing/integrations/direc_pay/return.rb +32 -0
  146. data/lib/active_merchant/billing/integrations/direc_pay/status.rb +37 -0
  147. data/lib/active_merchant/billing/integrations/directebanking.rb +47 -0
  148. data/lib/active_merchant/billing/integrations/directebanking/helper.rb +90 -0
  149. data/lib/active_merchant/billing/integrations/directebanking/notification.rb +120 -0
  150. data/lib/active_merchant/billing/integrations/directebanking/return.rb +11 -0
  151. data/lib/active_merchant/billing/integrations/dotpay.rb +22 -0
  152. data/lib/active_merchant/billing/integrations/dotpay/helper.rb +77 -0
  153. data/lib/active_merchant/billing/integrations/dotpay/notification.rb +86 -0
  154. data/lib/active_merchant/billing/integrations/dotpay/return.rb +11 -0
  155. data/lib/active_merchant/billing/integrations/dwolla.rb +30 -0
  156. data/lib/active_merchant/billing/integrations/dwolla/helper.rb +31 -0
  157. data/lib/active_merchant/billing/integrations/dwolla/notification.rb +55 -0
  158. data/lib/active_merchant/billing/integrations/dwolla/return.rb +38 -0
  159. data/lib/active_merchant/billing/integrations/e_payment_plans.rb +48 -0
  160. data/lib/active_merchant/billing/integrations/e_payment_plans/helper.rb +34 -0
  161. data/lib/active_merchant/billing/integrations/e_payment_plans/notification.rb +84 -0
  162. data/lib/active_merchant/billing/integrations/easy_pay.rb +30 -0
  163. data/lib/active_merchant/billing/integrations/easy_pay/common.rb +40 -0
  164. data/lib/active_merchant/billing/integrations/easy_pay/helper.rb +40 -0
  165. data/lib/active_merchant/billing/integrations/easy_pay/notification.rb +51 -0
  166. data/lib/active_merchant/billing/integrations/epay.rb +21 -0
  167. data/lib/active_merchant/billing/integrations/epay/helper.rb +55 -0
  168. data/lib/active_merchant/billing/integrations/epay/notification.rb +110 -0
  169. data/lib/active_merchant/billing/integrations/first_data.rb +38 -0
  170. data/lib/active_merchant/billing/integrations/first_data/helper.rb +63 -0
  171. data/lib/active_merchant/billing/integrations/first_data/notification.rb +56 -0
  172. data/lib/active_merchant/billing/integrations/gestpay.rb +25 -0
  173. data/lib/active_merchant/billing/integrations/gestpay/common.rb +42 -0
  174. data/lib/active_merchant/billing/integrations/gestpay/helper.rb +70 -0
  175. data/lib/active_merchant/billing/integrations/gestpay/notification.rb +85 -0
  176. data/lib/active_merchant/billing/integrations/gestpay/return.rb +10 -0
  177. data/lib/active_merchant/billing/integrations/helper.rb +117 -0
  178. data/lib/active_merchant/billing/integrations/hi_trust.rb +27 -0
  179. data/lib/active_merchant/billing/integrations/hi_trust/helper.rb +58 -0
  180. data/lib/active_merchant/billing/integrations/hi_trust/notification.rb +59 -0
  181. data/lib/active_merchant/billing/integrations/hi_trust/return.rb +67 -0
  182. data/lib/active_merchant/billing/integrations/maksuturva.rb +86 -0
  183. data/lib/active_merchant/billing/integrations/maksuturva/helper.rb +119 -0
  184. data/lib/active_merchant/billing/integrations/maksuturva/notification.rb +48 -0
  185. data/lib/active_merchant/billing/integrations/moneybookers.rb +26 -0
  186. data/lib/active_merchant/billing/integrations/moneybookers/helper.rb +59 -0
  187. data/lib/active_merchant/billing/integrations/moneybookers/notification.rb +129 -0
  188. data/lib/active_merchant/billing/integrations/nelnet.rb +40 -0
  189. data/lib/active_merchant/billing/integrations/nelnet/helper.rb +34 -0
  190. data/lib/active_merchant/billing/integrations/nelnet/notification.rb +100 -0
  191. data/lib/active_merchant/billing/integrations/nochex.rb +88 -0
  192. data/lib/active_merchant/billing/integrations/nochex/helper.rb +68 -0
  193. data/lib/active_merchant/billing/integrations/nochex/notification.rb +94 -0
  194. data/lib/active_merchant/billing/integrations/nochex/return.rb +10 -0
  195. data/lib/active_merchant/billing/integrations/notification.rb +62 -0
  196. data/lib/active_merchant/billing/integrations/paxum.rb +44 -0
  197. data/lib/active_merchant/billing/integrations/paxum/common.rb +24 -0
  198. data/lib/active_merchant/billing/integrations/paxum/helper.rb +42 -0
  199. data/lib/active_merchant/billing/integrations/paxum/notification.rb +33 -0
  200. data/lib/active_merchant/billing/integrations/payflow_link.rb +21 -0
  201. data/lib/active_merchant/billing/integrations/payflow_link/helper.rb +100 -0
  202. data/lib/active_merchant/billing/integrations/payflow_link/notification.rb +78 -0
  203. data/lib/active_merchant/billing/integrations/paypal.rb +39 -0
  204. data/lib/active_merchant/billing/integrations/paypal/helper.rb +119 -0
  205. data/lib/active_merchant/billing/integrations/paypal/notification.rb +155 -0
  206. data/lib/active_merchant/billing/integrations/paypal/return.rb +10 -0
  207. data/lib/active_merchant/billing/integrations/paypal_payments_advanced.rb +20 -0
  208. data/lib/active_merchant/billing/integrations/paypal_payments_advanced/helper.rb +15 -0
  209. data/lib/active_merchant/billing/integrations/pxpay.rb +31 -0
  210. data/lib/active_merchant/billing/integrations/pxpay/helper.rb +110 -0
  211. data/lib/active_merchant/billing/integrations/pxpay/notification.rb +157 -0
  212. data/lib/active_merchant/billing/integrations/pxpay/return.rb +25 -0
  213. data/lib/active_merchant/billing/integrations/quickpay.rb +21 -0
  214. data/lib/active_merchant/billing/integrations/quickpay/helper.rb +71 -0
  215. data/lib/active_merchant/billing/integrations/quickpay/notification.rb +74 -0
  216. data/lib/active_merchant/billing/integrations/return.rb +42 -0
  217. data/lib/active_merchant/billing/integrations/robokassa.rb +49 -0
  218. data/lib/active_merchant/billing/integrations/robokassa/common.rb +19 -0
  219. data/lib/active_merchant/billing/integrations/robokassa/helper.rb +50 -0
  220. data/lib/active_merchant/billing/integrations/robokassa/notification.rb +55 -0
  221. data/lib/active_merchant/billing/integrations/robokassa/return.rb +17 -0
  222. data/lib/active_merchant/billing/integrations/sage_pay_form.rb +37 -0
  223. data/lib/active_merchant/billing/integrations/sage_pay_form/encryption.rb +33 -0
  224. data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +129 -0
  225. data/lib/active_merchant/billing/integrations/sage_pay_form/notification.rb +210 -0
  226. data/lib/active_merchant/billing/integrations/sage_pay_form/return.rb +31 -0
  227. data/lib/active_merchant/billing/integrations/two_checkout.rb +44 -0
  228. data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +91 -0
  229. data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +139 -0
  230. data/lib/active_merchant/billing/integrations/two_checkout/return.rb +17 -0
  231. data/lib/active_merchant/billing/integrations/valitor.rb +33 -0
  232. data/lib/active_merchant/billing/integrations/valitor/helper.rb +86 -0
  233. data/lib/active_merchant/billing/integrations/valitor/notification.rb +13 -0
  234. data/lib/active_merchant/billing/integrations/valitor/response_fields.rb +97 -0
  235. data/lib/active_merchant/billing/integrations/valitor/return.rb +13 -0
  236. data/lib/active_merchant/billing/integrations/verkkomaksut.rb +20 -0
  237. data/lib/active_merchant/billing/integrations/verkkomaksut/helper.rb +87 -0
  238. data/lib/active_merchant/billing/integrations/verkkomaksut/notification.rb +59 -0
  239. data/lib/active_merchant/billing/integrations/web_pay.rb +45 -0
  240. data/lib/active_merchant/billing/integrations/web_pay/common.rb +50 -0
  241. data/lib/active_merchant/billing/integrations/web_pay/helper.rb +68 -0
  242. data/lib/active_merchant/billing/integrations/web_pay/notification.rb +51 -0
  243. data/lib/active_merchant/billing/integrations/world_pay.rb +27 -0
  244. data/lib/active_merchant/billing/integrations/world_pay/helper.rb +100 -0
  245. data/lib/active_merchant/billing/integrations/world_pay/notification.rb +160 -0
  246. data/lib/active_merchant/billing/response.rb +64 -0
  247. data/lib/active_merchant/version.rb +3 -0
  248. data/lib/activemerchant.rb +1 -0
  249. data/lib/support/gateway_support.rb +65 -0
  250. data/lib/support/outbound_hosts.rb +25 -0
  251. data/lib/support/ssl_verify.rb +93 -0
  252. metadata +482 -0
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/payflow_express'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class PayflowExpressUkGateway < PayflowExpressGateway
6
+ self.default_currency = 'GBP'
7
+ self.partner = 'PayPalUk'
8
+
9
+ self.supported_countries = ['GB']
10
+ self.homepage_url = 'https://www.paypal.com/uk/cgi-bin/webscr?cmd=_additional-payment-overview-outside'
11
+ self.display_name = 'PayPal Express Checkout (UK)'
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,21 @@
1
+ require File.dirname(__FILE__) + '/payflow'
2
+ require File.dirname(__FILE__) + '/payflow_express_uk'
3
+
4
+ module ActiveMerchant #:nodoc:
5
+ module Billing #:nodoc:
6
+ class PayflowUkGateway < PayflowGateway
7
+ self.default_currency = 'GBP'
8
+ self.partner = 'PayPalUk'
9
+
10
+ def express
11
+ @express ||= PayflowExpressUkGateway.new(@options)
12
+ end
13
+
14
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :solo, :switch]
15
+ self.supported_countries = ['GB']
16
+ self.homepage_url = 'https://www.paypal.com/uk/cgi-bin/webscr?cmd=_wp-pro-overview-outside'
17
+ self.display_name = 'PayPal Website Payments Pro (UK)'
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,282 @@
1
+ require 'rexml/document'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+
6
+ # In NZ DPS supports ANZ, Westpac, National Bank, ASB and BNZ.
7
+ # In Australia DPS supports ANZ, NAB, Westpac, CBA, St George and Bank of South Australia.
8
+ # The Maybank in Malaysia is supported and the Citibank for Singapore.
9
+ class PaymentExpressGateway < Gateway
10
+ self.default_currency = 'NZD'
11
+ # PS supports all major credit cards; Visa, Mastercard, Amex, Diners, BankCard & JCB.
12
+ # Various white label cards can be accepted as well; Farmers, AirNZCard and Elders etc.
13
+ # Please note that not all acquirers and Eftpos networks can support some of these card types.
14
+ # VISA, Mastercard, Diners Club and Farmers cards are supported
15
+ #
16
+ # However, regular accounts with DPS only support VISA and Mastercard
17
+ self.supported_cardtypes = [ :visa, :master, :american_express, :diners_club, :jcb ]
18
+
19
+ self.supported_countries = %w[ AU MY NZ SG ZA GB US ]
20
+
21
+ self.homepage_url = 'http://www.paymentexpress.com/'
22
+ self.display_name = 'PaymentExpress'
23
+
24
+ self.live_url = self.test_url = 'https://sec.paymentexpress.com/pxpost.aspx'
25
+
26
+ APPROVED = '1'
27
+
28
+ TRANSACTIONS = {
29
+ :purchase => 'Purchase',
30
+ :credit => 'Refund',
31
+ :authorization => 'Auth',
32
+ :capture => 'Complete',
33
+ :validate => 'Validate'
34
+ }
35
+
36
+ # We require the DPS gateway username and password when the object is created.
37
+ #
38
+ # The PaymentExpress gateway also supports a :use_custom_payment_token boolean option.
39
+ # If set to true the gateway will use BillingId for the Token type. If set to false,
40
+ # then the token will be sent as the DPS specified "DpsBillingId". This is per the documentation at
41
+ # http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#Tokenbilling
42
+ def initialize(options = {})
43
+ # A DPS username and password must exist
44
+ requires!(options, :login, :password)
45
+ # Make the options an instance variable
46
+ @options = options
47
+ super
48
+ end
49
+
50
+ # Funds are transferred immediately.
51
+ #
52
+ # `payment_source` can be a usual ActiveMerchant credit_card object, or can also
53
+ # be a string of the `DpsBillingId` or `BillingId` which can be gotten through the
54
+ # store method. If you are using a `BillingId` instead of `DpsBillingId` you must
55
+ # also set the instance method `#use_billing_id_for_token` to true, see the `#store`
56
+ # method for an example of how to do this.
57
+ def purchase(money, payment_source, options = {})
58
+ request = build_purchase_or_authorization_request(money, payment_source, options)
59
+ commit(:purchase, request)
60
+ end
61
+
62
+ # NOTE: Perhaps in options we allow a transaction note to be inserted
63
+ # Verifies that funds are available for the requested card and amount and reserves the specified amount.
64
+ # See: http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#Authcomplete
65
+ #
66
+ # `payment_source` can be a usual ActiveMerchant credit_card object or a token, see #purchased method
67
+ def authorize(money, payment_source, options = {})
68
+ request = build_purchase_or_authorization_request(money, payment_source, options)
69
+ commit(:authorization, request)
70
+ end
71
+
72
+ # Transfer pre-authorized funds immediately
73
+ # See: http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#Authcomplete
74
+ def capture(money, identification, options = {})
75
+ request = build_capture_or_credit_request(money, identification, options)
76
+ commit(:capture, request)
77
+ end
78
+
79
+ # Refund funds to the card holder
80
+ def refund(money, identification, options = {})
81
+ requires!(options, :description)
82
+
83
+ request = build_capture_or_credit_request(money, identification, options)
84
+ commit(:credit, request)
85
+ end
86
+
87
+ def credit(money, identification, options = {})
88
+ deprecated CREDIT_DEPRECATION_MESSAGE
89
+ refund(money, identification, options)
90
+ end
91
+
92
+ # Token Based Billing
93
+ #
94
+ # Instead of storing the credit card details locally, you can store them inside the
95
+ # Payment Express system and instead bill future transactions against a token.
96
+ #
97
+ # This token can either be specified by your code or autogenerated by the PaymentExpress
98
+ # system. The default is to let PaymentExpress generate the token for you and so use
99
+ # the `DpsBillingId`. If you do not pass in any option of the `billing_id`, then the store
100
+ # method will ask PaymentExpress to create a token for you. Additionally, if you are
101
+ # using the default `DpsBillingId`, you do not have to do anything extra in the
102
+ # initialization of your gateway object.
103
+ #
104
+ # To specify and use your own token, you need to do two things.
105
+ #
106
+ # Firstly, pass in a `:billing_id` as an option in the hash of this store method. No
107
+ # validation is done on this BillingId by PaymentExpress so you must ensure that it is unique.
108
+ #
109
+ # gateway.store(credit_card, {:billing_id => 'YourUniqueBillingId'})
110
+ #
111
+ # Secondly, you will need to pass in the option `{:use_custom_payment_token => true}` when
112
+ # initializing your gateway instance, like so:
113
+ #
114
+ # gateway = ActiveMerchant::Billing::PaymentExpressGateway.new(
115
+ # :login => 'USERNAME',
116
+ # :password => 'PASSWORD',
117
+ # :use_custom_payment_token => true
118
+ # )
119
+ #
120
+ # see: http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#Tokenbilling
121
+ #
122
+ # Note, once stored, PaymentExpress does not support unstoring a stored card.
123
+ def store(credit_card, options = {})
124
+ request = build_token_request(credit_card, options)
125
+ commit(:validate, request)
126
+ end
127
+
128
+ private
129
+
130
+ def use_custom_payment_token?
131
+ @options[:use_custom_payment_token]
132
+ end
133
+
134
+ def build_purchase_or_authorization_request(money, payment_source, options)
135
+ result = new_transaction
136
+
137
+ if payment_source.is_a?(String)
138
+ add_billing_token(result, payment_source)
139
+ else
140
+ add_credit_card(result, payment_source)
141
+ end
142
+
143
+ add_amount(result, money, options)
144
+ add_invoice(result, options)
145
+ add_address_verification_data(result, options)
146
+ result
147
+ end
148
+
149
+ def build_capture_or_credit_request(money, identification, options)
150
+ result = new_transaction
151
+
152
+ add_amount(result, money, options)
153
+ add_invoice(result, options)
154
+ add_reference(result, identification)
155
+ result
156
+ end
157
+
158
+ def build_token_request(credit_card, options)
159
+ result = new_transaction
160
+ add_credit_card(result, credit_card)
161
+ add_amount(result, 100, options) #need to make an auth request for $1
162
+ add_token_request(result, options)
163
+ result
164
+ end
165
+
166
+ def add_credentials(xml)
167
+ xml.add_element("PostUsername").text = @options[:login]
168
+ xml.add_element("PostPassword").text = @options[:password]
169
+ end
170
+
171
+ def add_reference(xml, identification)
172
+ xml.add_element("DpsTxnRef").text = identification
173
+ end
174
+
175
+ def add_credit_card(xml, credit_card)
176
+ xml.add_element("CardHolderName").text = credit_card.name
177
+ xml.add_element("CardNumber").text = credit_card.number
178
+ xml.add_element("DateExpiry").text = format_date(credit_card.month, credit_card.year)
179
+
180
+ if credit_card.verification_value?
181
+ xml.add_element("Cvc2").text = credit_card.verification_value
182
+ xml.add_element("Cvc2Presence").text = "1"
183
+ end
184
+
185
+ if requires_start_date_or_issue_number?(credit_card)
186
+ xml.add_element("DateStart").text = format_date(credit_card.start_month, credit_card.start_year) unless credit_card.start_month.blank? || credit_card.start_year.blank?
187
+ xml.add_element("IssueNumber").text = credit_card.issue_number unless credit_card.issue_number.blank?
188
+ end
189
+ end
190
+
191
+ def add_billing_token(xml, token)
192
+ if use_custom_payment_token?
193
+ xml.add_element("BillingId").text = token
194
+ else
195
+ xml.add_element("DpsBillingId").text = token
196
+ end
197
+ end
198
+
199
+ def add_token_request(xml, options)
200
+ xml.add_element("BillingId").text = options[:billing_id] if options[:billing_id]
201
+ xml.add_element("EnableAddBillCard").text = 1
202
+ end
203
+
204
+ def add_amount(xml, money, options)
205
+ xml.add_element("Amount").text = amount(money)
206
+ xml.add_element("InputCurrency").text = options[:currency] || currency(money)
207
+ end
208
+
209
+ def add_transaction_type(xml, action)
210
+ xml.add_element("TxnType").text = TRANSACTIONS[action]
211
+ end
212
+
213
+ def add_invoice(xml, options)
214
+ xml.add_element("TxnId").text = options[:order_id].to_s.slice(0, 16) unless options[:order_id].blank?
215
+ xml.add_element("MerchantReference").text = options[:description] unless options[:description].blank?
216
+ end
217
+
218
+ def add_address_verification_data(xml, options)
219
+ address = options[:billing_address] || options[:address]
220
+ return if address.nil?
221
+
222
+ xml.add_element("EnableAvsData").text = 1
223
+ xml.add_element("AvsAction").text = 1
224
+
225
+ xml.add_element("AvsStreetAddress").text = address[:address1]
226
+ xml.add_element("AvsPostCode").text = address[:zip]
227
+ end
228
+
229
+ def new_transaction
230
+ REXML::Document.new.add_element("Txn")
231
+ end
232
+
233
+ # Take in the request and post it to DPS
234
+ def commit(action, request)
235
+ add_credentials(request)
236
+ add_transaction_type(request, action)
237
+
238
+ # Parse the XML response
239
+ response = parse( ssl_post(self.live_url, request.to_s) )
240
+
241
+ # Return a response
242
+ PaymentExpressResponse.new(response[:success] == APPROVED, response[:card_holder_help_text], response,
243
+ :test => response[:test_mode] == '1',
244
+ :authorization => response[:dps_txn_ref]
245
+ )
246
+ end
247
+
248
+ # Response XML documentation: http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#XMLTxnOutput
249
+ def parse(xml_string)
250
+ response = {}
251
+
252
+ xml = REXML::Document.new(xml_string)
253
+
254
+ # Gather all root elements such as HelpText
255
+ xml.elements.each('Txn/*') do |element|
256
+ response[element.name.underscore.to_sym] = element.text unless element.name == 'Transaction'
257
+ end
258
+
259
+ # Gather all transaction elements and prefix with "account_"
260
+ # So we could access the MerchantResponseText by going
261
+ # response[account_merchant_response_text]
262
+ xml.elements.each('Txn/Transaction/*') do |element|
263
+ response[element.name.underscore.to_sym] = element.text
264
+ end
265
+
266
+ response
267
+ end
268
+
269
+ def format_date(month, year)
270
+ "#{format(month, :two_digits)}#{format(year, :two_digits)}"
271
+ end
272
+ end
273
+
274
+ class PaymentExpressResponse < Response
275
+ # add a method to response so we can easily get the token
276
+ # for Validate transactions
277
+ def token
278
+ @params["billing_id"] || @params["dps_billing_id"]
279
+ end
280
+ end
281
+ end
282
+ end
@@ -0,0 +1,106 @@
1
+ require File.dirname(__FILE__) + '/paypal/paypal_common_api'
2
+ require File.dirname(__FILE__) + '/paypal/paypal_recurring_api'
3
+ require File.dirname(__FILE__) + '/paypal_express'
4
+
5
+ module ActiveMerchant #:nodoc:
6
+ module Billing #:nodoc:
7
+ class PaypalGateway < Gateway
8
+ include PaypalCommonAPI
9
+ include PaypalRecurringApi
10
+
11
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
12
+ self.supported_countries = ['US']
13
+ self.homepage_url = 'https://www.paypal.com/cgi-bin/webscr?cmd=_wp-pro-overview-outside'
14
+ self.display_name = 'PayPal Website Payments Pro (US)'
15
+
16
+ def authorize(money, credit_card_or_referenced_id, options = {})
17
+ requires!(options, :ip)
18
+ commit define_transaction_type(credit_card_or_referenced_id), build_sale_or_authorization_request('Authorization', money, credit_card_or_referenced_id, options)
19
+ end
20
+
21
+ def purchase(money, credit_card_or_referenced_id, options = {})
22
+ requires!(options, :ip)
23
+ commit define_transaction_type(credit_card_or_referenced_id), build_sale_or_authorization_request('Sale', money, credit_card_or_referenced_id, options)
24
+ end
25
+
26
+ def express
27
+ @express ||= PaypalExpressGateway.new(@options)
28
+ end
29
+
30
+ private
31
+
32
+ def define_transaction_type(transaction_arg)
33
+ if transaction_arg.is_a?(String)
34
+ return 'DoReferenceTransaction'
35
+ else
36
+ return 'DoDirectPayment'
37
+ end
38
+ end
39
+
40
+ def build_sale_or_authorization_request(action, money, credit_card_or_referenced_id, options)
41
+ transaction_type = define_transaction_type(credit_card_or_referenced_id)
42
+ reference_id = credit_card_or_referenced_id if transaction_type == "DoReferenceTransaction"
43
+
44
+ billing_address = options[:billing_address] || options[:address]
45
+ currency_code = options[:currency] || currency(money)
46
+
47
+ xml = Builder::XmlMarkup.new :indent => 2
48
+ xml.tag! transaction_type + 'Req', 'xmlns' => PAYPAL_NAMESPACE do
49
+ xml.tag! transaction_type + 'Request', 'xmlns:n2' => EBAY_NAMESPACE do
50
+ xml.tag! 'n2:Version', API_VERSION
51
+ xml.tag! 'n2:' + transaction_type + 'RequestDetails' do
52
+ xml.tag! 'n2:ReferenceID', reference_id if transaction_type == 'DoReferenceTransaction'
53
+ xml.tag! 'n2:PaymentAction', action
54
+ add_payment_details(xml, money, currency_code, options)
55
+ add_credit_card(xml, credit_card_or_referenced_id, billing_address, options) unless transaction_type == 'DoReferenceTransaction'
56
+ xml.tag! 'n2:IPAddress', options[:ip]
57
+ end
58
+ end
59
+ end
60
+
61
+ xml.target!
62
+ end
63
+
64
+ def add_credit_card(xml, credit_card, address, options)
65
+ xml.tag! 'n2:CreditCard' do
66
+ xml.tag! 'n2:CreditCardType', credit_card_type(card_brand(credit_card))
67
+ xml.tag! 'n2:CreditCardNumber', credit_card.number
68
+ xml.tag! 'n2:ExpMonth', format(credit_card.month, :two_digits)
69
+ xml.tag! 'n2:ExpYear', format(credit_card.year, :four_digits)
70
+ xml.tag! 'n2:CVV2', credit_card.verification_value
71
+
72
+ if [ 'switch', 'solo' ].include?(card_brand(credit_card).to_s)
73
+ xml.tag! 'n2:StartMonth', format(credit_card.start_month, :two_digits) unless credit_card.start_month.blank?
74
+ xml.tag! 'n2:StartYear', format(credit_card.start_year, :four_digits) unless credit_card.start_year.blank?
75
+ xml.tag! 'n2:IssueNumber', format(credit_card.issue_number, :two_digits) unless credit_card.issue_number.blank?
76
+ end
77
+
78
+ xml.tag! 'n2:CardOwner' do
79
+ xml.tag! 'n2:PayerName' do
80
+ xml.tag! 'n2:FirstName', credit_card.first_name
81
+ xml.tag! 'n2:LastName', credit_card.last_name
82
+ end
83
+
84
+ xml.tag! 'n2:Payer', options[:email]
85
+ add_address(xml, 'n2:Address', address)
86
+ end
87
+ end
88
+ end
89
+
90
+ def credit_card_type(type)
91
+ case type
92
+ when 'visa' then 'Visa'
93
+ when 'master' then 'MasterCard'
94
+ when 'discover' then 'Discover'
95
+ when 'american_express' then 'Amex'
96
+ when 'switch' then 'Switch'
97
+ when 'solo' then 'Solo'
98
+ end
99
+ end
100
+
101
+ def build_response(success, message, response, options = {})
102
+ Response.new(success, message, response, options)
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,653 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ # This module is included in both PaypalGateway and PaypalExpressGateway
4
+ module PaypalCommonAPI
5
+ API_VERSION = '72'
6
+
7
+ URLS = {
8
+ :test => { :certificate => 'https://api.sandbox.paypal.com/2.0/',
9
+ :signature => 'https://api-3t.sandbox.paypal.com/2.0/' },
10
+ :live => { :certificate => 'https://api-aa.paypal.com/2.0/',
11
+ :signature => 'https://api-3t.paypal.com/2.0/' }
12
+ }
13
+
14
+ PAYPAL_NAMESPACE = 'urn:ebay:api:PayPalAPI'
15
+ EBAY_NAMESPACE = 'urn:ebay:apis:eBLBaseComponents'
16
+
17
+ ENVELOPE_NAMESPACES = { 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
18
+ 'xmlns:env' => 'http://schemas.xmlsoap.org/soap/envelope/',
19
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
20
+ }
21
+ CREDENTIALS_NAMESPACES = { 'xmlns' => PAYPAL_NAMESPACE,
22
+ 'xmlns:n1' => EBAY_NAMESPACE,
23
+ 'env:mustUnderstand' => '0'
24
+ }
25
+
26
+ AUSTRALIAN_STATES = {
27
+ 'ACT' => 'Australian Capital Territory',
28
+ 'NSW' => 'New South Wales',
29
+ 'NT' => 'Northern Territory',
30
+ 'QLD' => 'Queensland',
31
+ 'SA' => 'South Australia',
32
+ 'TAS' => 'Tasmania',
33
+ 'VIC' => 'Victoria',
34
+ 'WA' => 'Western Australia'
35
+ }
36
+
37
+ SUCCESS_CODES = [ 'Success', 'SuccessWithWarning' ]
38
+
39
+ FRAUD_REVIEW_CODE = "11610"
40
+
41
+ def self.included(base)
42
+ base.default_currency = 'USD'
43
+ base.cattr_accessor :pem_file
44
+ base.cattr_accessor :signature
45
+ base.live_url = URLS[:live][:signature]
46
+ base.test_url = URLS[:test][:signature]
47
+ end
48
+
49
+ # The gateway must be configured with either your PayPal PEM file
50
+ # or your PayPal API Signature. Only one is required.
51
+ #
52
+ # <tt>:pem</tt> The text of your PayPal PEM file. Note
53
+ # this is not the path to file, but its
54
+ # contents. If you are only using one PEM
55
+ # file on your site you can declare it
56
+ # globally and then you won't need to
57
+ # include this option
58
+ #
59
+ # <tt>:signature</tt> The text of your PayPal signature.
60
+ # If you are only using one API Signature
61
+ # on your site you can declare it
62
+ # globally and then you won't need to
63
+ # include this option
64
+ def initialize(options = {})
65
+ requires!(options, :login, :password)
66
+
67
+ headers = {'X-PP-AUTHORIZATION' => options.delete(:auth_signature), 'X-PAYPAL-MESSAGE-PROTOCOL' => 'SOAP11'} if options[:auth_signature]
68
+ @options = {
69
+ :pem => pem_file,
70
+ :signature => signature,
71
+ :headers => headers || {}
72
+ }.update(options)
73
+
74
+
75
+ if @options[:pem].blank? && @options[:signature].blank?
76
+ raise ArgumentError, "An API Certificate or API Signature is required to make requests to PayPal"
77
+ end
78
+
79
+ super
80
+ end
81
+
82
+ def test?
83
+ @options[:test] || Base.gateway_mode == :test
84
+ end
85
+
86
+ def reauthorize(money, authorization, options = {})
87
+ commit 'DoReauthorization', build_reauthorize_request(money, authorization, options)
88
+ end
89
+
90
+ def capture(money, authorization, options = {})
91
+ commit 'DoCapture', build_capture_request(money, authorization, options)
92
+ end
93
+
94
+ # Transfer money to one or more recipients.
95
+ #
96
+ # gateway.transfer 1000, 'bob@example.com',
97
+ # :subject => "The money I owe you", :note => "Sorry it's so late"
98
+ #
99
+ # gateway.transfer [1000, 'fred@example.com'],
100
+ # [2450, 'wilma@example.com', :note => 'You will receive another payment on 3/24'],
101
+ # [2000, 'barney@example.com'],
102
+ # :subject => "Your Earnings", :note => "Thanks for your business."
103
+ #
104
+ def transfer(*args)
105
+ commit 'MassPay', build_mass_pay_request(*args)
106
+ end
107
+
108
+ def void(authorization, options = {})
109
+ commit 'DoVoid', build_void_request(authorization, options)
110
+ end
111
+
112
+ # Refunds a transaction.
113
+ #
114
+ # For a full refund pass nil for the amount:
115
+ #
116
+ # gateway.refund nil, 'G39883289DH238'
117
+ #
118
+ # This will automatically make the :refund_type be "Full".
119
+ #
120
+ # For a partial refund just pass the amount as usual:
121
+ #
122
+ # gateway.refund 100, 'UBU83983N920'
123
+ #
124
+ def refund(money, identification, options = {})
125
+ commit 'RefundTransaction', build_refund_request(money, identification, options)
126
+ end
127
+
128
+ def credit(money, identification, options = {})
129
+ deprecated Gateway::CREDIT_DEPRECATION_MESSAGE
130
+ refund(money, identification, options)
131
+ end
132
+
133
+ # ==== For full documentation see {Paypal API Reference:}[https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_soap_r_DoReferenceTransaction]
134
+ # ==== Parameter:
135
+ # * <tt>:money</tt> -- (Required) The amount of this new transaction,
136
+ # required fo the payment details portion of this request
137
+ #
138
+ # ==== Options:
139
+ # * <tt>:reference_id</tt> -- (Required) A transaction ID from a previous purchase, such as a credit card charge using the DoDirectPayment API, or a billing agreement ID.
140
+ # * <tt>:payment_action</tt> -- (Optional) How you want to obtain payment. It is one of the following values:
141
+ #
142
+ # Authorization – This payment is a basic authorization subject to settlement with PayPal Authorization and Capture.
143
+ # Sale – This is a final sale for which you are requesting payment.
144
+ #
145
+ # * <tt>:ip_address</tt> -- (Optional) IP address of the buyer’s browser.
146
+ # Note: PayPal records this IP addresses as a means to detect possible fraud.
147
+ # * <tt>:req_confirm_shipping</tt> -- Whether you require that the buyer’s shipping address on file with PayPal be a confirmed address. You must have permission from PayPal to not require a confirmed address. It is one of the following values:
148
+ #
149
+ # 0 – You do not require that the buyer’s shipping address be a confirmed address.
150
+ # 1 – You require that the buyer’s shipping address be a confirmed address.
151
+ #
152
+ # * <tt>:merchant_session_id</tt> -- (Optional) Your buyer session identification token.
153
+ # * <tt>:return_fmf_details</tt> -- (Optional) Flag to indicate whether you want the results returned by Fraud Management Filters. By default, you do not receive this information. It is one of the following values:
154
+ #
155
+ # 0 – Do not receive FMF details (default)
156
+ # 1 – Receive FMF details
157
+ #
158
+ # * <tt>:soft_descriptor</tt> -- (Optional) Per transaction description of the payment that is passed to the consumer’s credit card statement. If the API request provides a value for the soft descriptor field, the full descriptor displayed on the buyer’s statement has the following format:
159
+ #
160
+ # <PP * | PAYPAL *><Merchant descriptor as set in the Payment Receiving Preferences><1 space><soft descriptor>
161
+ # The soft descriptor can contain only the following characters:
162
+ #
163
+ # Alphanumeric characters
164
+ # - (dash)
165
+ # * (asterisk)
166
+ # . (period)
167
+ # {space}
168
+ #
169
+ def reference_transaction(money, options = {})
170
+ requires!(options, :reference_id)
171
+ commit 'DoReferenceTransaction', build_reference_transaction_request(money, options)
172
+ end
173
+
174
+ def transaction_details(transaction_id)
175
+ commit 'GetTransactionDetails', build_get_transaction_details(transaction_id)
176
+ end
177
+
178
+ # ==== For full documentation see {PayPal API Reference}[https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_soap_r_TransactionSearch]
179
+ # ==== Options:
180
+ # * <tt>:payer </tt> -- (Optional) Search by the buyer’s email address.
181
+ # * <tt>:receipt_id </tt> -- (Optional) Search by the PayPal Account Optional receipt ID.
182
+ # * <tt>:receiver </tt> -- (Optional) Search by the receiver’s email address. If the merchant account has only one email address, this is the primary email. It can also be a non-primary email address.
183
+ # * <tt>:transaction_id</tt> -- (Optional) Search by the transaction ID. The returned results are from the merchant’s transaction records.
184
+ # * <tt>:invoice_id</tt> -- (Optional) Search by invoice identification key, as set by you for the original transaction. This field searches the records for items the merchant sells, not the items purchased.
185
+ # * <tt>:card_number </tt> -- (Optional) Search by credit card number, as set by you for the original transaction. This field searches the records for items the merchant sells, not the items purchased.
186
+ # * <tt>:auction_item_number </tt> -- (Optional) Search by auction item number of the purchased goods.
187
+ # * <tt>:transaction_class </tt> -- (Optional) Search by classification of transaction. Some kinds of possible classes of transactions are not searchable with this field. You cannot search for bank transfer withdrawals, for example. It is one of the following values:
188
+ # All – All transaction classifications
189
+ # Sent – Only payments sent
190
+ # Received – Only payments received
191
+ # MassPay – Only mass payments
192
+ # MoneyRequest – Only money requests
193
+ # FundsAdded – Only funds added to balance
194
+ # FundsWithdrawn – Only funds withdrawn from balance
195
+ # Referral – Only transactions involving referrals
196
+ # Fee – Only transactions involving fees
197
+ # Subscription – Only transactions involving subscriptions
198
+ # Dividend – Only transactions involving dividends
199
+ # Billpay – Only transactions involving BillPay Transactions
200
+ # Refund – Only transactions involving funds
201
+ # CurrencyConversions – Only transactions involving currency conversions
202
+ # BalanceTransfer – Only transactions involving balance transfers
203
+ # Reversal – Only transactions involving BillPay reversals
204
+ # Shipping – Only transactions involving UPS shipping fees
205
+ # BalanceAffecting – Only transactions that affect the account balance
206
+ # ECheck – Only transactions involving eCheck
207
+ #
208
+ # * <tt>:currency_code </tt> -- (Optional) Search by currency code.
209
+ # * <tt>:status</tt> -- (Optional) Search by transaction status. It is one of the following values:
210
+ # One of:
211
+ # Pending – The payment is pending. The specific reason the payment is pending is returned by the GetTransactionDetails API PendingReason field.
212
+ # Processing – The payment is being processed.
213
+ # Success – The payment has been completed and the funds have been added successfully to your account balance.
214
+ # Denied – You denied the payment. This happens only if the payment was previously pending.
215
+ # Reversed – A payment was reversed due to a chargeback or other type of reversal. The funds have been removed from your account balance and returned to the buyer.
216
+ #
217
+ def transaction_search(options)
218
+ requires!(options, :start_date)
219
+ commit 'TransactionSearch', build_transaction_search(options)
220
+ end
221
+
222
+ # ==== Parameters:
223
+ # * <tt>:return_all_currencies</tt> -- Either '1' or '0'
224
+ # 0 – Return only the balance for the primary currency holding.
225
+ # 1 – Return the balance for each currency holding.
226
+ #
227
+ def balance(return_all_currencies = false)
228
+ clean_currency_argument = case return_all_currencies
229
+ when 1, '1' , true; '1'
230
+ else
231
+ '0'
232
+ end
233
+ commit 'GetBalance', build_get_balance(clean_currency_argument)
234
+ end
235
+
236
+ # DoAuthorization takes the transaction_id returned when you call
237
+ # DoExpressCheckoutPayment with a PaymentAction of 'Order'.
238
+ # When you did that, you created an order authorization subject to settlement
239
+ # with PayPal DoAuthorization and DoCapture
240
+ #
241
+ # ==== Parameters:
242
+ # * <tt>:transaction_id</tt> -- The ID returned by DoExpressCheckoutPayment with a PaymentAction of 'Order'.
243
+ # * <tt>:money</tt> -- The amount of money to be authorized for this purchase.
244
+ #
245
+ def authorize_transaction(transaction_id, money, options = {})
246
+ commit 'DoAuthorization', build_do_authorize(transaction_id, money, options)
247
+ end
248
+
249
+ # The ManagePendingTransactionStatus API operation accepts or denys a
250
+ # pending transaction held by Fraud Management Filters.
251
+ #
252
+ # ==== Parameters:
253
+ # * <tt>:transaction_id</tt> -- The ID of the transaction held by Fraud Management Filters.
254
+ # * <tt>:action</tt> -- Either 'Accept' or 'Deny'
255
+ #
256
+ def manage_pending_transaction(transaction_id, action)
257
+ commit 'ManagePendingTransactionStatus', build_manage_pending_transaction_status(transaction_id, action)
258
+ end
259
+
260
+ private
261
+ def build_request_wrapper(action, options = {})
262
+ xml = Builder::XmlMarkup.new :indent => 2
263
+ xml.tag! action + 'Req', 'xmlns' => PAYPAL_NAMESPACE do
264
+ xml.tag! action + 'Request', 'xmlns:n2' => EBAY_NAMESPACE do
265
+ xml.tag! 'n2:Version', API_VERSION
266
+ if options[:request_details]
267
+ xml.tag! 'n2:' + action + 'RequestDetails' do
268
+ yield(xml)
269
+ end
270
+ else
271
+ yield(xml)
272
+ end
273
+ end
274
+ end
275
+ xml.target!
276
+ end
277
+
278
+ def build_do_authorize(transaction_id, money, options = {})
279
+ build_request_wrapper('DoAuthorization') do |xml|
280
+ xml.tag! 'TransactionID', transaction_id
281
+ xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
282
+ end
283
+ end
284
+
285
+ def build_reauthorize_request(money, authorization, options)
286
+ xml = Builder::XmlMarkup.new
287
+
288
+ xml.tag! 'DoReauthorizationReq', 'xmlns' => PAYPAL_NAMESPACE do
289
+ xml.tag! 'DoReauthorizationRequest', 'xmlns:n2' => EBAY_NAMESPACE do
290
+ xml.tag! 'n2:Version', API_VERSION
291
+ xml.tag! 'AuthorizationID', authorization
292
+ xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
293
+ end
294
+ end
295
+
296
+ xml.target!
297
+ end
298
+
299
+ def build_capture_request(money, authorization, options)
300
+ xml = Builder::XmlMarkup.new
301
+
302
+ xml.tag! 'DoCaptureReq', 'xmlns' => PAYPAL_NAMESPACE do
303
+ xml.tag! 'DoCaptureRequest', 'xmlns:n2' => EBAY_NAMESPACE do
304
+ xml.tag! 'n2:Version', API_VERSION
305
+ xml.tag! 'AuthorizationID', authorization
306
+ xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
307
+ xml.tag! 'CompleteType', options[:complete_type] || 'Complete'
308
+ xml.tag! 'InvoiceID', options[:order_id] unless options[:order_id].blank?
309
+ xml.tag! 'Note', options[:description]
310
+ end
311
+ end
312
+
313
+ xml.target!
314
+ end
315
+
316
+ def build_refund_request(money, identification, options)
317
+ xml = Builder::XmlMarkup.new
318
+
319
+ xml.tag! 'RefundTransactionReq', 'xmlns' => PAYPAL_NAMESPACE do
320
+ xml.tag! 'RefundTransactionRequest', 'xmlns:n2' => EBAY_NAMESPACE do
321
+ xml.tag! 'n2:Version', API_VERSION
322
+ xml.tag! 'TransactionID', identification
323
+ xml.tag! 'Amount', amount(money), 'currencyID' => (options[:currency] || currency(money)) if money.present?
324
+ xml.tag! 'RefundType', (options[:refund_type] || (money.present? ? 'Partial' : 'Full'))
325
+ xml.tag! 'Memo', options[:note] unless options[:note].blank?
326
+ end
327
+ end
328
+
329
+ xml.target!
330
+ end
331
+
332
+ def build_void_request(authorization, options)
333
+ xml = Builder::XmlMarkup.new
334
+
335
+ xml.tag! 'DoVoidReq', 'xmlns' => PAYPAL_NAMESPACE do
336
+ xml.tag! 'DoVoidRequest', 'xmlns:n2' => EBAY_NAMESPACE do
337
+ xml.tag! 'n2:Version', API_VERSION
338
+ xml.tag! 'AuthorizationID', authorization
339
+ xml.tag! 'Note', options[:description]
340
+ end
341
+ end
342
+
343
+ xml.target!
344
+ end
345
+
346
+ def build_mass_pay_request(*args)
347
+ default_options = args.last.is_a?(Hash) ? args.pop : {}
348
+ recipients = args.first.is_a?(Array) ? args : [args]
349
+
350
+ xml = Builder::XmlMarkup.new
351
+
352
+ xml.tag! 'MassPayReq', 'xmlns' => PAYPAL_NAMESPACE do
353
+ xml.tag! 'MassPayRequest', 'xmlns:n2' => EBAY_NAMESPACE do
354
+ xml.tag! 'n2:Version', API_VERSION
355
+ xml.tag! 'EmailSubject', default_options[:subject] if default_options[:subject]
356
+ recipients.each do |money, recipient, options|
357
+ options ||= default_options
358
+ xml.tag! 'MassPayItem' do
359
+ xml.tag! 'ReceiverEmail', recipient
360
+ xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
361
+ xml.tag! 'Note', options[:note] if options[:note]
362
+ xml.tag! 'UniqueId', options[:unique_id] if options[:unique_id]
363
+ end
364
+ end
365
+ end
366
+ end
367
+
368
+ xml.target!
369
+ end
370
+
371
+ def build_manage_pending_transaction_status(transaction_id, action)
372
+ build_request_wrapper('ManagePendingTransactionStatus') do |xml|
373
+ xml.tag! 'TransactionID', transaction_id
374
+ xml.tag! 'Action', action
375
+ end
376
+ end
377
+
378
+ def build_reference_transaction_request(money, options)
379
+ opts = options.dup
380
+ opts[:ip_address] ||= opts[:ip]
381
+ currency_code = opts[:currency] || currency(money)
382
+ reference_transaction_optional_fields = %w{ n2:ReferenceID n2:PaymentAction
383
+ n2:PaymentType n2:IPAddress
384
+ n2:ReqConfirmShipping n2:MerchantSessionId
385
+ n2:ReturnFMFDetails n2:SoftDescriptor }
386
+ build_request_wrapper('DoReferenceTransaction', :request_details => true) do |xml|
387
+ add_optional_fields(xml, reference_transaction_optional_fields, opts)
388
+ add_payment_details(xml, money, currency_code, opts)
389
+ end
390
+ end
391
+
392
+ def build_get_transaction_details(transaction_id)
393
+ build_request_wrapper('GetTransactionDetails') do |xml|
394
+ xml.tag! 'TransactionID', transaction_id
395
+ end
396
+ end
397
+
398
+ def build_transaction_search(options)
399
+ currency_code = options[:currency_code]
400
+ currency_code ||= currency(options[:amount]) if options[:amount]
401
+ transaction_search_optional_fields = %w{ Payer ReceiptID Receiver
402
+ TransactionID InvoiceID CardNumber
403
+ AuctionItemNumber TransactionClass
404
+ CurrencyCode Status }
405
+ build_request_wrapper('TransactionSearch') do |xml|
406
+ xml.tag! 'StartDate', date_to_iso(options[:start_date])
407
+ xml.tag! 'EndDate', date_to_iso(options[:end_date]) unless options[:end_date].blank?
408
+ add_optional_fields(xml, transaction_search_optional_fields, options)
409
+ xml.tag! 'Amount', localized_amount(options[:amount], currency_code), 'currencyID' => currency_code unless options[:amount].blank?
410
+ end
411
+ end
412
+
413
+
414
+ def build_get_balance(return_all_currencies)
415
+ build_request_wrapper('GetBalance') do |xml|
416
+ xml.tag! 'ReturnAllCurrencies', return_all_currencies unless return_all_currencies.nil?
417
+ end
418
+ end
419
+
420
+ def parse(action, xml)
421
+ legacy_hash = legacy_parse(action, xml)
422
+ xml = strip_attributes(xml)
423
+ hash = Hash.from_xml(xml)
424
+ hash = hash.fetch('Envelope').fetch('Body').fetch("#{action}Response")
425
+ hash = hash["#{action}ResponseDetails"] if hash["#{action}ResponseDetails"]
426
+
427
+ legacy_hash.merge(hash)
428
+ rescue IndexError
429
+ legacy_hash.merge(hash['Envelope']['Body'])
430
+ end
431
+
432
+ def strip_attributes(xml)
433
+ xml = REXML::Document.new(xml)
434
+ REXML::XPath.each(xml, '//SOAP-ENV:Envelope//*[@*]') do |el|
435
+ el.attributes.each_attribute { |a| a.remove }
436
+ end
437
+ xml.to_s
438
+ end
439
+
440
+ def legacy_parse(action, xml)
441
+ response = {}
442
+
443
+ error_messages = []
444
+ error_codes = []
445
+
446
+ xml = REXML::Document.new(xml)
447
+ if root = REXML::XPath.first(xml, "//#{action}Response")
448
+ root.elements.each do |node|
449
+ case node.name
450
+ when 'Errors'
451
+ short_message = nil
452
+ long_message = nil
453
+
454
+ node.elements.each do |child|
455
+ case child.name
456
+ when "LongMessage"
457
+ long_message = child.text unless child.text.blank?
458
+ when "ShortMessage"
459
+ short_message = child.text unless child.text.blank?
460
+ when "ErrorCode"
461
+ error_codes << child.text unless child.text.blank?
462
+ end
463
+ end
464
+
465
+ if message = long_message || short_message
466
+ error_messages << message
467
+ end
468
+ else
469
+ legacy_parse_element(response, node)
470
+ end
471
+ end
472
+ response[:message] = error_messages.uniq.join(". ") unless error_messages.empty?
473
+ response[:error_codes] = error_codes.uniq.join(",") unless error_codes.empty?
474
+ elsif root = REXML::XPath.first(xml, "//SOAP-ENV:Fault")
475
+ legacy_parse_element(response, root)
476
+ response[:message] = "#{response[:faultcode]}: #{response[:faultstring]} - #{response[:detail]}"
477
+ end
478
+
479
+ response
480
+ end
481
+
482
+ def legacy_parse_element(response, node)
483
+ if node.has_elements?
484
+ node.elements.each{|e| legacy_parse_element(response, e) }
485
+ else
486
+ response[node.name.underscore.to_sym] = node.text
487
+ node.attributes.each do |k, v|
488
+ response["#{node.name.underscore}_#{k.underscore}".to_sym] = v if k == 'currencyID'
489
+ end
490
+ end
491
+ end
492
+
493
+ def build_request(body)
494
+ xml = Builder::XmlMarkup.new
495
+
496
+ xml.instruct!
497
+ xml.tag! 'env:Envelope', ENVELOPE_NAMESPACES do
498
+ xml.tag! 'env:Header' do
499
+ add_credentials(xml) unless @options[:headers] && @options[:headers]['X-PP-AUTHORIZATION']
500
+ end
501
+
502
+ xml.tag! 'env:Body' do
503
+ xml << body
504
+ end
505
+ end
506
+ xml.target!
507
+ end
508
+
509
+ def add_credentials(xml)
510
+ xml.tag! 'RequesterCredentials', CREDENTIALS_NAMESPACES do
511
+ xml.tag! 'n1:Credentials' do
512
+ xml.tag! 'Username', @options[:login]
513
+ xml.tag! 'Password', @options[:password]
514
+ xml.tag! 'Subject', @options[:subject]
515
+ xml.tag! 'Signature', @options[:signature] unless @options[:signature].blank?
516
+ end
517
+ end
518
+ end
519
+
520
+ def add_address(xml, element, address)
521
+ return if address.nil?
522
+ xml.tag! element do
523
+ xml.tag! 'n2:Name', address[:name]
524
+ xml.tag! 'n2:Street1', address[:address1]
525
+ xml.tag! 'n2:Street2', address[:address2]
526
+ xml.tag! 'n2:CityName', address[:city]
527
+ xml.tag! 'n2:StateOrProvince', address[:state].blank? ? 'N/A' : address[:state]
528
+ xml.tag! 'n2:Country', address[:country]
529
+ xml.tag! 'n2:Phone', address[:phone] unless address[:phone].blank?
530
+ xml.tag! 'n2:PostalCode', address[:zip]
531
+ end
532
+ end
533
+
534
+ def add_payment_details_items_xml(xml, options, currency_code)
535
+ options[:items].each do |item|
536
+ xml.tag! 'n2:PaymentDetailsItem' do
537
+ xml.tag! 'n2:Name', item[:name]
538
+ xml.tag! 'n2:Number', item[:number]
539
+ xml.tag! 'n2:Quantity', item[:quantity]
540
+ if item[:amount]
541
+ xml.tag! 'n2:Amount', localized_amount(item[:amount], currency_code), 'currencyID' => currency_code
542
+ end
543
+ xml.tag! 'n2:Description', item[:description]
544
+ xml.tag! 'n2:ItemURL', item[:url]
545
+ xml.tag! 'n2:ItemCategory', item[:category] if item[:category]
546
+ end
547
+ end
548
+ end
549
+
550
+ def add_payment_details(xml, money, currency_code, options = {})
551
+ xml.tag! 'n2:PaymentDetails' do
552
+ xml.tag! 'n2:OrderTotal', localized_amount(money, currency_code), 'currencyID' => currency_code
553
+
554
+ # All of the values must be included together and add up to the order total
555
+ if [:subtotal, :shipping, :handling, :tax].all?{ |o| options.has_key?(o) }
556
+ xml.tag! 'n2:ItemTotal', localized_amount(options[:subtotal], currency_code), 'currencyID' => currency_code
557
+ xml.tag! 'n2:ShippingTotal', localized_amount(options[:shipping], currency_code),'currencyID' => currency_code
558
+ xml.tag! 'n2:HandlingTotal', localized_amount(options[:handling], currency_code),'currencyID' => currency_code
559
+ xml.tag! 'n2:TaxTotal', localized_amount(options[:tax], currency_code), 'currencyID' => currency_code
560
+ end
561
+
562
+ xml.tag! 'n2:InsuranceTotal', localized_amount(options[:insurance_total], currency_code),'currencyID' => currency_code unless options[:insurance_total].blank?
563
+ xml.tag! 'n2:ShippingDiscount', localized_amount(options[:shipping_discount], currency_code),'currencyID' => currency_code unless options[:shipping_discount].blank?
564
+ xml.tag! 'n2:InsuranceOptionOffered', options[:insurance_option_offered] if options.has_key?(:insurance_option_offered)
565
+
566
+ xml.tag! 'n2:OrderDescription', options[:description] unless options[:description].blank?
567
+
568
+ # Custom field Character length and limitations: 256 single-byte alphanumeric characters
569
+ xml.tag! 'n2:Custom', options[:custom] unless options[:custom].blank?
570
+
571
+ xml.tag! 'n2:InvoiceID', (options[:order_id] || options[:invoice_id]) unless (options[:order_id] || options[:invoice_id]).blank?
572
+ xml.tag! 'n2:ButtonSource', application_id.to_s.slice(0,32) unless application_id.blank?
573
+
574
+ # The notify URL applies only to DoExpressCheckoutPayment.
575
+ # This value is ignored when set in SetExpressCheckout or GetExpressCheckoutDetails
576
+ xml.tag! 'n2:NotifyURL', options[:notify_url] unless options[:notify_url].blank?
577
+
578
+ add_address(xml, 'n2:ShipToAddress', options[:shipping_address]) unless options[:shipping_address].blank?
579
+
580
+ add_payment_details_items_xml(xml, options, currency_code) unless options[:items].blank?
581
+
582
+ add_express_only_payment_details(xml, options) if options[:express_request]
583
+
584
+ # Any value other than Y – This is not a recurring transaction
585
+ # To pass Y in this field, you must have established a billing agreement with
586
+ # the buyer specifying the amount, frequency, and duration of the recurring payment.
587
+ # requires version 80.0 of the API
588
+ xml.tag! 'n2:Recurring', options[:recurring] unless options[:recurring].blank?
589
+ end
590
+ end
591
+
592
+ def add_express_only_payment_details(xml, options = {})
593
+ add_optional_fields(xml,
594
+ %w{n2:NoteText n2:SoftDescriptor
595
+ n2:TransactionId n2:AllowedPaymentMethodType
596
+ n2:PaymentRequestID n2:PaymentAction},
597
+ options)
598
+ end
599
+
600
+ def add_optional_fields(xml, optional_fields, options = {})
601
+ optional_fields.each do |optional_text_field|
602
+ if optional_text_field =~ /(\w+:)(\w+)/
603
+ ns = $1
604
+ field = $2
605
+ field_as_symbol = field.underscore.to_sym
606
+ else
607
+ ns = ''
608
+ field = optional_text_field
609
+ field_as_symbol = optional_text_field.underscore.to_sym
610
+ end
611
+ xml.tag! ns + field, options[field_as_symbol] unless options[field_as_symbol].blank?
612
+ end
613
+ xml
614
+ end
615
+
616
+ def endpoint_url
617
+ URLS[test? ? :test : :live][@options[:signature].blank? ? :certificate : :signature]
618
+ end
619
+
620
+ def commit(action, request)
621
+ response = parse(action, ssl_post(endpoint_url, build_request(request), @options[:headers]))
622
+
623
+ build_response(successful?(response), message_from(response), response,
624
+ :test => test?,
625
+ :authorization => authorization_from(response),
626
+ :fraud_review => fraud_review?(response),
627
+ :avs_result => { :code => response[:avs_code] },
628
+ :cvv_result => response[:cvv2_code]
629
+ )
630
+ end
631
+
632
+ def fraud_review?(response)
633
+ response[:error_codes] == FRAUD_REVIEW_CODE
634
+ end
635
+
636
+ def authorization_from(response)
637
+ response[:transaction_id] || response[:authorization_id] || response[:refund_transaction_id] # middle one is from reauthorization
638
+ end
639
+
640
+ def successful?(response)
641
+ SUCCESS_CODES.include?(response[:ack])
642
+ end
643
+
644
+ def message_from(response)
645
+ response[:message] || response[:ack]
646
+ end
647
+
648
+ def date_to_iso(date)
649
+ (date.is_a?(Date) ? date.to_time : date).utc.iso8601
650
+ end
651
+ end
652
+ end
653
+ end