aktivemerchant 2.0.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 (193) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +1596 -0
  3. data/CONTRIBUTORS +511 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +18 -0
  6. data/lib/active_merchant.rb +108 -0
  7. data/lib/active_merchant/billing.rb +13 -0
  8. data/lib/active_merchant/billing/apple_pay_payment_token.rb +22 -0
  9. data/lib/active_merchant/billing/avs_result.rb +98 -0
  10. data/lib/active_merchant/billing/base.rb +72 -0
  11. data/lib/active_merchant/billing/check.rb +76 -0
  12. data/lib/active_merchant/billing/compatibility.rb +120 -0
  13. data/lib/active_merchant/billing/credit_card.rb +352 -0
  14. data/lib/active_merchant/billing/credit_card_formatting.rb +24 -0
  15. data/lib/active_merchant/billing/credit_card_methods.rb +160 -0
  16. data/lib/active_merchant/billing/cvv_result.rb +38 -0
  17. data/lib/active_merchant/billing/gateway.rb +268 -0
  18. data/lib/active_merchant/billing/gateways.rb +17 -0
  19. data/lib/active_merchant/billing/gateways/adyen.rb +209 -0
  20. data/lib/active_merchant/billing/gateways/alfabank.rb +117 -0
  21. data/lib/active_merchant/billing/gateways/app55.rb +176 -0
  22. data/lib/active_merchant/billing/gateways/authorize_net.rb +419 -0
  23. data/lib/active_merchant/billing/gateways/authorize_net_arb.rb +417 -0
  24. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +976 -0
  25. data/lib/active_merchant/billing/gateways/balanced.rb +256 -0
  26. data/lib/active_merchant/billing/gateways/bank_frick.rb +225 -0
  27. data/lib/active_merchant/billing/gateways/banwire.rb +105 -0
  28. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +314 -0
  29. data/lib/active_merchant/billing/gateways/barclays_epdq_extra_plus.rb +15 -0
  30. data/lib/active_merchant/billing/gateways/be2bill.rb +131 -0
  31. data/lib/active_merchant/billing/gateways/beanstream.rb +188 -0
  32. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +393 -0
  33. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
  34. data/lib/active_merchant/billing/gateways/blue_pay.rb +506 -0
  35. data/lib/active_merchant/billing/gateways/bogus.rb +140 -0
  36. data/lib/active_merchant/billing/gateways/borgun.rb +210 -0
  37. data/lib/active_merchant/billing/gateways/braintree.rb +19 -0
  38. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
  39. data/lib/active_merchant/billing/gateways/braintree_blue.rb +515 -0
  40. data/lib/active_merchant/billing/gateways/braintree_orange.rb +20 -0
  41. data/lib/active_merchant/billing/gateways/bridge_pay.rb +189 -0
  42. data/lib/active_merchant/billing/gateways/card_save.rb +23 -0
  43. data/lib/active_merchant/billing/gateways/card_stream.rb +220 -0
  44. data/lib/active_merchant/billing/gateways/cashnet.rb +191 -0
  45. data/lib/active_merchant/billing/gateways/cc5.rb +201 -0
  46. data/lib/active_merchant/billing/gateways/cecabank.rb +229 -0
  47. data/lib/active_merchant/billing/gateways/certo_direct.rb +278 -0
  48. data/lib/active_merchant/billing/gateways/checkout.rb +213 -0
  49. data/lib/active_merchant/billing/gateways/commercegate.rb +143 -0
  50. data/lib/active_merchant/billing/gateways/conekta.rb +209 -0
  51. data/lib/active_merchant/billing/gateways/cyber_source.rb +709 -0
  52. data/lib/active_merchant/billing/gateways/data_cash.rb +600 -0
  53. data/lib/active_merchant/billing/gateways/efsnet.rb +219 -0
  54. data/lib/active_merchant/billing/gateways/elavon.rb +348 -0
  55. data/lib/active_merchant/billing/gateways/epay.rb +275 -0
  56. data/lib/active_merchant/billing/gateways/evo_ca.rb +308 -0
  57. data/lib/active_merchant/billing/gateways/eway.rb +214 -0
  58. data/lib/active_merchant/billing/gateways/eway_managed.rb +291 -0
  59. data/lib/active_merchant/billing/gateways/eway_rapid.rb +524 -0
  60. data/lib/active_merchant/billing/gateways/exact.rb +218 -0
  61. data/lib/active_merchant/billing/gateways/fat_zebra.rb +173 -0
  62. data/lib/active_merchant/billing/gateways/federated_canada.rb +160 -0
  63. data/lib/active_merchant/billing/gateways/finansbank.rb +23 -0
  64. data/lib/active_merchant/billing/gateways/first_giving.rb +143 -0
  65. data/lib/active_merchant/billing/gateways/first_pay.rb +160 -0
  66. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +355 -0
  67. data/lib/active_merchant/billing/gateways/garanti.rb +257 -0
  68. data/lib/active_merchant/billing/gateways/global_transport.rb +183 -0
  69. data/lib/active_merchant/billing/gateways/hdfc.rb +207 -0
  70. data/lib/active_merchant/billing/gateways/hps.rb +288 -0
  71. data/lib/active_merchant/billing/gateways/iats_payments.rb +251 -0
  72. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +246 -0
  73. data/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem +13 -0
  74. data/lib/active_merchant/billing/gateways/ideal/ideal_response.rb +29 -0
  75. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +66 -0
  76. data/lib/active_merchant/billing/gateways/inspire.rb +213 -0
  77. data/lib/active_merchant/billing/gateways/instapay.rb +163 -0
  78. data/lib/active_merchant/billing/gateways/iridium.rb +457 -0
  79. data/lib/active_merchant/billing/gateways/itransact.rb +448 -0
  80. data/lib/active_merchant/billing/gateways/jetpay.rb +275 -0
  81. data/lib/active_merchant/billing/gateways/linkpoint.rb +438 -0
  82. data/lib/active_merchant/billing/gateways/litle.rb +346 -0
  83. data/lib/active_merchant/billing/gateways/maxipago.rb +197 -0
  84. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +170 -0
  85. data/lib/active_merchant/billing/gateways/merchant_one.rb +114 -0
  86. data/lib/active_merchant/billing/gateways/merchant_ware.rb +319 -0
  87. data/lib/active_merchant/billing/gateways/merchant_ware_version_four.rb +268 -0
  88. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +195 -0
  89. data/lib/active_merchant/billing/gateways/mercury.rb +333 -0
  90. data/lib/active_merchant/billing/gateways/metrics_global.rb +303 -0
  91. data/lib/active_merchant/billing/gateways/migs.rb +265 -0
  92. data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +100 -0
  93. data/lib/active_merchant/billing/gateways/modern_payments.rb +37 -0
  94. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +219 -0
  95. data/lib/active_merchant/billing/gateways/moneris.rb +309 -0
  96. data/lib/active_merchant/billing/gateways/moneris_us.rb +291 -0
  97. data/lib/active_merchant/billing/gateways/money_movers.rb +152 -0
  98. data/lib/active_merchant/billing/gateways/nab_transact.rb +280 -0
  99. data/lib/active_merchant/billing/gateways/net_registry.rb +198 -0
  100. data/lib/active_merchant/billing/gateways/netaxept.rb +181 -0
  101. data/lib/active_merchant/billing/gateways/netbilling.rb +190 -0
  102. data/lib/active_merchant/billing/gateways/netpay.rb +223 -0
  103. data/lib/active_merchant/billing/gateways/network_merchants.rb +242 -0
  104. data/lib/active_merchant/billing/gateways/nmi.rb +256 -0
  105. data/lib/active_merchant/billing/gateways/ogone.rb +435 -0
  106. data/lib/active_merchant/billing/gateways/openpay.rb +194 -0
  107. data/lib/active_merchant/billing/gateways/optimal_payment.rb +313 -0
  108. data/lib/active_merchant/billing/gateways/orbital.rb +803 -0
  109. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +47 -0
  110. data/lib/active_merchant/billing/gateways/pac_net_raven.rb +207 -0
  111. data/lib/active_merchant/billing/gateways/pago_facil.rb +122 -0
  112. data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +261 -0
  113. data/lib/active_merchant/billing/gateways/pay_junction.rb +390 -0
  114. data/lib/active_merchant/billing/gateways/pay_secure.rb +112 -0
  115. data/lib/active_merchant/billing/gateways/pay_u_latam.rb +462 -0
  116. data/lib/active_merchant/billing/gateways/paybox_direct.rb +188 -0
  117. data/lib/active_merchant/billing/gateways/payex.rb +412 -0
  118. data/lib/active_merchant/billing/gateways/payflow.rb +304 -0
  119. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +209 -0
  120. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
  121. data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
  122. data/lib/active_merchant/billing/gateways/payflow_express.rb +224 -0
  123. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
  124. data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
  125. data/lib/active_merchant/billing/gateways/payment_express.rb +353 -0
  126. data/lib/active_merchant/billing/gateways/paymill.rb +281 -0
  127. data/lib/active_merchant/billing/gateways/paypal.rb +117 -0
  128. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +670 -0
  129. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +65 -0
  130. data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +262 -0
  131. data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
  132. data/lib/active_merchant/billing/gateways/paypal_digital_goods.rb +44 -0
  133. data/lib/active_merchant/billing/gateways/paypal_express.rb +264 -0
  134. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +30 -0
  135. data/lib/active_merchant/billing/gateways/payscout.rb +162 -0
  136. data/lib/active_merchant/billing/gateways/paystation.rb +199 -0
  137. data/lib/active_merchant/billing/gateways/payway.rb +207 -0
  138. data/lib/active_merchant/billing/gateways/pin.rb +197 -0
  139. data/lib/active_merchant/billing/gateways/plugnpay.rb +283 -0
  140. data/lib/active_merchant/billing/gateways/psigate.rb +216 -0
  141. data/lib/active_merchant/billing/gateways/psl_card.rb +303 -0
  142. data/lib/active_merchant/billing/gateways/qbms.rb +292 -0
  143. data/lib/active_merchant/billing/gateways/quantum.rb +276 -0
  144. data/lib/active_merchant/billing/gateways/quickpay.rb +367 -0
  145. data/lib/active_merchant/billing/gateways/realex.rb +298 -0
  146. data/lib/active_merchant/billing/gateways/redsys.rb +391 -0
  147. data/lib/active_merchant/billing/gateways/sage.rb +175 -0
  148. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +87 -0
  149. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +114 -0
  150. data/lib/active_merchant/billing/gateways/sage/sage_vault.rb +149 -0
  151. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +102 -0
  152. data/lib/active_merchant/billing/gateways/sage_pay.rb +398 -0
  153. data/lib/active_merchant/billing/gateways/sallie_mae.rb +143 -0
  154. data/lib/active_merchant/billing/gateways/secure_net.rb +252 -0
  155. data/lib/active_merchant/billing/gateways/secure_pay.rb +201 -0
  156. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +281 -0
  157. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +105 -0
  158. data/lib/active_merchant/billing/gateways/skip_jack.rb +452 -0
  159. data/lib/active_merchant/billing/gateways/smart_ps.rb +283 -0
  160. data/lib/active_merchant/billing/gateways/so_easy_pay.rb +194 -0
  161. data/lib/active_merchant/billing/gateways/spreedly_core.rb +247 -0
  162. data/lib/active_merchant/billing/gateways/stripe.rb +411 -0
  163. data/lib/active_merchant/billing/gateways/swipe_checkout.rb +157 -0
  164. data/lib/active_merchant/billing/gateways/tns.rb +227 -0
  165. data/lib/active_merchant/billing/gateways/trans_first.rb +126 -0
  166. data/lib/active_merchant/billing/gateways/transax.rb +23 -0
  167. data/lib/active_merchant/billing/gateways/transnational.rb +10 -0
  168. data/lib/active_merchant/billing/gateways/trust_commerce.rb +416 -0
  169. data/lib/active_merchant/billing/gateways/usa_epay.rb +25 -0
  170. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +1516 -0
  171. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +254 -0
  172. data/lib/active_merchant/billing/gateways/verifi.rb +225 -0
  173. data/lib/active_merchant/billing/gateways/viaklix.rb +183 -0
  174. data/lib/active_merchant/billing/gateways/vindicia.rb +385 -0
  175. data/lib/active_merchant/billing/gateways/webpay.rb +97 -0
  176. data/lib/active_merchant/billing/gateways/wepay.rb +189 -0
  177. data/lib/active_merchant/billing/gateways/wirecard.rb +421 -0
  178. data/lib/active_merchant/billing/gateways/worldpay.rb +331 -0
  179. data/lib/active_merchant/billing/gateways/worldpay_us.rb +181 -0
  180. data/lib/active_merchant/billing/model.rb +30 -0
  181. data/lib/active_merchant/billing/payment_token.rb +21 -0
  182. data/lib/active_merchant/billing/rails.rb +3 -0
  183. data/lib/active_merchant/billing/response.rb +91 -0
  184. data/lib/active_merchant/country.rb +332 -0
  185. data/lib/active_merchant/empty.rb +20 -0
  186. data/lib/active_merchant/errors.rb +29 -0
  187. data/lib/active_merchant/offsite_payments_shim.rb +19 -0
  188. data/lib/active_merchant/version.rb +3 -0
  189. data/lib/activemerchant.rb +1 -0
  190. data/lib/support/gateway_support.rb +71 -0
  191. data/lib/support/outbound_hosts.rb +25 -0
  192. data/lib/support/ssl_verify.rb +93 -0
  193. metadata +400 -0
