start_activemerchant 1.50.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 (218) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +1769 -0
  3. data/CONTRIBUTORS +540 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +226 -0
  6. data/lib/active_merchant.rb +67 -0
  7. data/lib/active_merchant/billing.rb +15 -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 +404 -0
  14. data/lib/active_merchant/billing/credit_card_formatting.rb +24 -0
  15. data/lib/active_merchant/billing/credit_card_methods.rb +195 -0
  16. data/lib/active_merchant/billing/cvv_result.rb +38 -0
  17. data/lib/active_merchant/billing/gateway.rb +291 -0
  18. data/lib/active_merchant/billing/gateways.rb +14 -0
  19. data/lib/active_merchant/billing/gateways/allied_wallet.rb +203 -0
  20. data/lib/active_merchant/billing/gateways/app55.rb +176 -0
  21. data/lib/active_merchant/billing/gateways/authorize_net.rb +510 -0
  22. data/lib/active_merchant/billing/gateways/authorize_net_arb.rb +417 -0
  23. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +976 -0
  24. data/lib/active_merchant/billing/gateways/axcessms.rb +181 -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 +192 -0
  32. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +389 -0
  33. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +58 -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 +211 -0
  37. data/lib/active_merchant/billing/gateways/bpoint.rb +277 -0
  38. data/lib/active_merchant/billing/gateways/braintree.rb +19 -0
  39. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
  40. data/lib/active_merchant/billing/gateways/braintree_blue.rb +574 -0
  41. data/lib/active_merchant/billing/gateways/braintree_orange.rb +20 -0
  42. data/lib/active_merchant/billing/gateways/bridge_pay.rb +189 -0
  43. data/lib/active_merchant/billing/gateways/card_save.rb +23 -0
  44. data/lib/active_merchant/billing/gateways/card_stream.rb +238 -0
  45. data/lib/active_merchant/billing/gateways/cashnet.rb +202 -0
  46. data/lib/active_merchant/billing/gateways/cc5.rb +201 -0
  47. data/lib/active_merchant/billing/gateways/cecabank.rb +229 -0
  48. data/lib/active_merchant/billing/gateways/cenpos.rb +262 -0
  49. data/lib/active_merchant/billing/gateways/certo_direct.rb +278 -0
  50. data/lib/active_merchant/billing/gateways/checkout.rb +216 -0
  51. data/lib/active_merchant/billing/gateways/checkout_v2.rb +200 -0
  52. data/lib/active_merchant/billing/gateways/commercegate.rb +143 -0
  53. data/lib/active_merchant/billing/gateways/conekta.rb +210 -0
  54. data/lib/active_merchant/billing/gateways/cyber_source.rb +720 -0
  55. data/lib/active_merchant/billing/gateways/data_cash.rb +600 -0
  56. data/lib/active_merchant/billing/gateways/dibs.rb +206 -0
  57. data/lib/active_merchant/billing/gateways/efsnet.rb +219 -0
  58. data/lib/active_merchant/billing/gateways/elavon.rb +348 -0
  59. data/lib/active_merchant/billing/gateways/epay.rb +274 -0
  60. data/lib/active_merchant/billing/gateways/evo_ca.rb +308 -0
  61. data/lib/active_merchant/billing/gateways/eway.rb +214 -0
  62. data/lib/active_merchant/billing/gateways/eway_managed.rb +291 -0
  63. data/lib/active_merchant/billing/gateways/eway_rapid.rb +522 -0
  64. data/lib/active_merchant/billing/gateways/exact.rb +227 -0
  65. data/lib/active_merchant/billing/gateways/ezic.rb +206 -0
  66. data/lib/active_merchant/billing/gateways/fat_zebra.rb +213 -0
  67. data/lib/active_merchant/billing/gateways/federated_canada.rb +160 -0
  68. data/lib/active_merchant/billing/gateways/finansbank.rb +23 -0
  69. data/lib/active_merchant/billing/gateways/first_giving.rb +143 -0
  70. data/lib/active_merchant/billing/gateways/first_pay.rb +160 -0
  71. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +413 -0
  72. data/lib/active_merchant/billing/gateways/flo2cash.rb +215 -0
  73. data/lib/active_merchant/billing/gateways/flo2cash_simple.rb +20 -0
  74. data/lib/active_merchant/billing/gateways/garanti.rb +261 -0
  75. data/lib/active_merchant/billing/gateways/global_transport.rb +179 -0
  76. data/lib/active_merchant/billing/gateways/hdfc.rb +207 -0
  77. data/lib/active_merchant/billing/gateways/hps.rb +287 -0
  78. data/lib/active_merchant/billing/gateways/iats_payments.rb +277 -0
  79. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +246 -0
  80. data/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem +13 -0
  81. data/lib/active_merchant/billing/gateways/ideal/ideal_response.rb +29 -0
  82. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +66 -0
  83. data/lib/active_merchant/billing/gateways/inspire.rb +219 -0
  84. data/lib/active_merchant/billing/gateways/instapay.rb +163 -0
  85. data/lib/active_merchant/billing/gateways/ipp.rb +175 -0
  86. data/lib/active_merchant/billing/gateways/iridium.rb +457 -0
  87. data/lib/active_merchant/billing/gateways/itransact.rb +448 -0
  88. data/lib/active_merchant/billing/gateways/jetpay.rb +275 -0
  89. data/lib/active_merchant/billing/gateways/linkpoint.rb +438 -0
  90. data/lib/active_merchant/billing/gateways/litle.rb +345 -0
  91. data/lib/active_merchant/billing/gateways/maxipago.rb +197 -0
  92. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +170 -0
  93. data/lib/active_merchant/billing/gateways/merchant_one.rb +114 -0
  94. data/lib/active_merchant/billing/gateways/merchant_partners.rb +245 -0
  95. data/lib/active_merchant/billing/gateways/merchant_ware.rb +319 -0
  96. data/lib/active_merchant/billing/gateways/merchant_ware_version_four.rb +268 -0
  97. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +195 -0
  98. data/lib/active_merchant/billing/gateways/mercury.rb +326 -0
  99. data/lib/active_merchant/billing/gateways/metrics_global.rb +303 -0
  100. data/lib/active_merchant/billing/gateways/migs.rb +280 -0
  101. data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +100 -0
  102. data/lib/active_merchant/billing/gateways/modern_payments.rb +37 -0
  103. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +219 -0
  104. data/lib/active_merchant/billing/gateways/monei.rb +307 -0
  105. data/lib/active_merchant/billing/gateways/moneris.rb +309 -0
  106. data/lib/active_merchant/billing/gateways/moneris_us.rb +298 -0
  107. data/lib/active_merchant/billing/gateways/money_movers.rb +152 -0
  108. data/lib/active_merchant/billing/gateways/nab_transact.rb +290 -0
  109. data/lib/active_merchant/billing/gateways/net_registry.rb +198 -0
  110. data/lib/active_merchant/billing/gateways/netaxept.rb +181 -0
  111. data/lib/active_merchant/billing/gateways/netbilling.rb +224 -0
  112. data/lib/active_merchant/billing/gateways/netpay.rb +223 -0
  113. data/lib/active_merchant/billing/gateways/network_merchants.rb +242 -0
  114. data/lib/active_merchant/billing/gateways/nmi.rb +256 -0
  115. data/lib/active_merchant/billing/gateways/ogone.rb +435 -0
  116. data/lib/active_merchant/billing/gateways/omise.rb +319 -0
  117. data/lib/active_merchant/billing/gateways/openpay.rb +194 -0
  118. data/lib/active_merchant/billing/gateways/optimal_payment.rb +314 -0
  119. data/lib/active_merchant/billing/gateways/orbital.rb +834 -0
  120. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +47 -0
  121. data/lib/active_merchant/billing/gateways/pac_net_raven.rb +207 -0
  122. data/lib/active_merchant/billing/gateways/pago_facil.rb +122 -0
  123. data/lib/active_merchant/billing/gateways/pay_conex.rb +246 -0
  124. data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +277 -0
  125. data/lib/active_merchant/billing/gateways/pay_hub.rb +213 -0
  126. data/lib/active_merchant/billing/gateways/pay_junction.rb +390 -0
  127. data/lib/active_merchant/billing/gateways/pay_secure.rb +112 -0
  128. data/lib/active_merchant/billing/gateways/paybox_direct.rb +188 -0
  129. data/lib/active_merchant/billing/gateways/payex.rb +412 -0
  130. data/lib/active_merchant/billing/gateways/payflow.rb +308 -0
  131. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +220 -0
  132. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
  133. data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
  134. data/lib/active_merchant/billing/gateways/payflow_express.rb +224 -0
  135. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
  136. data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
  137. data/lib/active_merchant/billing/gateways/payment_express.rb +353 -0
  138. data/lib/active_merchant/billing/gateways/paymill.rb +282 -0
  139. data/lib/active_merchant/billing/gateways/paypal.rb +129 -0
  140. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +679 -0
  141. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +65 -0
  142. data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +262 -0
  143. data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
  144. data/lib/active_merchant/billing/gateways/paypal_digital_goods.rb +44 -0
  145. data/lib/active_merchant/billing/gateways/paypal_express.rb +264 -0
  146. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +30 -0
  147. data/lib/active_merchant/billing/gateways/payscout.rb +162 -0
  148. data/lib/active_merchant/billing/gateways/paystation.rb +199 -0
  149. data/lib/active_merchant/billing/gateways/payu_in.rb +247 -0
  150. data/lib/active_merchant/billing/gateways/payway.rb +207 -0
  151. data/lib/active_merchant/billing/gateways/pin.rb +207 -0
  152. data/lib/active_merchant/billing/gateways/plugnpay.rb +283 -0
  153. data/lib/active_merchant/billing/gateways/psigate.rb +216 -0
  154. data/lib/active_merchant/billing/gateways/psl_card.rb +303 -0
  155. data/lib/active_merchant/billing/gateways/qbms.rb +292 -0
  156. data/lib/active_merchant/billing/gateways/quantum.rb +276 -0
  157. data/lib/active_merchant/billing/gateways/quickbooks.rb +280 -0
  158. data/lib/active_merchant/billing/gateways/quickpay.rb +26 -0
  159. data/lib/active_merchant/billing/gateways/quickpay/quickpay_common.rb +188 -0
  160. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +240 -0
  161. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v4to7.rb +227 -0
  162. data/lib/active_merchant/billing/gateways/qvalent.rb +179 -0
  163. data/lib/active_merchant/billing/gateways/realex.rb +298 -0
  164. data/lib/active_merchant/billing/gateways/redsys.rb +406 -0
  165. data/lib/active_merchant/billing/gateways/s5.rb +226 -0
  166. data/lib/active_merchant/billing/gateways/sage.rb +173 -0
  167. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +89 -0
  168. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +115 -0
  169. data/lib/active_merchant/billing/gateways/sage/sage_vault.rb +149 -0
  170. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
  171. data/lib/active_merchant/billing/gateways/sage_pay.rb +399 -0
  172. data/lib/active_merchant/billing/gateways/sallie_mae.rb +143 -0
  173. data/lib/active_merchant/billing/gateways/secure_net.rb +263 -0
  174. data/lib/active_merchant/billing/gateways/secure_pay.rb +201 -0
  175. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +281 -0
  176. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +105 -0
  177. data/lib/active_merchant/billing/gateways/skip_jack.rb +451 -0
  178. data/lib/active_merchant/billing/gateways/smart_ps.rb +283 -0
  179. data/lib/active_merchant/billing/gateways/so_easy_pay.rb +194 -0
  180. data/lib/active_merchant/billing/gateways/spreedly_core.rb +247 -0
  181. data/lib/active_merchant/billing/gateways/stripe.rb +489 -0
  182. data/lib/active_merchant/billing/gateways/swipe_checkout.rb +157 -0
  183. data/lib/active_merchant/billing/gateways/tns.rb +227 -0
  184. data/lib/active_merchant/billing/gateways/trans_first.rb +126 -0
  185. data/lib/active_merchant/billing/gateways/transax.rb +23 -0
  186. data/lib/active_merchant/billing/gateways/transnational.rb +10 -0
  187. data/lib/active_merchant/billing/gateways/trust_commerce.rb +416 -0
  188. data/lib/active_merchant/billing/gateways/usa_epay.rb +25 -0
  189. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +1516 -0
  190. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +259 -0
  191. data/lib/active_merchant/billing/gateways/vanco.rb +280 -0
  192. data/lib/active_merchant/billing/gateways/verifi.rb +225 -0
  193. data/lib/active_merchant/billing/gateways/viaklix.rb +183 -0
  194. data/lib/active_merchant/billing/gateways/webpay.rb +97 -0
  195. data/lib/active_merchant/billing/gateways/wepay.rb +205 -0
  196. data/lib/active_merchant/billing/gateways/wirecard.rb +420 -0
  197. data/lib/active_merchant/billing/gateways/worldpay.rb +331 -0
  198. data/lib/active_merchant/billing/gateways/worldpay_online_payments.rb +204 -0
  199. data/lib/active_merchant/billing/gateways/worldpay_us.rb +181 -0
  200. data/lib/active_merchant/billing/model.rb +30 -0
  201. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +24 -0
  202. data/lib/active_merchant/billing/payment_token.rb +21 -0
  203. data/lib/active_merchant/billing/rails.rb +3 -0
  204. data/lib/active_merchant/billing/response.rb +92 -0
  205. data/lib/active_merchant/connection.rb +172 -0
  206. data/lib/active_merchant/country.rb +332 -0
  207. data/lib/active_merchant/empty.rb +20 -0
  208. data/lib/active_merchant/errors.rb +35 -0
  209. data/lib/active_merchant/network_connection_retries.rb +79 -0
  210. data/lib/active_merchant/post_data.rb +24 -0
  211. data/lib/active_merchant/posts_data.rb +84 -0
  212. data/lib/active_merchant/version.rb +3 -0
  213. data/lib/activemerchant.rb +1 -0
  214. data/lib/certs/cacert.pem +3866 -0
  215. data/lib/support/gateway_support.rb +71 -0
  216. data/lib/support/outbound_hosts.rb +28 -0
  217. data/lib/support/ssl_verify.rb +93 -0
  218. metadata +387 -0
