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.
- checksums.yaml +7 -0
- data/CHANGELOG +1596 -0
- data/CONTRIBUTORS +511 -0
- data/MIT-LICENSE +20 -0
- data/README.md +18 -0
- data/lib/active_merchant.rb +108 -0
- data/lib/active_merchant/billing.rb +13 -0
- data/lib/active_merchant/billing/apple_pay_payment_token.rb +22 -0
- data/lib/active_merchant/billing/avs_result.rb +98 -0
- data/lib/active_merchant/billing/base.rb +72 -0
- data/lib/active_merchant/billing/check.rb +76 -0
- data/lib/active_merchant/billing/compatibility.rb +120 -0
- data/lib/active_merchant/billing/credit_card.rb +352 -0
- data/lib/active_merchant/billing/credit_card_formatting.rb +24 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +160 -0
- data/lib/active_merchant/billing/cvv_result.rb +38 -0
- data/lib/active_merchant/billing/gateway.rb +268 -0
- data/lib/active_merchant/billing/gateways.rb +17 -0
- data/lib/active_merchant/billing/gateways/adyen.rb +209 -0
- data/lib/active_merchant/billing/gateways/alfabank.rb +117 -0
- data/lib/active_merchant/billing/gateways/app55.rb +176 -0
- data/lib/active_merchant/billing/gateways/authorize_net.rb +419 -0
- data/lib/active_merchant/billing/gateways/authorize_net_arb.rb +417 -0
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +976 -0
- data/lib/active_merchant/billing/gateways/balanced.rb +256 -0
- data/lib/active_merchant/billing/gateways/bank_frick.rb +225 -0
- data/lib/active_merchant/billing/gateways/banwire.rb +105 -0
- data/lib/active_merchant/billing/gateways/barclays_epdq.rb +314 -0
- data/lib/active_merchant/billing/gateways/barclays_epdq_extra_plus.rb +15 -0
- data/lib/active_merchant/billing/gateways/be2bill.rb +131 -0
- data/lib/active_merchant/billing/gateways/beanstream.rb +188 -0
- data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +393 -0
- data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
- data/lib/active_merchant/billing/gateways/blue_pay.rb +506 -0
- data/lib/active_merchant/billing/gateways/bogus.rb +140 -0
- data/lib/active_merchant/billing/gateways/borgun.rb +210 -0
- data/lib/active_merchant/billing/gateways/braintree.rb +19 -0
- data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +515 -0
- data/lib/active_merchant/billing/gateways/braintree_orange.rb +20 -0
- data/lib/active_merchant/billing/gateways/bridge_pay.rb +189 -0
- data/lib/active_merchant/billing/gateways/card_save.rb +23 -0
- data/lib/active_merchant/billing/gateways/card_stream.rb +220 -0
- data/lib/active_merchant/billing/gateways/cashnet.rb +191 -0
- data/lib/active_merchant/billing/gateways/cc5.rb +201 -0
- data/lib/active_merchant/billing/gateways/cecabank.rb +229 -0
- data/lib/active_merchant/billing/gateways/certo_direct.rb +278 -0
- data/lib/active_merchant/billing/gateways/checkout.rb +213 -0
- data/lib/active_merchant/billing/gateways/commercegate.rb +143 -0
- data/lib/active_merchant/billing/gateways/conekta.rb +209 -0
- data/lib/active_merchant/billing/gateways/cyber_source.rb +709 -0
- data/lib/active_merchant/billing/gateways/data_cash.rb +600 -0
- data/lib/active_merchant/billing/gateways/efsnet.rb +219 -0
- data/lib/active_merchant/billing/gateways/elavon.rb +348 -0
- data/lib/active_merchant/billing/gateways/epay.rb +275 -0
- data/lib/active_merchant/billing/gateways/evo_ca.rb +308 -0
- data/lib/active_merchant/billing/gateways/eway.rb +214 -0
- data/lib/active_merchant/billing/gateways/eway_managed.rb +291 -0
- data/lib/active_merchant/billing/gateways/eway_rapid.rb +524 -0
- data/lib/active_merchant/billing/gateways/exact.rb +218 -0
- data/lib/active_merchant/billing/gateways/fat_zebra.rb +173 -0
- data/lib/active_merchant/billing/gateways/federated_canada.rb +160 -0
- data/lib/active_merchant/billing/gateways/finansbank.rb +23 -0
- data/lib/active_merchant/billing/gateways/first_giving.rb +143 -0
- data/lib/active_merchant/billing/gateways/first_pay.rb +160 -0
- data/lib/active_merchant/billing/gateways/firstdata_e4.rb +355 -0
- data/lib/active_merchant/billing/gateways/garanti.rb +257 -0
- data/lib/active_merchant/billing/gateways/global_transport.rb +183 -0
- data/lib/active_merchant/billing/gateways/hdfc.rb +207 -0
- data/lib/active_merchant/billing/gateways/hps.rb +288 -0
- data/lib/active_merchant/billing/gateways/iats_payments.rb +251 -0
- data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +246 -0
- data/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem +13 -0
- data/lib/active_merchant/billing/gateways/ideal/ideal_response.rb +29 -0
- data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +66 -0
- data/lib/active_merchant/billing/gateways/inspire.rb +213 -0
- data/lib/active_merchant/billing/gateways/instapay.rb +163 -0
- data/lib/active_merchant/billing/gateways/iridium.rb +457 -0
- data/lib/active_merchant/billing/gateways/itransact.rb +448 -0
- data/lib/active_merchant/billing/gateways/jetpay.rb +275 -0
- data/lib/active_merchant/billing/gateways/linkpoint.rb +438 -0
- data/lib/active_merchant/billing/gateways/litle.rb +346 -0
- data/lib/active_merchant/billing/gateways/maxipago.rb +197 -0
- data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +170 -0
- data/lib/active_merchant/billing/gateways/merchant_one.rb +114 -0
- data/lib/active_merchant/billing/gateways/merchant_ware.rb +319 -0
- data/lib/active_merchant/billing/gateways/merchant_ware_version_four.rb +268 -0
- data/lib/active_merchant/billing/gateways/merchant_warrior.rb +195 -0
- data/lib/active_merchant/billing/gateways/mercury.rb +333 -0
- data/lib/active_merchant/billing/gateways/metrics_global.rb +303 -0
- data/lib/active_merchant/billing/gateways/migs.rb +265 -0
- data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +100 -0
- data/lib/active_merchant/billing/gateways/modern_payments.rb +37 -0
- data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +219 -0
- data/lib/active_merchant/billing/gateways/moneris.rb +309 -0
- data/lib/active_merchant/billing/gateways/moneris_us.rb +291 -0
- data/lib/active_merchant/billing/gateways/money_movers.rb +152 -0
- data/lib/active_merchant/billing/gateways/nab_transact.rb +280 -0
- data/lib/active_merchant/billing/gateways/net_registry.rb +198 -0
- data/lib/active_merchant/billing/gateways/netaxept.rb +181 -0
- data/lib/active_merchant/billing/gateways/netbilling.rb +190 -0
- data/lib/active_merchant/billing/gateways/netpay.rb +223 -0
- data/lib/active_merchant/billing/gateways/network_merchants.rb +242 -0
- data/lib/active_merchant/billing/gateways/nmi.rb +256 -0
- data/lib/active_merchant/billing/gateways/ogone.rb +435 -0
- data/lib/active_merchant/billing/gateways/openpay.rb +194 -0
- data/lib/active_merchant/billing/gateways/optimal_payment.rb +313 -0
- data/lib/active_merchant/billing/gateways/orbital.rb +803 -0
- data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +47 -0
- data/lib/active_merchant/billing/gateways/pac_net_raven.rb +207 -0
- data/lib/active_merchant/billing/gateways/pago_facil.rb +122 -0
- data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +261 -0
- data/lib/active_merchant/billing/gateways/pay_junction.rb +390 -0
- data/lib/active_merchant/billing/gateways/pay_secure.rb +112 -0
- data/lib/active_merchant/billing/gateways/pay_u_latam.rb +462 -0
- data/lib/active_merchant/billing/gateways/paybox_direct.rb +188 -0
- data/lib/active_merchant/billing/gateways/payex.rb +412 -0
- data/lib/active_merchant/billing/gateways/payflow.rb +304 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +209 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
- data/lib/active_merchant/billing/gateways/payflow_express.rb +224 -0
- data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
- data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
- data/lib/active_merchant/billing/gateways/payment_express.rb +353 -0
- data/lib/active_merchant/billing/gateways/paymill.rb +281 -0
- data/lib/active_merchant/billing/gateways/paypal.rb +117 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +670 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +65 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +262 -0
- data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
- data/lib/active_merchant/billing/gateways/paypal_digital_goods.rb +44 -0
- data/lib/active_merchant/billing/gateways/paypal_express.rb +264 -0
- data/lib/active_merchant/billing/gateways/paypal_express_common.rb +30 -0
- data/lib/active_merchant/billing/gateways/payscout.rb +162 -0
- data/lib/active_merchant/billing/gateways/paystation.rb +199 -0
- data/lib/active_merchant/billing/gateways/payway.rb +207 -0
- data/lib/active_merchant/billing/gateways/pin.rb +197 -0
- data/lib/active_merchant/billing/gateways/plugnpay.rb +283 -0
- data/lib/active_merchant/billing/gateways/psigate.rb +216 -0
- data/lib/active_merchant/billing/gateways/psl_card.rb +303 -0
- data/lib/active_merchant/billing/gateways/qbms.rb +292 -0
- data/lib/active_merchant/billing/gateways/quantum.rb +276 -0
- data/lib/active_merchant/billing/gateways/quickpay.rb +367 -0
- data/lib/active_merchant/billing/gateways/realex.rb +298 -0
- data/lib/active_merchant/billing/gateways/redsys.rb +391 -0
- data/lib/active_merchant/billing/gateways/sage.rb +175 -0
- data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +87 -0
- data/lib/active_merchant/billing/gateways/sage/sage_core.rb +114 -0
- data/lib/active_merchant/billing/gateways/sage/sage_vault.rb +149 -0
- data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +102 -0
- data/lib/active_merchant/billing/gateways/sage_pay.rb +398 -0
- data/lib/active_merchant/billing/gateways/sallie_mae.rb +143 -0
- data/lib/active_merchant/billing/gateways/secure_net.rb +252 -0
- data/lib/active_merchant/billing/gateways/secure_pay.rb +201 -0
- data/lib/active_merchant/billing/gateways/secure_pay_au.rb +281 -0
- data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +105 -0
- data/lib/active_merchant/billing/gateways/skip_jack.rb +452 -0
- data/lib/active_merchant/billing/gateways/smart_ps.rb +283 -0
- data/lib/active_merchant/billing/gateways/so_easy_pay.rb +194 -0
- data/lib/active_merchant/billing/gateways/spreedly_core.rb +247 -0
- data/lib/active_merchant/billing/gateways/stripe.rb +411 -0
- data/lib/active_merchant/billing/gateways/swipe_checkout.rb +157 -0
- data/lib/active_merchant/billing/gateways/tns.rb +227 -0
- data/lib/active_merchant/billing/gateways/trans_first.rb +126 -0
- data/lib/active_merchant/billing/gateways/transax.rb +23 -0
- data/lib/active_merchant/billing/gateways/transnational.rb +10 -0
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +416 -0
- data/lib/active_merchant/billing/gateways/usa_epay.rb +25 -0
- data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +1516 -0
- data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +254 -0
- data/lib/active_merchant/billing/gateways/verifi.rb +225 -0
- data/lib/active_merchant/billing/gateways/viaklix.rb +183 -0
- data/lib/active_merchant/billing/gateways/vindicia.rb +385 -0
- data/lib/active_merchant/billing/gateways/webpay.rb +97 -0
- data/lib/active_merchant/billing/gateways/wepay.rb +189 -0
- data/lib/active_merchant/billing/gateways/wirecard.rb +421 -0
- data/lib/active_merchant/billing/gateways/worldpay.rb +331 -0
- data/lib/active_merchant/billing/gateways/worldpay_us.rb +181 -0
- data/lib/active_merchant/billing/model.rb +30 -0
- data/lib/active_merchant/billing/payment_token.rb +21 -0
- data/lib/active_merchant/billing/rails.rb +3 -0
- data/lib/active_merchant/billing/response.rb +91 -0
- data/lib/active_merchant/country.rb +332 -0
- data/lib/active_merchant/empty.rb +20 -0
- data/lib/active_merchant/errors.rb +29 -0
- data/lib/active_merchant/offsite_payments_shim.rb +19 -0
- data/lib/active_merchant/version.rb +3 -0
- data/lib/activemerchant.rb +1 -0
- data/lib/support/gateway_support.rb +71 -0
- data/lib/support/outbound_hosts.rb +25 -0
- data/lib/support/ssl_verify.rb +93 -0
- 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
|