fishman-activemerchant 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. data/CHANGELOG +733 -0
  2. data/CONTRIBUTORS +257 -0
  3. data/MIT-LICENSE +20 -0
  4. data/gem-public_cert.pem +20 -0
  5. data/lib/active_merchant.rb +47 -0
  6. data/lib/active_merchant/billing.rb +9 -0
  7. data/lib/active_merchant/billing/avs_result.rb +98 -0
  8. data/lib/active_merchant/billing/base.rb +57 -0
  9. data/lib/active_merchant/billing/check.rb +68 -0
  10. data/lib/active_merchant/billing/credit_card.rb +260 -0
  11. data/lib/active_merchant/billing/credit_card_formatting.rb +21 -0
  12. data/lib/active_merchant/billing/credit_card_methods.rb +125 -0
  13. data/lib/active_merchant/billing/cvv_result.rb +38 -0
  14. data/lib/active_merchant/billing/expiry_date.rb +34 -0
  15. data/lib/active_merchant/billing/gateway.rb +170 -0
  16. data/lib/active_merchant/billing/gateways.rb +18 -0
  17. data/lib/active_merchant/billing/gateways/authorize_net.rb +693 -0
  18. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +858 -0
  19. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +308 -0
  20. data/lib/active_merchant/billing/gateways/beanstream.rb +139 -0
  21. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +282 -0
  22. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
  23. data/lib/active_merchant/billing/gateways/blue_pay.rb +11 -0
  24. data/lib/active_merchant/billing/gateways/bogus.rb +142 -0
  25. data/lib/active_merchant/billing/gateways/braintree.rb +17 -0
  26. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
  27. data/lib/active_merchant/billing/gateways/braintree_blue.rb +303 -0
  28. data/lib/active_merchant/billing/gateways/braintree_orange.rb +17 -0
  29. data/lib/active_merchant/billing/gateways/card_save.rb +23 -0
  30. data/lib/active_merchant/billing/gateways/card_stream.rb +230 -0
  31. data/lib/active_merchant/billing/gateways/cyber_source.rb +430 -0
  32. data/lib/active_merchant/billing/gateways/data_cash.rb +597 -0
  33. data/lib/active_merchant/billing/gateways/efsnet.rb +235 -0
  34. data/lib/active_merchant/billing/gateways/elavon.rb +134 -0
  35. data/lib/active_merchant/billing/gateways/epay.rb +274 -0
  36. data/lib/active_merchant/billing/gateways/eway.rb +277 -0
  37. data/lib/active_merchant/billing/gateways/eway_managed.rb +264 -0
  38. data/lib/active_merchant/billing/gateways/exact.rb +222 -0
  39. data/lib/active_merchant/billing/gateways/federated_canada.rb +168 -0
  40. data/lib/active_merchant/billing/gateways/first_pay.rb +177 -0
  41. data/lib/active_merchant/billing/gateways/garanti.rb +262 -0
  42. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +250 -0
  43. data/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem +13 -0
  44. data/lib/active_merchant/billing/gateways/ideal/ideal_response.rb +29 -0
  45. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +55 -0
  46. data/lib/active_merchant/billing/gateways/inspire.rb +221 -0
  47. data/lib/active_merchant/billing/gateways/instapay.rb +164 -0
  48. data/lib/active_merchant/billing/gateways/iridium.rb +258 -0
  49. data/lib/active_merchant/billing/gateways/jetpay.rb +276 -0
  50. data/lib/active_merchant/billing/gateways/linkpoint.rb +454 -0
  51. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +156 -0
  52. data/lib/active_merchant/billing/gateways/merchant_ware.rb +289 -0
  53. data/lib/active_merchant/billing/gateways/modern_payments.rb +36 -0
  54. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +220 -0
  55. data/lib/active_merchant/billing/gateways/moneris.rb +209 -0
  56. data/lib/active_merchant/billing/gateways/net_registry.rb +189 -0
  57. data/lib/active_merchant/billing/gateways/netaxept.rb +239 -0
  58. data/lib/active_merchant/billing/gateways/netbilling.rb +168 -0
  59. data/lib/active_merchant/billing/gateways/nmi.rb +13 -0
  60. data/lib/active_merchant/billing/gateways/ogone.rb +292 -0
  61. data/lib/active_merchant/billing/gateways/optimal_payment.rb +274 -0
  62. data/lib/active_merchant/billing/gateways/orbital.rb +321 -0
  63. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +46 -0
  64. data/lib/active_merchant/billing/gateways/pay_junction.rb +392 -0
  65. data/lib/active_merchant/billing/gateways/pay_secure.rb +120 -0
  66. data/lib/active_merchant/billing/gateways/paybox_direct.rb +207 -0
  67. data/lib/active_merchant/billing/gateways/payflow.rb +253 -0
  68. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +207 -0
  69. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
  70. data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
  71. data/lib/active_merchant/billing/gateways/payflow_express.rb +222 -0
  72. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
  73. data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
  74. data/lib/active_merchant/billing/gateways/payment_express.rb +235 -0
  75. data/lib/active_merchant/billing/gateways/paypal.rb +121 -0
  76. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +354 -0
  77. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +49 -0
  78. data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
  79. data/lib/active_merchant/billing/gateways/paypal_express.rb +184 -0
  80. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +25 -0
  81. data/lib/active_merchant/billing/gateways/paypal_express_de.rb +14 -0
  82. data/lib/active_merchant/billing/gateways/paystation.rb +201 -0
  83. data/lib/active_merchant/billing/gateways/plugnpay.rb +298 -0
  84. data/lib/active_merchant/billing/gateways/psigate.rb +219 -0
  85. data/lib/active_merchant/billing/gateways/psl_card.rb +304 -0
  86. data/lib/active_merchant/billing/gateways/qbms.rb +297 -0
  87. data/lib/active_merchant/billing/gateways/quantum.rb +282 -0
  88. data/lib/active_merchant/billing/gateways/quickpay.rb +297 -0
  89. data/lib/active_merchant/billing/gateways/realex.rb +311 -0
  90. data/lib/active_merchant/billing/gateways/sage.rb +146 -0
  91. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +88 -0
  92. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +116 -0
  93. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
  94. data/lib/active_merchant/billing/gateways/sage_pay.rb +320 -0
  95. data/lib/active_merchant/billing/gateways/sallie_mae.rb +144 -0
  96. data/lib/active_merchant/billing/gateways/secure_net.rb +330 -0
  97. data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
  98. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +193 -0
  99. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +113 -0
  100. data/lib/active_merchant/billing/gateways/skip_jack.rb +453 -0
  101. data/lib/active_merchant/billing/gateways/smart_ps.rb +271 -0
  102. data/lib/active_merchant/billing/gateways/stripe.rb +212 -0
  103. data/lib/active_merchant/billing/gateways/trans_first.rb +127 -0
  104. data/lib/active_merchant/billing/gateways/transax.rb +25 -0
  105. data/lib/active_merchant/billing/gateways/trust_commerce.rb +423 -0
  106. data/lib/active_merchant/billing/gateways/usa_epay.rb +194 -0
  107. data/lib/active_merchant/billing/gateways/verifi.rb +233 -0
  108. data/lib/active_merchant/billing/gateways/viaklix.rb +189 -0
  109. data/lib/active_merchant/billing/gateways/wirecard.rb +318 -0
  110. data/lib/active_merchant/billing/gateways/worldpay.rb +280 -0
  111. data/lib/active_merchant/billing/integrations.rb +17 -0
  112. data/lib/active_merchant/billing/integrations/action_view_helper.rb +68 -0
  113. data/lib/active_merchant/billing/integrations/bogus.rb +23 -0
  114. data/lib/active_merchant/billing/integrations/bogus/helper.rb +17 -0
  115. data/lib/active_merchant/billing/integrations/bogus/notification.rb +11 -0
  116. data/lib/active_merchant/billing/integrations/bogus/return.rb +10 -0
  117. data/lib/active_merchant/billing/integrations/chronopay.rb +23 -0
  118. data/lib/active_merchant/billing/integrations/chronopay/helper.rb +120 -0
  119. data/lib/active_merchant/billing/integrations/chronopay/notification.rb +158 -0
  120. data/lib/active_merchant/billing/integrations/chronopay/return.rb +10 -0
  121. data/lib/active_merchant/billing/integrations/direc_pay.rb +41 -0
  122. data/lib/active_merchant/billing/integrations/direc_pay/helper.rb +200 -0
  123. data/lib/active_merchant/billing/integrations/direc_pay/notification.rb +76 -0
  124. data/lib/active_merchant/billing/integrations/direc_pay/return.rb +32 -0
  125. data/lib/active_merchant/billing/integrations/direc_pay/status.rb +37 -0
  126. data/lib/active_merchant/billing/integrations/directebanking.rb +47 -0
  127. data/lib/active_merchant/billing/integrations/directebanking/helper.rb +90 -0
  128. data/lib/active_merchant/billing/integrations/directebanking/notification.rb +120 -0
  129. data/lib/active_merchant/billing/integrations/directebanking/return.rb +11 -0
  130. data/lib/active_merchant/billing/integrations/dwolla.rb +30 -0
  131. data/lib/active_merchant/billing/integrations/dwolla/helper.rb +28 -0
  132. data/lib/active_merchant/billing/integrations/dwolla/notification.rb +50 -0
  133. data/lib/active_merchant/billing/integrations/dwolla/return.rb +38 -0
  134. data/lib/active_merchant/billing/integrations/e_payment_plans.rb +48 -0
  135. data/lib/active_merchant/billing/integrations/e_payment_plans/helper.rb +34 -0
  136. data/lib/active_merchant/billing/integrations/e_payment_plans/notification.rb +84 -0
  137. data/lib/active_merchant/billing/integrations/gestpay.rb +25 -0
  138. data/lib/active_merchant/billing/integrations/gestpay/common.rb +42 -0
  139. data/lib/active_merchant/billing/integrations/gestpay/helper.rb +70 -0
  140. data/lib/active_merchant/billing/integrations/gestpay/notification.rb +85 -0
  141. data/lib/active_merchant/billing/integrations/gestpay/return.rb +10 -0
  142. data/lib/active_merchant/billing/integrations/helper.rb +96 -0
  143. data/lib/active_merchant/billing/integrations/hi_trust.rb +27 -0
  144. data/lib/active_merchant/billing/integrations/hi_trust/helper.rb +58 -0
  145. data/lib/active_merchant/billing/integrations/hi_trust/notification.rb +59 -0
  146. data/lib/active_merchant/billing/integrations/hi_trust/return.rb +67 -0
  147. data/lib/active_merchant/billing/integrations/moneybookers.rb +26 -0
  148. data/lib/active_merchant/billing/integrations/moneybookers/helper.rb +59 -0
  149. data/lib/active_merchant/billing/integrations/moneybookers/notification.rb +129 -0
  150. data/lib/active_merchant/billing/integrations/nochex.rb +88 -0
  151. data/lib/active_merchant/billing/integrations/nochex/helper.rb +68 -0
  152. data/lib/active_merchant/billing/integrations/nochex/notification.rb +94 -0
  153. data/lib/active_merchant/billing/integrations/nochex/return.rb +10 -0
  154. data/lib/active_merchant/billing/integrations/notification.rb +62 -0
  155. data/lib/active_merchant/billing/integrations/payflow_link.rb +21 -0
  156. data/lib/active_merchant/billing/integrations/payflow_link/helper.rb +58 -0
  157. data/lib/active_merchant/billing/integrations/payflow_link/notification.rb +78 -0
  158. data/lib/active_merchant/billing/integrations/paypal.rb +39 -0
  159. data/lib/active_merchant/billing/integrations/paypal/helper.rb +119 -0
  160. data/lib/active_merchant/billing/integrations/paypal/notification.rb +154 -0
  161. data/lib/active_merchant/billing/integrations/paypal/return.rb +10 -0
  162. data/lib/active_merchant/billing/integrations/quickpay.rb +21 -0
  163. data/lib/active_merchant/billing/integrations/quickpay/helper.rb +72 -0
  164. data/lib/active_merchant/billing/integrations/quickpay/notification.rb +74 -0
  165. data/lib/active_merchant/billing/integrations/return.rb +42 -0
  166. data/lib/active_merchant/billing/integrations/sage_pay_form.rb +37 -0
  167. data/lib/active_merchant/billing/integrations/sage_pay_form/encryption.rb +33 -0
  168. data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +111 -0
  169. data/lib/active_merchant/billing/integrations/sage_pay_form/notification.rb +210 -0
  170. data/lib/active_merchant/billing/integrations/sage_pay_form/return.rb +31 -0
  171. data/lib/active_merchant/billing/integrations/two_checkout.rb +23 -0
  172. data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +59 -0
  173. data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +114 -0
  174. data/lib/active_merchant/billing/integrations/two_checkout/return.rb +17 -0
  175. data/lib/active_merchant/billing/integrations/valitor.rb +33 -0
  176. data/lib/active_merchant/billing/integrations/valitor/helper.rb +86 -0
  177. data/lib/active_merchant/billing/integrations/valitor/notification.rb +13 -0
  178. data/lib/active_merchant/billing/integrations/valitor/response_fields.rb +97 -0
  179. data/lib/active_merchant/billing/integrations/valitor/return.rb +13 -0
  180. data/lib/active_merchant/billing/integrations/world_pay.rb +27 -0
  181. data/lib/active_merchant/billing/integrations/world_pay/helper.rb +100 -0
  182. data/lib/active_merchant/billing/integrations/world_pay/notification.rb +160 -0
  183. data/lib/active_merchant/billing/response.rb +32 -0
  184. data/lib/active_merchant/version.rb +3 -0
  185. data/lib/activemerchant.rb +1 -0
  186. data/lib/support/gateway_support.rb +58 -0
  187. data/lib/support/outbound_hosts.rb +25 -0
  188. metadata +335 -0