@@ -0,0 +1,308 @@
1
+ require 'active_merchant/billing/gateways/payflow/payflow_common_api'
2
+ require 'active_merchant/billing/gateways/payflow/payflow_response'
3
+ require 'active_merchant/billing/gateways/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
+ def verify(payment, options={})
48
+ authorize(0, payment, options)
49
+ end
50
+
51
+ # Adds or modifies a recurring Payflow profile. See the Payflow Pro Recurring Billing Guide for more details:
52
+ # https://www.paypal.com/en_US/pdf/PayflowPro_RecurringBilling_Guide.pdf
53
+ #
54
+ # Several options are available to customize the recurring profile:
55
+ #
56
+ # * <tt>profile_id</tt> - is only required for editing a recurring profile
57
+ # * <tt>starting_at</tt> - takes a Date, Time, or string in mmddyyyy format. The date must be in the future.
58
+ # * <tt>name</tt> - The name of the customer to be billed. If not specified, the name from the credit card is used.
59
+ # * <tt>periodicity</tt> - The frequency that the recurring payments will occur at. Can be one of
60
+ # :bimonthly, :monthly, :biweekly, :weekly, :yearly, :daily, :semimonthly, :quadweekly, :quarterly, :semiyearly
61
+ # * <tt>payments</tt> - The term, or number of payments that will be made
62
+ # * <tt>comment</tt> - A comment associated with the profile
63
+ def recurring(money, credit_card, options = {})
64
+ ActiveMerchant.deprecated RECURRING_DEPRECATION_MESSAGE
65
+
66
+ options[:name] = credit_card.name if options[:name].blank? && credit_card
67
+ request = build_recurring_request(options[:profile_id] ? :modify : :add, money, options) do |xml|
68
+ add_credit_card(xml, credit_card) if credit_card
69
+ end
70
+ commit(request, options.merge(:request_type => :recurring))
71
+ end
72
+
73
+ def cancel_recurring(profile_id)
74
+ ActiveMerchant.deprecated RECURRING_DEPRECATION_MESSAGE
75
+
76
+ request = build_recurring_request(:cancel, 0, :profile_id => profile_id)
77
+ commit(request, options.merge(:request_type => :recurring))
78
+ end
79
+
80
+ def recurring_inquiry(profile_id, options = {})
81
+ ActiveMerchant.deprecated RECURRING_DEPRECATION_MESSAGE
82
+
83
+ request = build_recurring_request(:inquiry, nil, options.update( :profile_id => profile_id ))
84
+ commit(request, options.merge(:request_type => :recurring))
85
+ end
86
+
87
+ def express
88
+ @express ||= PayflowExpressGateway.new(@options)
89
+ end
90
+
91
+ private
92
+ def build_sale_or_authorization_request(action, money, funding_source, options)
93
+ if funding_source.is_a?(String)
94
+ build_reference_sale_or_authorization_request(action, money, funding_source, options)
95
+ elsif card_brand(funding_source) == 'check'
96
+ build_check_request(action, money, funding_source, options)
97
+ else
98
+ build_credit_card_request(action, money, funding_source, options)
99
+ end
100
+ end
101
+
102
+ def build_reference_sale_or_authorization_request(action, money, reference, options)
103
+ xml = Builder::XmlMarkup.new
104
+ xml.tag! TRANSACTIONS[action] do
105
+ xml.tag! 'PayData' do
106
+ xml.tag! 'Invoice' do
107
+ # Fields accepted by PayFlow and recommended to be provided even for Reference Transaction, per Payflow docs.
108
+ xml.tag! 'CustIP', options[:ip] unless options[:ip].blank?
109
+ xml.tag! 'InvNum', options[:order_id].to_s.gsub(/[^\w.]/, '') unless options[:order_id].blank?
110
+ xml.tag! 'Description', options[:description] unless options[:description].blank?
111
+ xml.tag! 'Comment', options[:comment] unless options[:comment].blank?
112
+ xml.tag!('ExtData', 'Name'=> 'COMMENT2', 'Value'=> options[:comment2]) unless options[:comment2].blank?
113
+ xml.tag! 'TaxAmt', options[:taxamt] unless options[:taxamt].blank?
114
+ xml.tag! 'FreightAmt', options[:freightamt] unless options[:freightamt].blank?
115
+ xml.tag! 'DutyAmt', options[:dutyamt] unless options[:dutyamt].blank?
116
+ xml.tag! 'DiscountAmt', options[:discountamt] unless options[:discountamt].blank?
117
+
118
+ billing_address = options[:billing_address] || options[:address]
119
+ add_address(xml, 'BillTo', billing_address, options) if billing_address
120
+ add_address(xml, 'ShipTo', options[:shipping_address],options) if options[:shipping_address]
121
+
122
+ xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
123
+ end
124
+ xml.tag! 'Tender' do
125
+ xml.tag! 'Card' do
126
+ xml.tag! 'ExtData', 'Name' => 'ORIGID', 'Value' => reference
127
+ end
128
+ end
129
+ end
130
+ end
131
+ xml.target!
132
+ end
133
+
134
+ def build_credit_card_request(action, money, credit_card, options)
135
+ xml = Builder::XmlMarkup.new
136
+ xml.tag! TRANSACTIONS[action] do
137
+ xml.tag! 'PayData' do
138
+ xml.tag! 'Invoice' do
139
+ xml.tag! 'CustIP', options[:ip] unless options[:ip].blank?
140
+ xml.tag! 'InvNum', options[:order_id].to_s.gsub(/[^\w.]/, '') unless options[:order_id].blank?
141
+ xml.tag! 'Description', options[:description] unless options[:description].blank?
142
+ # Comment and Comment2 will show up in manager.paypal.com as Comment1 and Comment2
143
+ xml.tag! 'Comment', options[:comment] unless options[:comment].blank?
144
+ xml.tag!('ExtData', 'Name'=> 'COMMENT2', 'Value'=> options[:comment2]) unless options[:comment2].blank?
145
+ xml.tag! 'TaxAmt', options[:taxamt] unless options[:taxamt].blank?
146
+ xml.tag! 'FreightAmt', options[:freightamt] unless options[:freightamt].blank?
147
+ xml.tag! 'DutyAmt', options[:dutyamt] unless options[:dutyamt].blank?
148
+ xml.tag! 'DiscountAmt', options[:discountamt] unless options[:discountamt].blank?
149
+
150
+ billing_address = options[:billing_address] || options[:address]
151
+ add_address(xml, 'BillTo', billing_address, options) if billing_address
152
+ add_address(xml, 'ShipTo', options[:shipping_address], options) if options[:shipping_address]
153
+
154
+ xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
155
+ end
156
+
157
+ xml.tag! 'Tender' do
158
+ add_credit_card(xml, credit_card)
159
+ end
160
+ end
161
+ end
162
+ xml.target!
163
+ end
164
+
165
+ def build_check_request(action, money, check, options)
166
+ xml = Builder::XmlMarkup.new
167
+ xml.tag! TRANSACTIONS[action] do
168
+ xml.tag! 'PayData' do
169
+ xml.tag! 'Invoice' do
170
+ xml.tag! 'CustIP', options[:ip] unless options[:ip].blank?
171
+ xml.tag! 'InvNum', options[:order_id].to_s.gsub(/[^\w.]/, '') unless options[:order_id].blank?
172
+ xml.tag! 'Description', options[:description] unless options[:description].blank?
173
+ xml.tag! 'BillTo' do
174
+ xml.tag! 'Name', check.name
175
+ end
176
+ xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
177
+ end
178
+ xml.tag! 'Tender' do
179
+ xml.tag! 'ACH' do
180
+ xml.tag! 'AcctType', check.account_type == 'checking' ? 'C' : 'S'
181
+ xml.tag! 'AcctNum', check.account_number
182
+ xml.tag! 'ABA', check.routing_number
183
+ end
184
+ end
185
+ end
186
+ end
187
+ xml.target!
188
+ end
189
+
190
+ def add_credit_card(xml, credit_card)
191
+ xml.tag! 'Card' do
192
+ xml.tag! 'CardType', credit_card_type(credit_card)
193
+ xml.tag! 'CardNum', credit_card.number
194
+ xml.tag! 'ExpDate', expdate(credit_card)
195
+ xml.tag! 'NameOnCard', credit_card.first_name
196
+ xml.tag! 'CVNum', credit_card.verification_value if credit_card.verification_value?
197
+
198
+ if requires_start_date_or_issue_number?(credit_card)
199
+ xml.tag!('ExtData', 'Name' => 'CardStart', 'Value' => startdate(credit_card)) unless credit_card.start_month.blank? || credit_card.start_year.blank?
200
+ xml.tag!('ExtData', 'Name' => 'CardIssue', 'Value' => format(credit_card.issue_number, :two_digits)) unless credit_card.issue_number.blank?
201
+ end
202
+ xml.tag! 'ExtData', 'Name' => 'LASTNAME', 'Value' => credit_card.last_name
203
+ end
204
+ end
205
+
206
+ def credit_card_type(credit_card)
207
+ return '' if card_brand(credit_card).blank?
208
+
209
+ CARD_MAPPING[card_brand(credit_card).to_sym]
210
+ end
211
+
212
+ def expdate(creditcard)
213
+ year = sprintf("%.4i", creditcard.year.to_s.sub(/^0+/, ''))
214
+ month = sprintf("%.2i", creditcard.month.to_s.sub(/^0+/, ''))
215
+
216
+ "#{year}#{month}"
217
+ end
218
+
219
+ def startdate(creditcard)
220
+ year = format(creditcard.start_year, :two_digits)
221
+ month = format(creditcard.start_month, :two_digits)
222
+
223
+ "#{month}#{year}"
224
+ end
225
+
226
+ def build_recurring_request(action, money, options)
227
+ unless RECURRING_ACTIONS.include?(action)
228
+ raise StandardError, "Invalid Recurring Profile Action: #{action}"
229
+ end
230
+
231
+ xml = Builder::XmlMarkup.new
232
+ xml.tag! 'RecurringProfiles' do
233
+ xml.tag! 'RecurringProfile' do
234
+ xml.tag! action.to_s.capitalize do
235
+ unless [:cancel, :inquiry].include?(action)
236
+ xml.tag! 'RPData' do
237
+ xml.tag! 'Name', options[:name] unless options[:name].nil?
238
+ xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
239
+ xml.tag! 'PayPeriod', get_pay_period(options)
240
+ xml.tag! 'Term', options[:payments] unless options[:payments].nil?
241
+ xml.tag! 'Comment', options[:comment] unless options[:comment].nil?
242
+ xml.tag! 'RetryNumDays', options[:retry_num_days] unless options[:retry_num_days].nil?
243
+ xml.tag! 'MaxFailPayments', options[:max_fail_payments] unless options[:max_fail_payments].nil?
244
+
245
+ if initial_tx = options[:initial_transaction]
246
+ requires!(initial_tx, [:type, :authorization, :purchase])
247
+ requires!(initial_tx, :amount) if initial_tx[:type] == :purchase
248
+
249
+ xml.tag! 'OptionalTrans', TRANSACTIONS[initial_tx[:type]]
250
+ xml.tag! 'OptionalTransAmt', amount(initial_tx[:amount]) unless initial_tx[:amount].blank?
251
+ end
252
+
253
+ if action == :add
254
+ xml.tag! 'Start', format_rp_date(options[:starting_at] || Date.today + 1 )
255
+ else
256
+ xml.tag! 'Start', format_rp_date(options[:starting_at]) unless options[:starting_at].nil?
257
+ end
258
+
259
+ xml.tag! 'EMail', options[:email] unless options[:email].nil?
260
+
261
+ billing_address = options[:billing_address] || options[:address]
262
+ add_address(xml, 'BillTo', billing_address, options) if billing_address
263
+ add_address(xml, 'ShipTo', options[:shipping_address], options) if options[:shipping_address]
264
+ end
265
+ xml.tag! 'Tender' do
266
+ yield xml
267
+ end
268
+ end
269
+ if action != :add
270
+ xml.tag! "ProfileID", options[:profile_id]
271
+ end
272
+ if action == :inquiry
273
+ xml.tag! "PaymentHistory", ( options[:history] ? 'Y' : 'N' )
274
+ end
275
+ end
276
+ end
277
+ end
278
+ end
279
+
280
+ def get_pay_period(options)
281
+ requires!(options, [:periodicity, :bimonthly, :monthly, :biweekly, :weekly, :yearly, :daily, :semimonthly, :quadweekly, :quarterly, :semiyearly])
282
+ case options[:periodicity]
283
+ when :weekly then 'Weekly'
284
+ when :biweekly then 'Bi-weekly'
285
+ when :semimonthly then 'Semi-monthly'
286
+ when :quadweekly then 'Every four weeks'
287
+ when :monthly then 'Monthly'
288
+ when :quarterly then 'Quarterly'
289
+ when :semiyearly then 'Semi-yearly'
290
+ when :yearly then 'Yearly'
291
+ end
292
+ end
293
+
294
+ def format_rp_date(time)
295
+ case time
296
+ when Time, Date then time.strftime("%m%d%Y")
297
+ else
298
+ time.to_s
299
+ end
300
+ end
301
+
302
+ def build_response(success, message, response, options = {})
303
+ PayflowResponse.new(success, message, response, options)
304
+ end
305
+ end
306
+ end
307
+ end
308
+
@@ -0,0 +1,220 @@
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(
201
+ success_for(response),
202
+ response[:message], response,
203
+ test: test?,
204
+ authorization: response[:pn_ref] || response[:rp_ref],
205
+ cvv_result: CVV_CODE[response[:cv_result]],
206
+ avs_result: { code: response[:avs_result] },
207
+ fraud_review: under_fraud_review?(response)
208
+ )
209
+ end
210
+
211
+ def success_for(response)
212
+ %w(0 126).include?(response[:result])
213
+ end
214
+
215
+ def under_fraud_review?(response)
216
+ (response[:result] == "126")
217
+ end
218
+ end
219
+ end
220
+ end