@@ -0,0 +1,304 @@
1
+ require File.dirname(__FILE__) + '/payflow/payflow_common_api'
2
+ require File.dirname(__FILE__) + '/payflow/payflow_response'
3
+ require File.dirname(__FILE__) + '/payflow_express'
4
+
5
+ module ActiveMerchant #:nodoc:
6
+ module Billing #:nodoc:
7
+ class PayflowGateway < Gateway
8
+ include PayflowCommonAPI
9
+
10
+ RECURRING_ACTIONS = Set.new([:add, :modify, :cancel, :inquiry, :reactivate, :payment])
11
+
12
+ self.supported_cardtypes = [:visa, :master, :american_express, :jcb, :discover, :diners_club]
13
+ self.homepage_url = 'https://www.paypal.com/cgi-bin/webscr?cmd=_payflow-pro-overview-outside'
14
+ self.display_name = 'PayPal Payflow Pro'
15
+
16
+ def authorize(money, credit_card_or_reference, options = {})
17
+ request = build_sale_or_authorization_request(:authorization, money, credit_card_or_reference, options)
18
+
19
+ commit(request, options)
20
+ end
21
+
22
+ def purchase(money, funding_source, options = {})
23
+ request = build_sale_or_authorization_request(:purchase, money, funding_source, options)
24
+
25
+ commit(request, options)
26
+ end
27
+
28
+ def credit(money, funding_source, options = {})
29
+ if funding_source.is_a?(String)
30
+ ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE
31
+ # Perform referenced credit
32
+ refund(money, funding_source, options)
33
+ elsif card_brand(funding_source) == 'check'
34
+ # Perform non-referenced credit
35
+ request = build_check_request(:credit, money, funding_source, options)
36
+ commit(request, options)
37
+ else
38
+ request = build_credit_card_request(:credit, money, funding_source, options)
39
+ commit(request, options)
40
+ end
41
+ end
42
+
43
+ def refund(money, reference, options = {})
44
+ commit(build_reference_request(:credit, money, reference, options), options)
45
+ end
46
+
47
+ # Adds or modifies a recurring Payflow profile. See the Payflow Pro Recurring Billing Guide for more details:
48
+ # https://www.paypal.com/en_US/pdf/PayflowPro_RecurringBilling_Guide.pdf
49
+ #
50
+ # Several options are available to customize the recurring profile:
51
+ #
52
+ # * <tt>profile_id</tt> - is only required for editing a recurring profile
53
+ # * <tt>starting_at</tt> - takes a Date, Time, or string in mmddyyyy format. The date must be in the future.
54
+ # * <tt>name</tt> - The name of the customer to be billed. If not specified, the name from the credit card is used.
55
+ # * <tt>periodicity</tt> - The frequency that the recurring payments will occur at. Can be one of
56
+ # :bimonthly, :monthly, :biweekly, :weekly, :yearly, :daily, :semimonthly, :quadweekly, :quarterly, :semiyearly
57
+ # * <tt>payments</tt> - The term, or number of payments that will be made
58
+ # * <tt>comment</tt> - A comment associated with the profile
59
+ def recurring(money, credit_card, options = {})
60
+ ActiveMerchant.deprecated RECURRING_DEPRECATION_MESSAGE
61
+
62
+ options[:name] = credit_card.name if options[:name].blank? && credit_card
63
+ request = build_recurring_request(options[:profile_id] ? :modify : :add, money, options) do |xml|
64
+ add_credit_card(xml, credit_card) if credit_card
65
+ end
66
+ commit(request, options.merge(:request_type => :recurring))
67
+ end
68
+
69
+ def cancel_recurring(profile_id)
70
+ ActiveMerchant.deprecated RECURRING_DEPRECATION_MESSAGE
71
+
72
+ request = build_recurring_request(:cancel, 0, :profile_id => profile_id)
73
+ commit(request, options.merge(:request_type => :recurring))
74
+ end
75
+
76
+ def recurring_inquiry(profile_id, options = {})
77
+ ActiveMerchant.deprecated RECURRING_DEPRECATION_MESSAGE
78
+
79
+ request = build_recurring_request(:inquiry, nil, options.update( :profile_id => profile_id ))
80
+ commit(request, options.merge(:request_type => :recurring))
81
+ end
82
+
83
+ def express
84
+ @express ||= PayflowExpressGateway.new(@options)
85
+ end
86
+
87
+ private
88
+ def build_sale_or_authorization_request(action, money, funding_source, options)
89
+ if funding_source.is_a?(String)
90
+ build_reference_sale_or_authorization_request(action, money, funding_source, options)
91
+ elsif card_brand(funding_source) == 'check'
92
+ build_check_request(action, money, funding_source, options)
93
+ else
94
+ build_credit_card_request(action, money, funding_source, options)
95
+ end
96
+ end
97
+
98
+ def build_reference_sale_or_authorization_request(action, money, reference, options)
99
+ xml = Builder::XmlMarkup.new
100
+ xml.tag! TRANSACTIONS[action] do
101
+ xml.tag! 'PayData' do
102
+ xml.tag! 'Invoice' do
103
+ # Fields accepted by PayFlow and recommended to be provided even for Reference Transaction, per Payflow docs.
104
+ xml.tag! 'CustIP', options[:ip] unless options[:ip].blank?
105
+ xml.tag! 'InvNum', options[:order_id].to_s.gsub(/[^\w.]/, '') unless options[:order_id].blank?
106
+ xml.tag! 'Description', options[:description] unless options[:description].blank?
107
+ xml.tag! 'Comment', options[:comment] unless options[:comment].blank?
108
+ xml.tag!('ExtData', 'Name'=> 'COMMENT2', 'Value'=> options[:comment2]) unless options[:comment2].blank?
109
+ xml.tag! 'TaxAmt', options[:taxamt] unless options[:taxamt].blank?
110
+ xml.tag! 'FreightAmt', options[:freightamt] unless options[:freightamt].blank?
111
+ xml.tag! 'DutyAmt', options[:dutyamt] unless options[:dutyamt].blank?
112
+ xml.tag! 'DiscountAmt', options[:discountamt] unless options[:discountamt].blank?
113
+
114
+ billing_address = options[:billing_address] || options[:address]
115
+ add_address(xml, 'BillTo', billing_address, options) if billing_address
116
+ add_address(xml, 'ShipTo', options[:shipping_address],options) if options[:shipping_address]
117
+
118
+ xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
119
+ end
120
+ xml.tag! 'Tender' do
121
+ xml.tag! 'Card' do
122
+ xml.tag! 'ExtData', 'Name' => 'ORIGID', 'Value' => reference
123
+ end
124
+ end
125
+ end
126
+ end
127
+ xml.target!
128
+ end
129
+
130
+ def build_credit_card_request(action, money, credit_card, options)
131
+ xml = Builder::XmlMarkup.new
132
+ xml.tag! TRANSACTIONS[action] do
133
+ xml.tag! 'PayData' do
134
+ xml.tag! 'Invoice' do
135
+ xml.tag! 'CustIP', options[:ip] unless options[:ip].blank?
136
+ xml.tag! 'InvNum', options[:order_id].to_s.gsub(/[^\w.]/, '') unless options[:order_id].blank?
137
+ xml.tag! 'Description', options[:description] unless options[:description].blank?
138
+ # Comment and Comment2 will show up in manager.paypal.com as Comment1 and Comment2
139
+ xml.tag! 'Comment', options[:comment] unless options[:comment].blank?
140
+ xml.tag!('ExtData', 'Name'=> 'COMMENT2', 'Value'=> options[:comment2]) unless options[:comment2].blank?
141
+ xml.tag! 'TaxAmt', options[:taxamt] unless options[:taxamt].blank?
142
+ xml.tag! 'FreightAmt', options[:freightamt] unless options[:freightamt].blank?
143
+ xml.tag! 'DutyAmt', options[:dutyamt] unless options[:dutyamt].blank?
144
+ xml.tag! 'DiscountAmt', options[:discountamt] unless options[:discountamt].blank?
145
+
146
+ billing_address = options[:billing_address] || options[:address]
147
+ add_address(xml, 'BillTo', billing_address, options) if billing_address
148
+ add_address(xml, 'ShipTo', options[:shipping_address], options) if options[:shipping_address]
149
+
150
+ xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
151
+ end
152
+
153
+ xml.tag! 'Tender' do
154
+ add_credit_card(xml, credit_card)
155
+ end
156
+ end
157
+ end
158
+ xml.target!
159
+ end
160
+
161
+ def build_check_request(action, money, check, options)
162
+ xml = Builder::XmlMarkup.new
163
+ xml.tag! TRANSACTIONS[action] do
164
+ xml.tag! 'PayData' do
165
+ xml.tag! 'Invoice' do
166
+ xml.tag! 'CustIP', options[:ip] unless options[:ip].blank?
167
+ xml.tag! 'InvNum', options[:order_id].to_s.gsub(/[^\w.]/, '') unless options[:order_id].blank?
168
+ xml.tag! 'Description', options[:description] unless options[:description].blank?
169
+ xml.tag! 'BillTo' do
170
+ xml.tag! 'Name', check.name
171
+ end
172
+ xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
173
+ end
174
+ xml.tag! 'Tender' do
175
+ xml.tag! 'ACH' do
176
+ xml.tag! 'AcctType', check.account_type == 'checking' ? 'C' : 'S'
177
+ xml.tag! 'AcctNum', check.account_number
178
+ xml.tag! 'ABA', check.routing_number
179
+ end
180
+ end
181
+ end
182
+ end
183
+ xml.target!
184
+ end
185
+
186
+ def add_credit_card(xml, credit_card)
187
+ xml.tag! 'Card' do
188
+ xml.tag! 'CardType', credit_card_type(credit_card)
189
+ xml.tag! 'CardNum', credit_card.number
190
+ xml.tag! 'ExpDate', expdate(credit_card)
191
+ xml.tag! 'NameOnCard', credit_card.first_name
192
+ xml.tag! 'CVNum', credit_card.verification_value if credit_card.verification_value?
193
+
194
+ if requires_start_date_or_issue_number?(credit_card)
195
+ xml.tag!('ExtData', 'Name' => 'CardStart', 'Value' => startdate(credit_card)) unless credit_card.start_month.blank? || credit_card.start_year.blank?
196
+ xml.tag!('ExtData', 'Name' => 'CardIssue', 'Value' => format(credit_card.issue_number, :two_digits)) unless credit_card.issue_number.blank?
197
+ end
198
+ xml.tag! 'ExtData', 'Name' => 'LASTNAME', 'Value' => credit_card.last_name
199
+ end
200
+ end
201
+
202
+ def credit_card_type(credit_card)
203
+ return '' if card_brand(credit_card).blank?
204
+
205
+ CARD_MAPPING[card_brand(credit_card).to_sym]
206
+ end
207
+
208
+ def expdate(creditcard)
209
+ year = sprintf("%.4i", creditcard.year.to_s.sub(/^0+/, ''))
210
+ month = sprintf("%.2i", creditcard.month.to_s.sub(/^0+/, ''))
211
+
212
+ "#{year}#{month}"
213
+ end
214
+
215
+ def startdate(creditcard)
216
+ year = format(creditcard.start_year, :two_digits)
217
+ month = format(creditcard.start_month, :two_digits)
218
+
219
+ "#{month}#{year}"
220
+ end
221
+
222
+ def build_recurring_request(action, money, options)
223
+ unless RECURRING_ACTIONS.include?(action)
224
+ raise StandardError, "Invalid Recurring Profile Action: #{action}"
225
+ end
226
+
227
+ xml = Builder::XmlMarkup.new
228
+ xml.tag! 'RecurringProfiles' do
229
+ xml.tag! 'RecurringProfile' do
230
+ xml.tag! action.to_s.capitalize do
231
+ unless [:cancel, :inquiry].include?(action)
232
+ xml.tag! 'RPData' do
233
+ xml.tag! 'Name', options[:name] unless options[:name].nil?
234
+ xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
235
+ xml.tag! 'PayPeriod', get_pay_period(options)
236
+ xml.tag! 'Term', options[:payments] unless options[:payments].nil?
237
+ xml.tag! 'Comment', options[:comment] unless options[:comment].nil?
238
+ xml.tag! 'RetryNumDays', options[:retry_num_days] unless options[:retry_num_days].nil?
239
+ xml.tag! 'MaxFailPayments', options[:max_fail_payments] unless options[:max_fail_payments].nil?
240
+
241
+ if initial_tx = options[:initial_transaction]
242
+ requires!(initial_tx, [:type, :authorization, :purchase])
243
+ requires!(initial_tx, :amount) if initial_tx[:type] == :purchase
244
+
245
+ xml.tag! 'OptionalTrans', TRANSACTIONS[initial_tx[:type]]
246
+ xml.tag! 'OptionalTransAmt', amount(initial_tx[:amount]) unless initial_tx[:amount].blank?
247
+ end
248
+
249
+ if action == :add
250
+ xml.tag! 'Start', format_rp_date(options[:starting_at] || Date.today + 1 )
251
+ else
252
+ xml.tag! 'Start', format_rp_date(options[:starting_at]) unless options[:starting_at].nil?
253
+ end
254
+
255
+ xml.tag! 'EMail', options[:email] unless options[:email].nil?
256
+
257
+ billing_address = options[:billing_address] || options[:address]
258
+ add_address(xml, 'BillTo', billing_address, options) if billing_address
259
+ add_address(xml, 'ShipTo', options[:shipping_address], options) if options[:shipping_address]
260
+ end
261
+ xml.tag! 'Tender' do
262
+ yield xml
263
+ end
264
+ end
265
+ if action != :add
266
+ xml.tag! "ProfileID", options[:profile_id]
267
+ end
268
+ if action == :inquiry
269
+ xml.tag! "PaymentHistory", ( options[:history] ? 'Y' : 'N' )
270
+ end
271
+ end
272
+ end
273
+ end
274
+ end
275
+
276
+ def get_pay_period(options)
277
+ requires!(options, [:periodicity, :bimonthly, :monthly, :biweekly, :weekly, :yearly, :daily, :semimonthly, :quadweekly, :quarterly, :semiyearly])
278
+ case options[:periodicity]
279
+ when :weekly then 'Weekly'
280
+ when :biweekly then 'Bi-weekly'
281
+ when :semimonthly then 'Semi-monthly'
282
+ when :quadweekly then 'Every four weeks'
283
+ when :monthly then 'Monthly'
284
+ when :quarterly then 'Quarterly'
285
+ when :semiyearly then 'Semi-yearly'
286
+ when :yearly then 'Yearly'
287
+ end
288
+ end
289
+
290
+ def format_rp_date(time)
291
+ case time
292
+ when Time, Date then time.strftime("%m%d%Y")
293
+ else
294
+ time.to_s
295
+ end
296
+ end
297
+
298
+ def build_response(success, message, response, options = {})
299
+ PayflowResponse.new(success, message, response, options)
300
+ end
301
+ end
302
+ end
303
+ end
304
+
@@ -0,0 +1,209 @@
1
+ require 'nokogiri'
2
+ module ActiveMerchant #:nodoc:
3
+ module Billing #:nodoc:
4
+ module PayflowCommonAPI
5
+ def self.included(base)
6
+ base.default_currency = 'USD'
7
+
8
+ base.class_attribute :partner
9
+
10
+ # Set the default partner to PayPal
11
+ base.partner = 'PayPal'
12
+
13
+ base.supported_countries = ['US', 'CA', 'SG', 'AU']
14
+
15
+ base.class_attribute :timeout
16
+ base.timeout = 60
17
+
18
+ base.test_url = 'https://pilot-payflowpro.paypal.com'
19
+ base.live_url = 'https://payflowpro.paypal.com'
20
+
21
+ # Enable safe retry of failed connections
22
+ # Payflow is safe to retry because retried transactions use the same
23
+ # X-VPS-Request-ID header. If a transaction is detected as a duplicate
24
+ # only the original transaction data will be used by Payflow, and the
25
+ # subsequent Responses will have a :duplicate parameter set in the params
26
+ # hash.
27
+ base.retry_safe = true
28
+ end
29
+
30
+ XMLNS = 'http://www.paypal.com/XMLPay'
31
+
32
+ CARD_MAPPING = {
33
+ :visa => 'Visa',
34
+ :master => 'MasterCard',
35
+ :discover => 'Discover',
36
+ :american_express => 'Amex',
37
+ :jcb => 'JCB',
38
+ :diners_club => 'DinersClub',
39
+ :switch => 'Switch',
40
+ :solo => 'Solo'
41
+ }
42
+
43
+ TRANSACTIONS = {
44
+ :purchase => "Sale",
45
+ :authorization => "Authorization",
46
+ :capture => "Capture",
47
+ :void => "Void",
48
+ :credit => "Credit"
49
+ }
50
+
51
+ CVV_CODE = {
52
+ 'Match' => 'M',
53
+ 'No Match' => 'N',
54
+ 'Service Not Available' => 'U',
55
+ 'Service not Requested' => 'P'
56
+ }
57
+
58
+ def initialize(options = {})
59
+ requires!(options, :login, :password)
60
+
61
+ options[:partner] = partner if options[:partner].blank?
62
+ super
63
+ end
64
+
65
+ def capture(money, authorization, options = {})
66
+ request = build_reference_request(:capture, money, authorization, options)
67
+ commit(request, options)
68
+ end
69
+
70
+ def void(authorization, options = {})
71
+ request = build_reference_request(:void, nil, authorization, options)
72
+ commit(request, options)
73
+ end
74
+
75
+ private
76
+ def build_request(body, options = {})
77
+ xml = Builder::XmlMarkup.new
78
+ xml.instruct!
79
+ xml.tag! 'XMLPayRequest', 'Timeout' => timeout.to_s, 'version' => "2.1", "xmlns" => XMLNS do
80
+ xml.tag! 'RequestData' do
81
+ xml.tag! 'Vendor', @options[:login]
82
+ xml.tag! 'Partner', @options[:partner]
83
+ if options[:request_type] == :recurring
84
+ xml << body
85
+ else
86
+ xml.tag! 'Transactions' do
87
+ xml.tag! 'Transaction', 'CustRef' => options[:customer] do
88
+ xml.tag! 'Verbosity', @options[:verbosity] || 'MEDIUM'
89
+ xml << body
90
+ end
91
+ end
92
+ end
93
+ end
94
+ xml.tag! 'RequestAuth' do
95
+ xml.tag! 'UserPass' do
96
+ xml.tag! 'User', !@options[:user].blank? ? @options[:user] : @options[:login]
97
+ xml.tag! 'Password', @options[:password]
98
+ end
99
+ end
100
+ end
101
+ xml.target!
102
+ end
103
+
104
+ def build_reference_request(action, money, authorization, options)
105
+ xml = Builder::XmlMarkup.new
106
+ xml.tag! TRANSACTIONS[action] do
107
+ xml.tag! 'PNRef', authorization
108
+
109
+ unless money.nil?
110
+ xml.tag! 'Invoice' do
111
+ xml.tag!('TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money))
112
+ xml.tag!('Description', options[:description]) unless options[:description].blank?
113
+ xml.tag!('Comment', options[:comment]) unless options[:comment].blank?
114
+ xml.tag!('ExtData', 'Name'=> 'COMMENT2', 'Value'=> options[:comment2]) unless options[:comment2].blank?
115
+ end
116
+ end
117
+ end
118
+
119
+ xml.target!
120
+ end
121
+
122
+ def add_address(xml, tag, address, options)
123
+ return if address.nil?
124
+ xml.tag! tag do
125
+ xml.tag! 'Name', address[:name] unless address[:name].blank?
126
+ xml.tag! 'EMail', options[:email] unless options[:email].blank?
127
+ xml.tag! 'Phone', address[:phone] unless address[:phone].blank?
128
+ xml.tag! 'CustCode', options[:customer] if !options[:customer].blank? && tag == 'BillTo'
129
+ xml.tag! 'PONum', options[:po_number] if !options[:po_number].blank? && tag == 'BillTo'
130
+
131
+ xml.tag! 'Address' do
132
+ xml.tag! 'Street', address[:address1] unless address[:address1].blank?
133
+ xml.tag! 'City', address[:city] unless address[:city].blank?
134
+ xml.tag! 'State', address[:state].blank? ? "N/A" : address[:state]
135
+ xml.tag! 'Country', address[:country] unless address[:country].blank?
136
+ xml.tag! 'Zip', address[:zip] unless address[:zip].blank?
137
+ end
138
+ end
139
+ end
140
+
141
+ def parse(data)
142
+ response = {}
143
+ xml = Nokogiri::XML(data)
144
+ xml.remove_namespaces!
145
+ root = xml.xpath("//ResponseData")
146
+
147
+ # REXML::XPath in Ruby 1.8.6 is now unable to match nodes based on their attributes
148
+ tx_result = root.xpath(".//TransactionResult").first
149
+
150
+ if tx_result && tx_result.attributes['Duplicate'].to_s == "true"
151
+ response[:duplicate] = true
152
+ end
153
+
154
+ root.xpath(".//*").each do |node|
155
+ parse_element(response, node)
156
+ end
157
+
158
+ response
159
+ end
160
+
161
+ def parse_element(response, node)
162
+ node_name = node.name.underscore.to_sym
163
+ case
164
+ when node_name == :rp_payment_result
165
+ # Since we'll have multiple history items, we can't just flatten everything
166
+ # down as we do everywhere else. RPPaymentResult elements are not contained
167
+ # in an RPPaymentResults element so we'll come here multiple times
168
+ response[node_name] ||= []
169
+ response[node_name] << ( payment_result_response = {} )
170
+ node.xpath(".//*").each{ |e| parse_element(payment_result_response, e) }
171
+ when node.xpath(".//*").to_a.any?
172
+ node.xpath(".//*").each{|e| parse_element(response, e) }
173
+ when node_name.to_s =~ /amt$/
174
+ # *Amt elements don't put the value in the #text - instead they use a Currency attribute
175
+ response[node_name] = node.attributes['Currency'].to_s
176
+ when node_name == :ext_data
177
+ response[node.attributes['Name'].to_s.underscore.to_sym] = node.attributes['Value'].to_s
178
+ else
179
+ response[node_name] = node.text
180
+ end
181
+ end
182
+
183
+ def build_headers(content_length)
184
+ {
185
+ "Content-Type" => "text/xml",
186
+ "Content-Length" => content_length.to_s,
187
+ "X-VPS-Client-Timeout" => timeout.to_s,
188
+ "X-VPS-VIT-Integration-Product" => "ActiveMerchant",
189
+ "X-VPS-VIT-Runtime-Version" => RUBY_VERSION,
190
+ "X-VPS-Request-ID" => SecureRandom.hex(16)
191
+ }
192
+ end
193
+
194
+ def commit(request_body, options = {})
195
+ request = build_request(request_body, options)
196
+ headers = build_headers(request.size)
197
+
198
+ response = parse(ssl_post(test? ? self.test_url : self.live_url, request, headers))
199
+
200
+ build_response(response[:result] == "0", response[:message], response,
201
+ :test => test?,
202
+ :authorization => response[:pn_ref] || response[:rp_ref],
203
+ :cvv_result => CVV_CODE[response[:cv_result]],
204
+ :avs_result => { :code => response[:avs_result] }
205
+ )
206
+ end
207
+ end
208
+ end
209
+ end