@@ -0,0 +1,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,235 @@
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 = [ 'AU', 'MY', 'NZ', 'SG', 'ZA', 'GB', 'US' ]
20
+
21
+ self.homepage_url = 'http://www.paymentexpress.com/'
22
+ self.display_name = 'PaymentExpress'
23
+
24
+ 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
+ def initialize(options = {})
38
+ # A DPS username and password must exist
39
+ requires!(options, :login, :password)
40
+ # Make the options an instance variable
41
+ @options = options
42
+ super
43
+ end
44
+
45
+ # Funds are transferred immediately.
46
+ def purchase(money, payment_source, options = {})
47
+ request = build_purchase_or_authorization_request(money, payment_source, options)
48
+ commit(:purchase, request)
49
+ end
50
+
51
+ # NOTE: Perhaps in options we allow a transaction note to be inserted
52
+ # Verifies that funds are available for the requested card and amount and reserves the specified amount.
53
+ # See: http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#Authcomplete
54
+ def authorize(money, payment_source, options = {})
55
+ request = build_purchase_or_authorization_request(money, payment_source, options)
56
+ commit(:authorization, request)
57
+ end
58
+
59
+ # Transfer pre-authorized funds immediately
60
+ # See: http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#Authcomplete
61
+ def capture(money, identification, options = {})
62
+ request = build_capture_or_credit_request(money, identification, options)
63
+ commit(:capture, request)
64
+ end
65
+
66
+ # Refund funds to the card holder
67
+ def refund(money, identification, options = {})
68
+ requires!(options, :description)
69
+
70
+ request = build_capture_or_credit_request(money, identification, options)
71
+ commit(:credit, request)
72
+ end
73
+
74
+ def credit(money, identification, options = {})
75
+ deprecated CREDIT_DEPRECATION_MESSAGE
76
+ refund(money, identification, options)
77
+ end
78
+
79
+ # token based billing
80
+
81
+ # initiates a "Validate" transcation to store card data on payment express servers
82
+ # returns a "token" that can be used to rebill this card
83
+ # see: http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#Tokenbilling
84
+ # PaymentExpress does not support unstoring a stored card.
85
+ def store(credit_card, options = {})
86
+ request = build_token_request(credit_card, options)
87
+ commit(:validate, request)
88
+ end
89
+
90
+ private
91
+
92
+ def build_purchase_or_authorization_request(money, payment_source, options)
93
+ result = new_transaction
94
+
95
+ if payment_source.is_a?(String)
96
+ add_billing_token(result, payment_source)
97
+ else
98
+ add_credit_card(result, payment_source)
99
+ end
100
+
101
+ add_amount(result, money, options)
102
+ add_invoice(result, options)
103
+ add_address_verification_data(result, options)
104
+ result
105
+ end
106
+
107
+ def build_capture_or_credit_request(money, identification, options)
108
+ result = new_transaction
109
+
110
+ add_amount(result, money, options)
111
+ add_invoice(result, options)
112
+ add_reference(result, identification)
113
+ result
114
+ end
115
+
116
+ def build_token_request(credit_card, options)
117
+ result = new_transaction
118
+ add_credit_card(result, credit_card)
119
+ add_amount(result, 100, options) #need to make an auth request for $1
120
+ add_token_request(result, options)
121
+ result
122
+ end
123
+
124
+ def add_credentials(xml)
125
+ xml.add_element("PostUsername").text = @options[:login]
126
+ xml.add_element("PostPassword").text = @options[:password]
127
+ end
128
+
129
+ def add_reference(xml, identification)
130
+ xml.add_element("DpsTxnRef").text = identification
131
+ end
132
+
133
+ def add_credit_card(xml, credit_card)
134
+ xml.add_element("CardHolderName").text = credit_card.name
135
+ xml.add_element("CardNumber").text = credit_card.number
136
+ xml.add_element("DateExpiry").text = format_date(credit_card.month, credit_card.year)
137
+
138
+ if credit_card.verification_value?
139
+ xml.add_element("Cvc2").text = credit_card.verification_value
140
+ end
141
+
142
+ if requires_start_date_or_issue_number?(credit_card)
143
+ 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?
144
+ xml.add_element("IssueNumber").text = credit_card.issue_number unless credit_card.issue_number.blank?
145
+ end
146
+ end
147
+
148
+ def add_billing_token(xml, token)
149
+ xml.add_element("DpsBillingId").text = token
150
+ end
151
+
152
+ def add_token_request(xml, options)
153
+ xml.add_element("BillingId").text = options[:billing_id] if options[:billing_id]
154
+ xml.add_element("EnableAddBillCard").text = 1
155
+ end
156
+
157
+ def add_amount(xml, money, options)
158
+ xml.add_element("Amount").text = amount(money)
159
+ xml.add_element("InputCurrency").text = options[:currency] || currency(money)
160
+ end
161
+
162
+ def add_transaction_type(xml, action)
163
+ xml.add_element("TxnType").text = TRANSACTIONS[action]
164
+ end
165
+
166
+ def add_invoice(xml, options)
167
+ xml.add_element("TxnId").text = options[:order_id].to_s.slice(0, 16) unless options[:order_id].blank?
168
+ xml.add_element("MerchantReference").text = options[:description] unless options[:description].blank?
169
+ end
170
+
171
+ def add_address_verification_data(xml, options)
172
+ address = options[:billing_address] || options[:address]
173
+ return if address.nil?
174
+
175
+ xml.add_element("EnableAvsData").text = 1
176
+ xml.add_element("AvsAction").text = 1
177
+
178
+ xml.add_element("AvsStreetAddress").text = address[:address1]
179
+ xml.add_element("AvsPostCode").text = address[:zip]
180
+ end
181
+
182
+ def new_transaction
183
+ REXML::Document.new.add_element("Txn")
184
+ end
185
+
186
+ # Take in the request and post it to DPS
187
+ def commit(action, request)
188
+ add_credentials(request)
189
+ add_transaction_type(request, action)
190
+
191
+ # Parse the XML response
192
+ response = parse( ssl_post(URL, request.to_s) )
193
+
194
+ # Return a response
195
+ PaymentExpressResponse.new(response[:success] == APPROVED, response[:card_holder_help_text], response,
196
+ :test => response[:test_mode] == '1',
197
+ :authorization => response[:dps_txn_ref]
198
+ )
199
+ end
200
+
201
+ # Response XML documentation: http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#XMLTxnOutput
202
+ def parse(xml_string)
203
+ response = {}
204
+
205
+ xml = REXML::Document.new(xml_string)
206
+
207
+ # Gather all root elements such as HelpText
208
+ xml.elements.each('Txn/*') do |element|
209
+ response[element.name.underscore.to_sym] = element.text unless element.name == 'Transaction'
210
+ end
211
+
212
+ # Gather all transaction elements and prefix with "account_"
213
+ # So we could access the MerchantResponseText by going
214
+ # response[account_merchant_response_text]
215
+ xml.elements.each('Txn/Transaction/*') do |element|
216
+ response[element.name.underscore.to_sym] = element.text
217
+ end
218
+
219
+ response
220
+ end
221
+
222
+ def format_date(month, year)
223
+ "#{format(month, :two_digits)}#{format(year, :two_digits)}"
224
+ end
225
+ end
226
+
227
+ class PaymentExpressResponse < Response
228
+ # add a method to response so we can easily get the token
229
+ # for Validate transactions
230
+ def token
231
+ @params["billing_id"] || @params["dps_billing_id"]
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,121 @@
1
+ require File.dirname(__FILE__) + '/paypal/paypal_common_api'
2
+ require File.dirname(__FILE__) + '/paypal_express'
3
+
4
+ module ActiveMerchant #: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_or_referenced_id, options = {})
15
+ requires!(options, :ip)
16
+ commit define_transaction_type(credit_card_or_referenced_id), build_sale_or_authorization_request('Authorization', money, credit_card_or_referenced_id, options)
17
+ end
18
+
19
+ def purchase(money, credit_card_or_referenced_id, options = {})
20
+ requires!(options, :ip)
21
+ commit define_transaction_type(credit_card_or_referenced_id), build_sale_or_authorization_request('Sale', money, credit_card_or_referenced_id, options)
22
+ end
23
+
24
+ def express
25
+ @express ||= PaypalExpressGateway.new(@options)
26
+ end
27
+
28
+ private
29
+
30
+ def define_transaction_type(transaction_arg)
31
+ if transaction_arg.is_a?(String)
32
+ return 'DoReferenceTransaction'
33
+ else
34
+ return 'DoDirectPayment'
35
+ end
36
+ end
37
+
38
+ def build_sale_or_authorization_request(action, money, credit_card_or_referenced_id, options)
39
+ transaction_type = define_transaction_type(credit_card_or_referenced_id)
40
+ reference_id = credit_card_or_referenced_id if transaction_type == "DoReferenceTransaction"
41
+
42
+ billing_address = options[:billing_address] || options[:address]
43
+ currency_code = options[:currency] || currency(money)
44
+
45
+ xml = Builder::XmlMarkup.new :indent => 2
46
+ xml.tag! transaction_type + 'Req', 'xmlns' => PAYPAL_NAMESPACE do
47
+ xml.tag! transaction_type + 'Request', 'xmlns:n2' => EBAY_NAMESPACE do
48
+ xml.tag! 'n2:Version', API_VERSION
49
+ xml.tag! 'n2:' + transaction_type + 'RequestDetails' do
50
+ xml.tag! 'n2:ReferenceID', reference_id if transaction_type == 'DoReferenceTransaction'
51
+ xml.tag! 'n2:PaymentAction', action
52
+ xml.tag! 'n2:PaymentDetails' do
53
+ xml.tag! 'n2:OrderTotal', localized_amount(money, currency_code), 'currencyID' => currency_code
54
+
55
+ # All of the values must be included together and add up to the order total
56
+ if [:subtotal, :shipping, :handling, :tax].all?{ |o| options.has_key?(o) }
57
+ xml.tag! 'n2:ItemTotal', localized_amount(options[:subtotal], currency_code), 'currencyID' => currency_code
58
+ xml.tag! 'n2:ShippingTotal', localized_amount(options[:shipping], currency_code),'currencyID' => currency_code
59
+ xml.tag! 'n2:HandlingTotal', localized_amount(options[:handling], currency_code),'currencyID' => currency_code
60
+ xml.tag! 'n2:TaxTotal', localized_amount(options[:tax], currency_code), 'currencyID' => currency_code
61
+ end
62
+
63
+ xml.tag! 'n2:NotifyURL', options[:notify_url]
64
+ xml.tag! 'n2:OrderDescription', options[:description]
65
+ xml.tag! 'n2:InvoiceID', options[:order_id]
66
+ xml.tag! 'n2:ButtonSource', application_id.to_s.slice(0,32) unless application_id.blank?
67
+
68
+ add_address(xml, 'n2:ShipToAddress', options[:shipping_address]) if options[:shipping_address]
69
+ end
70
+ add_credit_card(xml, credit_card_or_referenced_id, billing_address, options) unless transaction_type == 'DoReferenceTransaction'
71
+ xml.tag! 'n2:IPAddress', options[:ip]
72
+ end
73
+ end
74
+ end
75
+
76
+ xml.target!
77
+ end
78
+
79
+ def add_credit_card(xml, credit_card, address, options)
80
+ xml.tag! 'n2:CreditCard' do
81
+ xml.tag! 'n2:CreditCardType', credit_card_type(card_brand(credit_card))
82
+ xml.tag! 'n2:CreditCardNumber', credit_card.number
83
+ xml.tag! 'n2:ExpMonth', format(credit_card.month, :two_digits)
84
+ xml.tag! 'n2:ExpYear', format(credit_card.year, :four_digits)
85
+ xml.tag! 'n2:CVV2', credit_card.verification_value
86
+
87
+ if [ 'switch', 'solo' ].include?(card_brand(credit_card).to_s)
88
+ xml.tag! 'n2:StartMonth', format(credit_card.start_month, :two_digits) unless credit_card.start_month.blank?
89
+ xml.tag! 'n2:StartYear', format(credit_card.start_year, :four_digits) unless credit_card.start_year.blank?
90
+ xml.tag! 'n2:IssueNumber', format(credit_card.issue_number, :two_digits) unless credit_card.issue_number.blank?
91
+ end
92
+
93
+ xml.tag! 'n2:CardOwner' do
94
+ xml.tag! 'n2:PayerName' do
95
+ xml.tag! 'n2:FirstName', credit_card.first_name
96
+ xml.tag! 'n2:LastName', credit_card.last_name
97
+ end
98
+
99
+ xml.tag! 'n2:Payer', options[:email]
100
+ add_address(xml, 'n2:Address', address)
101
+ end
102
+ end
103
+ end
104
+
105
+ def credit_card_type(type)
106
+ case type
107
+ when 'visa' then 'Visa'
108
+ when 'master' then 'MasterCard'
109
+ when 'discover' then 'Discover'
110
+ when 'american_express' then 'Amex'
111
+ when 'switch' then 'Switch'
112
+ when 'solo' then 'Solo'
113
+ end
114
+ end
115
+
116
+ def build_response(success, message, response, options = {})
117
+ Response.new(success, message, response, options)
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,354 @@
1
+ module ActiveMerchant #: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 = '62.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
+ headers = {'X-PP-AUTHORIZATION' => options.delete(:auth_signature), 'X-PAYPAL-MESSAGE-PROTOCOL' => 'SOAP11'} if options[:auth_signature]
67
+ @options = {
68
+ :pem => pem_file,
69
+ :signature => signature,
70
+ :headers => headers || {}
71
+ }.update(options)
72
+
73
+
74
+ if @options[:pem].blank? && @options[:signature].blank?
75
+ raise ArgumentError, "An API Certificate or API Signature is required to make requests to PayPal"
76
+ end
77
+
78
+ super
79
+ end
80
+
81
+ def test?
82
+ @options[:test] || Base.gateway_mode == :test
83
+ end
84
+
85
+ def reauthorize(money, authorization, options = {})
86
+ commit 'DoReauthorization', build_reauthorize_request(money, authorization, options)
87
+ end
88
+
89
+ def capture(money, authorization, options = {})
90
+ commit 'DoCapture', build_capture_request(money, authorization, options)
91
+ end
92
+
93
+ # Transfer money to one or more recipients.
94
+ #
95
+ # gateway.transfer 1000, 'bob@example.com',
96
+ # :subject => "The money I owe you", :note => "Sorry it's so late"
97
+ #
98
+ # gateway.transfer [1000, 'fred@example.com'],
99
+ # [2450, 'wilma@example.com', :note => 'You will receive another payment on 3/24'],
100
+ # [2000, 'barney@example.com'],
101
+ # :subject => "Your Earnings", :note => "Thanks for your business."
102
+ #
103
+ def transfer(*args)
104
+ commit 'MassPay', build_mass_pay_request(*args)
105
+ end
106
+
107
+ def void(authorization, options = {})
108
+ commit 'DoVoid', build_void_request(authorization, options)
109
+ end
110
+
111
+ def refund(money, identification, options = {})
112
+ commit 'RefundTransaction', build_refund_request(money, identification, options)
113
+ end
114
+
115
+ def credit(money, identification, options = {})
116
+ deprecated Gateway::CREDIT_DEPRECATION_MESSAGE
117
+ refund(money, identification, options)
118
+ end
119
+
120
+ private
121
+ def build_reauthorize_request(money, authorization, options)
122
+ xml = Builder::XmlMarkup.new
123
+
124
+ xml.tag! 'DoReauthorizationReq', 'xmlns' => PAYPAL_NAMESPACE do
125
+ xml.tag! 'DoReauthorizationRequest', 'xmlns:n2' => EBAY_NAMESPACE do
126
+ xml.tag! 'n2:Version', API_VERSION
127
+ xml.tag! 'AuthorizationID', authorization
128
+ xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
129
+ end
130
+ end
131
+
132
+ xml.target!
133
+ end
134
+
135
+ def build_capture_request(money, authorization, options)
136
+ xml = Builder::XmlMarkup.new
137
+
138
+ xml.tag! 'DoCaptureReq', 'xmlns' => PAYPAL_NAMESPACE do
139
+ xml.tag! 'DoCaptureRequest', 'xmlns:n2' => EBAY_NAMESPACE do
140
+ xml.tag! 'n2:Version', API_VERSION
141
+ xml.tag! 'AuthorizationID', authorization
142
+ xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
143
+ xml.tag! 'CompleteType', 'Complete'
144
+ xml.tag! 'InvoiceID', options[:order_id] unless options[:order_id].blank?
145
+ xml.tag! 'Note', options[:description]
146
+ end
147
+ end
148
+
149
+ xml.target!
150
+ end
151
+
152
+ def build_refund_request(money, identification, options)
153
+ xml = Builder::XmlMarkup.new
154
+
155
+ xml.tag! 'RefundTransactionReq', 'xmlns' => PAYPAL_NAMESPACE do
156
+ xml.tag! 'RefundTransactionRequest', 'xmlns:n2' => EBAY_NAMESPACE do
157
+ xml.tag! 'n2:Version', API_VERSION
158
+ xml.tag! 'TransactionID', identification
159
+ xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
160
+ xml.tag! 'RefundType', 'Partial'
161
+ xml.tag! 'Memo', options[:note] unless options[:note].blank?
162
+ end
163
+ end
164
+
165
+ xml.target!
166
+ end
167
+
168
+ def build_void_request(authorization, options)
169
+ xml = Builder::XmlMarkup.new
170
+
171
+ xml.tag! 'DoVoidReq', 'xmlns' => PAYPAL_NAMESPACE do
172
+ xml.tag! 'DoVoidRequest', 'xmlns:n2' => EBAY_NAMESPACE do
173
+ xml.tag! 'n2:Version', API_VERSION
174
+ xml.tag! 'AuthorizationID', authorization
175
+ xml.tag! 'Note', options[:description]
176
+ end
177
+ end
178
+
179
+ xml.target!
180
+ end
181
+
182
+ def build_mass_pay_request(*args)
183
+ default_options = args.last.is_a?(Hash) ? args.pop : {}
184
+ recipients = args.first.is_a?(Array) ? args : [args]
185
+
186
+ xml = Builder::XmlMarkup.new
187
+
188
+ xml.tag! 'MassPayReq', 'xmlns' => PAYPAL_NAMESPACE do
189
+ xml.tag! 'MassPayRequest', 'xmlns:n2' => EBAY_NAMESPACE do
190
+ xml.tag! 'n2:Version', API_VERSION
191
+ xml.tag! 'EmailSubject', default_options[:subject] if default_options[:subject]
192
+ recipients.each do |money, recipient, options|
193
+ options ||= default_options
194
+ xml.tag! 'MassPayItem' do
195
+ xml.tag! 'ReceiverEmail', recipient
196
+ xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
197
+ xml.tag! 'Note', options[:note] if options[:note]
198
+ xml.tag! 'UniqueId', options[:unique_id] if options[:unique_id]
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ xml.target!
205
+ end
206
+
207
+ def parse(action, xml)
208
+ legacy_hash = legacy_parse(action, xml)
209
+ xml = strip_attributes(xml)
210
+ hash = Hash.from_xml(xml)
211
+ hash = hash.fetch('Envelope').fetch('Body').fetch("#{action}Response")
212
+ hash = hash["#{action}ResponseDetails"] if hash["#{action}ResponseDetails"]
213
+
214
+ legacy_hash.merge(hash)
215
+ rescue IndexError
216
+ legacy_hash.merge(hash['Envelope']['Body'])
217
+ end
218
+
219
+ def strip_attributes(xml)
220
+ xml = REXML::Document.new(xml)
221
+ REXML::XPath.each(xml, '//SOAP-ENV:Envelope//*[@*]') do |el|
222
+ el.attributes.each_attribute { |a| a.remove }
223
+ end
224
+ xml.to_s
225
+ end
226
+
227
+ def legacy_parse(action, xml)
228
+ response = {}
229
+
230
+ error_messages = []
231
+ error_codes = []
232
+
233
+ xml = REXML::Document.new(xml)
234
+ if root = REXML::XPath.first(xml, "//#{action}Response")
235
+ root.elements.each do |node|
236
+ case node.name
237
+ when 'Errors'
238
+ short_message = nil
239
+ long_message = nil
240
+
241
+ node.elements.each do |child|
242
+ case child.name
243
+ when "LongMessage"
244
+ long_message = child.text unless child.text.blank?
245
+ when "ShortMessage"
246
+ short_message = child.text unless child.text.blank?
247
+ when "ErrorCode"
248
+ error_codes << child.text unless child.text.blank?
249
+ end
250
+ end
251
+
252
+ if message = long_message || short_message
253
+ error_messages << message
254
+ end
255
+ else
256
+ legacy_parse_element(response, node)
257
+ end
258
+ end
259
+ response[:message] = error_messages.uniq.join(". ") unless error_messages.empty?
260
+ response[:error_codes] = error_codes.uniq.join(",") unless error_codes.empty?
261
+ elsif root = REXML::XPath.first(xml, "//SOAP-ENV:Fault")
262
+ legacy_parse_element(response, root)
263
+ response[:message] = "#{response[:faultcode]}: #{response[:faultstring]} - #{response[:detail]}"
264
+ end
265
+
266
+ response
267
+ end
268
+
269
+ def legacy_parse_element(response, node)
270
+ if node.has_elements?
271
+ node.elements.each{|e| legacy_parse_element(response, e) }
272
+ else
273
+ response[node.name.underscore.to_sym] = node.text
274
+ node.attributes.each do |k, v|
275
+ response["#{node.name.underscore}_#{k.underscore}".to_sym] = v if k == 'currencyID'
276
+ end
277
+ end
278
+ end
279
+
280
+ def build_request(body)
281
+ xml = Builder::XmlMarkup.new
282
+
283
+ xml.instruct!
284
+ xml.tag! 'env:Envelope', ENVELOPE_NAMESPACES do
285
+ xml.tag! 'env:Header' do
286
+ add_credentials(xml) unless @options[:headers] && @options[:headers]['X-PP-AUTHORIZATION']
287
+ end
288
+
289
+ xml.tag! 'env:Body' do
290
+ xml << body
291
+ end
292
+ end
293
+ xml.target!
294
+ end
295
+
296
+ def add_credentials(xml)
297
+ xml.tag! 'RequesterCredentials', CREDENTIALS_NAMESPACES do
298
+ xml.tag! 'n1:Credentials' do
299
+ xml.tag! 'Username', @options[:login]
300
+ xml.tag! 'Password', @options[:password]
301
+ xml.tag! 'Subject', @options[:subject]
302
+ xml.tag! 'Signature', @options[:signature] unless @options[:signature].blank?
303
+ end
304
+ end
305
+ end
306
+
307
+ def add_address(xml, element, address)
308
+ return if address.nil?
309
+ xml.tag! element do
310
+ xml.tag! 'n2:Name', address[:name]
311
+ xml.tag! 'n2:Street1', address[:address1]
312
+ xml.tag! 'n2:Street2', address[:address2]
313
+ xml.tag! 'n2:CityName', address[:city]
314
+ xml.tag! 'n2:StateOrProvince', address[:state].blank? ? 'N/A' : address[:state]
315
+ xml.tag! 'n2:Country', address[:country]
316
+ xml.tag! 'n2:PostalCode', address[:zip]
317
+ xml.tag! 'n2:Phone', address[:phone]
318
+ end
319
+ end
320
+
321
+ def endpoint_url
322
+ URLS[test? ? :test : :live][@options[:signature].blank? ? :certificate : :signature]
323
+ end
324
+
325
+ def commit(action, request)
326
+ response = parse(action, ssl_post(endpoint_url, build_request(request), @options[:headers]))
327
+
328
+ build_response(successful?(response), message_from(response), response,
329
+ :test => test?,
330
+ :authorization => authorization_from(response),
331
+ :fraud_review => fraud_review?(response),
332
+ :avs_result => { :code => response[:avs_code] },
333
+ :cvv_result => response[:cvv2_code]
334
+ )
335
+ end
336
+
337
+ def fraud_review?(response)
338
+ response[:error_codes] == FRAUD_REVIEW_CODE
339
+ end
340
+
341
+ def authorization_from(response)
342
+ response[:transaction_id] || response[:authorization_id] || response[:refund_transaction_id] # middle one is from reauthorization
343
+ end
344
+
345
+ def successful?(response)
346
+ SUCCESS_CODES.include?(response[:ack])
347
+ end
348
+
349
+ def message_from(response)
350
+ response[:message] || response[:ack]
351
+ end
352
+ end
353
+ end
354
+ end