johnreitano-activemerchant 1.5.2
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.
- data/CHANGELOG +508 -0
- data/CONTRIBUTORS +134 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +136 -0
- data/gem-public_cert.pem +20 -0
- data/lib/active_merchant/billing/avs_result.rb +98 -0
- data/lib/active_merchant/billing/base.rb +57 -0
- data/lib/active_merchant/billing/check.rb +68 -0
- data/lib/active_merchant/billing/credit_card.rb +159 -0
- data/lib/active_merchant/billing/credit_card_formatting.rb +21 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +125 -0
- data/lib/active_merchant/billing/cvv_result.rb +38 -0
- data/lib/active_merchant/billing/expiry_date.rb +34 -0
- data/lib/active_merchant/billing/gateway.rb +163 -0
- data/lib/active_merchant/billing/gateways/authorize_net.rb +654 -0
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +736 -0
- data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +244 -0
- data/lib/active_merchant/billing/gateways/beanstream.rb +102 -0
- data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
- data/lib/active_merchant/billing/gateways/bogus.rb +98 -0
- data/lib/active_merchant/billing/gateways/braintree.rb +17 -0
- data/lib/active_merchant/billing/gateways/card_stream.rb +230 -0
- data/lib/active_merchant/billing/gateways/cyber_source.rb +594 -0
- data/lib/active_merchant/billing/gateways/data_cash.rb +593 -0
- data/lib/active_merchant/billing/gateways/efsnet.rb +229 -0
- data/lib/active_merchant/billing/gateways/elavon.rb +134 -0
- data/lib/active_merchant/billing/gateways/eway.rb +277 -0
- data/lib/active_merchant/billing/gateways/exact.rb +222 -0
- data/lib/active_merchant/billing/gateways/first_pay.rb +172 -0
- data/lib/active_merchant/billing/gateways/instapay.rb +164 -0
- data/lib/active_merchant/billing/gateways/jetpay.rb +270 -0
- data/lib/active_merchant/billing/gateways/linkpoint.rb +449 -0
- data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +154 -0
- data/lib/active_merchant/billing/gateways/merchant_ware.rb +283 -0
- data/lib/active_merchant/billing/gateways/modern_payments.rb +36 -0
- data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +220 -0
- data/lib/active_merchant/billing/gateways/moneris.rb +205 -0
- data/lib/active_merchant/billing/gateways/net_registry.rb +189 -0
- data/lib/active_merchant/billing/gateways/netbilling.rb +168 -0
- data/lib/active_merchant/billing/gateways/ogone.rb +279 -0
- data/lib/active_merchant/billing/gateways/pay_junction.rb +392 -0
- data/lib/active_merchant/billing/gateways/pay_secure.rb +120 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +207 -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.rb +236 -0
- data/lib/active_merchant/billing/gateways/payflow_express.rb +138 -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 +230 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +326 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +38 -0
- data/lib/active_merchant/billing/gateways/paypal.rb +121 -0
- data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
- data/lib/active_merchant/billing/gateways/paypal_express.rb +130 -0
- data/lib/active_merchant/billing/gateways/paypal_express_common.rb +20 -0
- data/lib/active_merchant/billing/gateways/plugnpay.rb +292 -0
- data/lib/active_merchant/billing/gateways/psigate.rb +214 -0
- data/lib/active_merchant/billing/gateways/psl_card.rb +304 -0
- data/lib/active_merchant/billing/gateways/quickpay.rb +213 -0
- data/lib/active_merchant/billing/gateways/realex.rb +200 -0
- data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +88 -0
- data/lib/active_merchant/billing/gateways/sage/sage_core.rb +116 -0
- data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
- data/lib/active_merchant/billing/gateways/sage.rb +146 -0
- data/lib/active_merchant/billing/gateways/sage_pay.rb +309 -0
- data/lib/active_merchant/billing/gateways/sallie_mae.rb +144 -0
- data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
- data/lib/active_merchant/billing/gateways/secure_pay_au.rb +157 -0
- data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +113 -0
- data/lib/active_merchant/billing/gateways/skip_jack.rb +453 -0
- data/lib/active_merchant/billing/gateways/smart_ps.rb +265 -0
- data/lib/active_merchant/billing/gateways/trans_first.rb +127 -0
- data/lib/active_merchant/billing/gateways/transax.rb +25 -0
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +418 -0
- data/lib/active_merchant/billing/gateways/usa_epay.rb +194 -0
- data/lib/active_merchant/billing/gateways/verifi.rb +228 -0
- data/lib/active_merchant/billing/gateways/viaklix.rb +189 -0
- data/lib/active_merchant/billing/gateways/wirecard.rb +318 -0
- data/lib/active_merchant/billing/gateways.rb +18 -0
- data/lib/active_merchant/billing/integrations/action_view_helper.rb +79 -0
- data/lib/active_merchant/billing/integrations/bogus/helper.rb +17 -0
- data/lib/active_merchant/billing/integrations/bogus/notification.rb +11 -0
- data/lib/active_merchant/billing/integrations/bogus/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/bogus.rb +23 -0
- data/lib/active_merchant/billing/integrations/chronopay/helper.rb +120 -0
- data/lib/active_merchant/billing/integrations/chronopay/notification.rb +158 -0
- data/lib/active_merchant/billing/integrations/chronopay/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/chronopay.rb +23 -0
- data/lib/active_merchant/billing/integrations/gestpay/common.rb +42 -0
- data/lib/active_merchant/billing/integrations/gestpay/helper.rb +70 -0
- data/lib/active_merchant/billing/integrations/gestpay/notification.rb +85 -0
- data/lib/active_merchant/billing/integrations/gestpay/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/gestpay.rb +25 -0
- data/lib/active_merchant/billing/integrations/helper.rb +93 -0
- data/lib/active_merchant/billing/integrations/hi_trust/helper.rb +58 -0
- data/lib/active_merchant/billing/integrations/hi_trust/notification.rb +59 -0
- data/lib/active_merchant/billing/integrations/hi_trust/return.rb +67 -0
- data/lib/active_merchant/billing/integrations/hi_trust.rb +27 -0
- data/lib/active_merchant/billing/integrations/nochex/helper.rb +68 -0
- data/lib/active_merchant/billing/integrations/nochex/notification.rb +94 -0
- data/lib/active_merchant/billing/integrations/nochex/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/nochex.rb +88 -0
- data/lib/active_merchant/billing/integrations/notification.rb +62 -0
- data/lib/active_merchant/billing/integrations/paypal/helper.rb +119 -0
- data/lib/active_merchant/billing/integrations/paypal/notification.rb +154 -0
- data/lib/active_merchant/billing/integrations/paypal/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/paypal.rb +39 -0
- data/lib/active_merchant/billing/integrations/quickpay/helper.rb +72 -0
- data/lib/active_merchant/billing/integrations/quickpay/notification.rb +74 -0
- data/lib/active_merchant/billing/integrations/quickpay.rb +17 -0
- data/lib/active_merchant/billing/integrations/return.rb +35 -0
- data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +59 -0
- data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +114 -0
- data/lib/active_merchant/billing/integrations/two_checkout/return.rb +17 -0
- data/lib/active_merchant/billing/integrations/two_checkout.rb +23 -0
- data/lib/active_merchant/billing/integrations.rb +29 -0
- data/lib/active_merchant/billing/response.rb +32 -0
- data/lib/active_merchant/billing.rb +9 -0
- data/lib/active_merchant/lib/connection.rb +170 -0
- data/lib/active_merchant/lib/country.rb +319 -0
- data/lib/active_merchant/lib/error.rb +4 -0
- data/lib/active_merchant/lib/post_data.rb +22 -0
- data/lib/active_merchant/lib/posts_data.rb +47 -0
- data/lib/active_merchant/lib/requires_parameters.rb +16 -0
- data/lib/active_merchant/lib/utils.rb +18 -0
- data/lib/active_merchant/lib/validateable.rb +76 -0
- data/lib/active_merchant.rb +46 -0
- data/lib/certs/cacert.pem +7815 -0
- data/lib/support/gateway_support.rb +58 -0
- metadata +218 -0
@@ -0,0 +1,326 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
# This module is included in both PaypalGateway and PaypalExpressGateway
|
4
|
+
module PaypalCommonAPI
|
5
|
+
def self.included(base)
|
6
|
+
base.default_currency = 'USD'
|
7
|
+
base.cattr_accessor :pem_file
|
8
|
+
base.cattr_accessor :signature
|
9
|
+
end
|
10
|
+
|
11
|
+
API_VERSION = '59.0'
|
12
|
+
|
13
|
+
URLS = {
|
14
|
+
:test => { :certificate => 'https://api.sandbox.paypal.com/2.0/',
|
15
|
+
:signature => 'https://api-3t.sandbox.paypal.com/2.0/' },
|
16
|
+
:live => { :certificate => 'https://api-aa.paypal.com/2.0/',
|
17
|
+
:signature => 'https://api-3t.paypal.com/2.0/' }
|
18
|
+
}
|
19
|
+
|
20
|
+
PAYPAL_NAMESPACE = 'urn:ebay:api:PayPalAPI'
|
21
|
+
EBAY_NAMESPACE = 'urn:ebay:apis:eBLBaseComponents'
|
22
|
+
|
23
|
+
ENVELOPE_NAMESPACES = { 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
|
24
|
+
'xmlns:env' => 'http://schemas.xmlsoap.org/soap/envelope/',
|
25
|
+
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
|
26
|
+
}
|
27
|
+
CREDENTIALS_NAMESPACES = { 'xmlns' => PAYPAL_NAMESPACE,
|
28
|
+
'xmlns:n1' => EBAY_NAMESPACE,
|
29
|
+
'env:mustUnderstand' => '0'
|
30
|
+
}
|
31
|
+
|
32
|
+
AUSTRALIAN_STATES = {
|
33
|
+
'ACT' => 'Australian Capital Territory',
|
34
|
+
'NSW' => 'New South Wales',
|
35
|
+
'NT' => 'Northern Territory',
|
36
|
+
'QLD' => 'Queensland',
|
37
|
+
'SA' => 'South Australia',
|
38
|
+
'TAS' => 'Tasmania',
|
39
|
+
'VIC' => 'Victoria',
|
40
|
+
'WA' => 'Western Australia'
|
41
|
+
}
|
42
|
+
|
43
|
+
SUCCESS_CODES = [ 'Success', 'SuccessWithWarning' ]
|
44
|
+
|
45
|
+
FRAUD_REVIEW_CODE = "11610"
|
46
|
+
|
47
|
+
# The gateway must be configured with either your PayPal PEM file
|
48
|
+
# or your PayPal API Signature. Only one is required.
|
49
|
+
#
|
50
|
+
# <tt>:pem</tt> The text of your PayPal PEM file. Note
|
51
|
+
# this is not the path to file, but its
|
52
|
+
# contents. If you are only using one PEM
|
53
|
+
# file on your site you can declare it
|
54
|
+
# globally and then you won't need to
|
55
|
+
# include this option
|
56
|
+
#
|
57
|
+
# <tt>:signature</tt> The text of your PayPal signature.
|
58
|
+
# If you are only using one API Signature
|
59
|
+
# on your site you can declare it
|
60
|
+
# globally and then you won't need to
|
61
|
+
# include this option
|
62
|
+
|
63
|
+
def initialize(options = {})
|
64
|
+
requires!(options, :login, :password)
|
65
|
+
|
66
|
+
@options = {
|
67
|
+
:pem => pem_file,
|
68
|
+
:signature => signature
|
69
|
+
}.update(options)
|
70
|
+
|
71
|
+
if @options[:pem].blank? && @options[:signature].blank?
|
72
|
+
raise ArgumentError, "An API Certificate or API Signature is required to make requests to PayPal"
|
73
|
+
end
|
74
|
+
|
75
|
+
super
|
76
|
+
end
|
77
|
+
|
78
|
+
def test?
|
79
|
+
@options[:test] || Base.gateway_mode == :test
|
80
|
+
end
|
81
|
+
|
82
|
+
def reauthorize(money, authorization, options = {})
|
83
|
+
commit 'DoReauthorization', build_reauthorize_request(money, authorization, options)
|
84
|
+
end
|
85
|
+
|
86
|
+
def capture(money, authorization, options = {})
|
87
|
+
commit 'DoCapture', build_capture_request(money, authorization, options)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Transfer money to one or more recipients.
|
91
|
+
#
|
92
|
+
# gateway.transfer 1000, 'bob@example.com',
|
93
|
+
# :subject => "The money I owe you", :note => "Sorry it's so late"
|
94
|
+
#
|
95
|
+
# gateway.transfer [1000, 'fred@example.com'],
|
96
|
+
# [2450, 'wilma@example.com', :note => 'You will receive another payment on 3/24'],
|
97
|
+
# [2000, 'barney@example.com'],
|
98
|
+
# :subject => "Your Earnings", :note => "Thanks for your business."
|
99
|
+
#
|
100
|
+
def transfer(*args)
|
101
|
+
commit 'MassPay', build_mass_pay_request(*args)
|
102
|
+
end
|
103
|
+
|
104
|
+
def void(authorization, options = {})
|
105
|
+
commit 'DoVoid', build_void_request(authorization, options)
|
106
|
+
end
|
107
|
+
|
108
|
+
def credit(money, identification, options = {})
|
109
|
+
commit 'RefundTransaction', build_credit_request(money, identification, options)
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
def build_reauthorize_request(money, authorization, options)
|
114
|
+
xml = Builder::XmlMarkup.new
|
115
|
+
|
116
|
+
xml.tag! 'DoReauthorizationReq', 'xmlns' => PAYPAL_NAMESPACE do
|
117
|
+
xml.tag! 'DoReauthorizationRequest', 'xmlns:n2' => EBAY_NAMESPACE do
|
118
|
+
xml.tag! 'n2:Version', API_VERSION
|
119
|
+
xml.tag! 'AuthorizationID', authorization
|
120
|
+
xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
xml.target!
|
125
|
+
end
|
126
|
+
|
127
|
+
def build_capture_request(money, authorization, options)
|
128
|
+
xml = Builder::XmlMarkup.new
|
129
|
+
|
130
|
+
xml.tag! 'DoCaptureReq', 'xmlns' => PAYPAL_NAMESPACE do
|
131
|
+
xml.tag! 'DoCaptureRequest', 'xmlns:n2' => EBAY_NAMESPACE do
|
132
|
+
xml.tag! 'n2:Version', API_VERSION
|
133
|
+
xml.tag! 'AuthorizationID', authorization
|
134
|
+
xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
|
135
|
+
xml.tag! 'CompleteType', 'Complete'
|
136
|
+
xml.tag! 'InvoiceID', options[:order_id] unless options[:order_id].blank?
|
137
|
+
xml.tag! 'Note', options[:description]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
xml.target!
|
142
|
+
end
|
143
|
+
|
144
|
+
def build_credit_request(money, identification, options)
|
145
|
+
xml = Builder::XmlMarkup.new
|
146
|
+
|
147
|
+
xml.tag! 'RefundTransactionReq', 'xmlns' => PAYPAL_NAMESPACE do
|
148
|
+
xml.tag! 'RefundTransactionRequest', 'xmlns:n2' => EBAY_NAMESPACE do
|
149
|
+
xml.tag! 'n2:Version', API_VERSION
|
150
|
+
xml.tag! 'TransactionID', identification
|
151
|
+
xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
|
152
|
+
xml.tag! 'RefundType', 'Partial'
|
153
|
+
xml.tag! 'Memo', options[:note] unless options[:note].blank?
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
xml.target!
|
158
|
+
end
|
159
|
+
|
160
|
+
def build_void_request(authorization, options)
|
161
|
+
xml = Builder::XmlMarkup.new
|
162
|
+
|
163
|
+
xml.tag! 'DoVoidReq', 'xmlns' => PAYPAL_NAMESPACE do
|
164
|
+
xml.tag! 'DoVoidRequest', 'xmlns:n2' => EBAY_NAMESPACE do
|
165
|
+
xml.tag! 'n2:Version', API_VERSION
|
166
|
+
xml.tag! 'AuthorizationID', authorization
|
167
|
+
xml.tag! 'Note', options[:description]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
xml.target!
|
172
|
+
end
|
173
|
+
|
174
|
+
def build_mass_pay_request(*args)
|
175
|
+
default_options = args.last.is_a?(Hash) ? args.pop : {}
|
176
|
+
recipients = args.first.is_a?(Array) ? args : [args]
|
177
|
+
|
178
|
+
xml = Builder::XmlMarkup.new
|
179
|
+
|
180
|
+
xml.tag! 'MassPayReq', 'xmlns' => PAYPAL_NAMESPACE do
|
181
|
+
xml.tag! 'MassPayRequest', 'xmlns:n2' => EBAY_NAMESPACE do
|
182
|
+
xml.tag! 'n2:Version', API_VERSION
|
183
|
+
xml.tag! 'EmailSubject', default_options[:subject] if default_options[:subject]
|
184
|
+
recipients.each do |money, recipient, options|
|
185
|
+
options ||= default_options
|
186
|
+
xml.tag! 'MassPayItem' do
|
187
|
+
xml.tag! 'ReceiverEmail', recipient
|
188
|
+
xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
|
189
|
+
xml.tag! 'Note', options[:note] if options[:note]
|
190
|
+
xml.tag! 'UniqueId', options[:unique_id] if options[:unique_id]
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
xml.target!
|
197
|
+
end
|
198
|
+
|
199
|
+
def parse(action, xml)
|
200
|
+
response = {}
|
201
|
+
|
202
|
+
error_messages = []
|
203
|
+
error_codes = []
|
204
|
+
|
205
|
+
xml = REXML::Document.new(xml)
|
206
|
+
if root = REXML::XPath.first(xml, "//#{action}Response")
|
207
|
+
root.elements.each do |node|
|
208
|
+
case node.name
|
209
|
+
when 'Errors'
|
210
|
+
short_message = nil
|
211
|
+
long_message = nil
|
212
|
+
|
213
|
+
node.elements.each do |child|
|
214
|
+
case child.name
|
215
|
+
when "LongMessage"
|
216
|
+
long_message = child.text unless child.text.blank?
|
217
|
+
when "ShortMessage"
|
218
|
+
short_message = child.text unless child.text.blank?
|
219
|
+
when "ErrorCode"
|
220
|
+
error_codes << child.text unless child.text.blank?
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
if message = long_message || short_message
|
225
|
+
error_messages << message
|
226
|
+
end
|
227
|
+
else
|
228
|
+
parse_element(response, node)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
response[:message] = error_messages.uniq.join(". ") unless error_messages.empty?
|
232
|
+
response[:error_codes] = error_codes.uniq.join(",") unless error_codes.empty?
|
233
|
+
elsif root = REXML::XPath.first(xml, "//SOAP-ENV:Fault")
|
234
|
+
parse_element(response, root)
|
235
|
+
response[:message] = "#{response[:faultcode]}: #{response[:faultstring]} - #{response[:detail]}"
|
236
|
+
end
|
237
|
+
|
238
|
+
response
|
239
|
+
end
|
240
|
+
|
241
|
+
def parse_element(response, node)
|
242
|
+
if node.has_elements?
|
243
|
+
node.elements.each{|e| parse_element(response, e) }
|
244
|
+
else
|
245
|
+
response[node.name.underscore.to_sym] = node.text
|
246
|
+
node.attributes.each do |k, v|
|
247
|
+
response["#{node.name.underscore}_#{k.underscore}".to_sym] = v if k == 'currencyID'
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def build_request(body)
|
253
|
+
xml = Builder::XmlMarkup.new
|
254
|
+
|
255
|
+
xml.instruct!
|
256
|
+
xml.tag! 'env:Envelope', ENVELOPE_NAMESPACES do
|
257
|
+
xml.tag! 'env:Header' do
|
258
|
+
add_credentials(xml)
|
259
|
+
end
|
260
|
+
|
261
|
+
xml.tag! 'env:Body' do
|
262
|
+
xml << body
|
263
|
+
end
|
264
|
+
end
|
265
|
+
xml.target!
|
266
|
+
end
|
267
|
+
|
268
|
+
def add_credentials(xml)
|
269
|
+
xml.tag! 'RequesterCredentials', CREDENTIALS_NAMESPACES do
|
270
|
+
xml.tag! 'n1:Credentials' do
|
271
|
+
xml.tag! 'Username', @options[:login]
|
272
|
+
xml.tag! 'Password', @options[:password]
|
273
|
+
xml.tag! 'Subject', @options[:subject]
|
274
|
+
xml.tag! 'Signature', @options[:signature] unless @options[:signature].blank?
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def add_address(xml, element, address)
|
280
|
+
return if address.nil?
|
281
|
+
xml.tag! element do
|
282
|
+
xml.tag! 'n2:Name', address[:name]
|
283
|
+
xml.tag! 'n2:Street1', address[:address1]
|
284
|
+
xml.tag! 'n2:Street2', address[:address2]
|
285
|
+
xml.tag! 'n2:CityName', address[:city]
|
286
|
+
xml.tag! 'n2:StateOrProvince', address[:state].blank? ? 'N/A' : address[:state]
|
287
|
+
xml.tag! 'n2:Country', address[:country]
|
288
|
+
xml.tag! 'n2:PostalCode', address[:zip]
|
289
|
+
xml.tag! 'n2:Phone', address[:phone]
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def endpoint_url
|
294
|
+
URLS[test? ? :test : :live][@options[:signature].blank? ? :certificate : :signature]
|
295
|
+
end
|
296
|
+
|
297
|
+
def commit(action, request)
|
298
|
+
response = parse(action, ssl_post(endpoint_url, build_request(request)))
|
299
|
+
|
300
|
+
build_response(successful?(response), message_from(response), response,
|
301
|
+
:test => test?,
|
302
|
+
:authorization => authorization_from(response),
|
303
|
+
:fraud_review => fraud_review?(response),
|
304
|
+
:avs_result => { :code => response[:avs_code] },
|
305
|
+
:cvv_result => response[:cvv2_code]
|
306
|
+
)
|
307
|
+
end
|
308
|
+
|
309
|
+
def fraud_review?(response)
|
310
|
+
response[:error_codes] == FRAUD_REVIEW_CODE
|
311
|
+
end
|
312
|
+
|
313
|
+
def authorization_from(response)
|
314
|
+
response[:transaction_id] || response[:authorization_id] || response[:refund_transaction_id] # middle one is from reauthorization
|
315
|
+
end
|
316
|
+
|
317
|
+
def successful?(response)
|
318
|
+
SUCCESS_CODES.include?(response[:ack])
|
319
|
+
end
|
320
|
+
|
321
|
+
def message_from(response)
|
322
|
+
response[:message] || response[:ack]
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
class PaypalExpressResponse < Response
|
4
|
+
def email
|
5
|
+
@params['payer']
|
6
|
+
end
|
7
|
+
|
8
|
+
def name
|
9
|
+
[@params['first_name'], @params['middle_name'], @params['last_name']].compact.join(' ')
|
10
|
+
end
|
11
|
+
|
12
|
+
def token
|
13
|
+
@params['token']
|
14
|
+
end
|
15
|
+
|
16
|
+
def payer_id
|
17
|
+
@params['payer_id']
|
18
|
+
end
|
19
|
+
|
20
|
+
def payer_country
|
21
|
+
@params['payer_country']
|
22
|
+
end
|
23
|
+
|
24
|
+
def address
|
25
|
+
{ 'name' => @params['name'],
|
26
|
+
'company' => @params['payer_business'],
|
27
|
+
'address1' => @params['street1'],
|
28
|
+
'address2' => @params['street2'],
|
29
|
+
'city' => @params['city_name'],
|
30
|
+
'state' => @params['state_or_province'],
|
31
|
+
'country' => @params['country'],
|
32
|
+
'zip' => @params['postal_code'],
|
33
|
+
'phone' => nil
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/paypal/paypal_common_api'
|
2
|
+
require File.dirname(__FILE__) + '/paypal_express'
|
3
|
+
|
4
|
+
module ActiveMerchant #:nodoc:
|
5
|
+
module Billing #:nodoc:
|
6
|
+
class PaypalGateway < Gateway
|
7
|
+
include PaypalCommonAPI
|
8
|
+
|
9
|
+
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
10
|
+
self.supported_countries = ['US']
|
11
|
+
self.homepage_url = 'https://www.paypal.com/cgi-bin/webscr?cmd=_wp-pro-overview-outside'
|
12
|
+
self.display_name = 'PayPal Website Payments Pro (US)'
|
13
|
+
|
14
|
+
def authorize(money, credit_card_or_referenced_id, options = {})
|
15
|
+
requires!(options, :ip)
|
16
|
+
commit define_transaction_type(credit_card_or_referenced_id), build_sale_or_authorization_request('Authorization', money, credit_card_or_referenced_id, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def purchase(money, credit_card_or_referenced_id, options = {})
|
20
|
+
requires!(options, :ip)
|
21
|
+
commit define_transaction_type(credit_card_or_referenced_id), build_sale_or_authorization_request('Sale', money, credit_card_or_referenced_id, options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def express
|
25
|
+
@express ||= PaypalExpressGateway.new(@options)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def define_transaction_type(transaction_arg)
|
31
|
+
if transaction_arg.is_a?(String)
|
32
|
+
return 'DoReferenceTransaction'
|
33
|
+
else
|
34
|
+
return 'DoDirectPayment'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_sale_or_authorization_request(action, money, credit_card_or_referenced_id, options)
|
39
|
+
transaction_type = define_transaction_type(credit_card_or_referenced_id)
|
40
|
+
reference_id = credit_card_or_referenced_id if transaction_type == "DoReferenceTransaction"
|
41
|
+
|
42
|
+
billing_address = options[:billing_address] || options[:address]
|
43
|
+
currency_code = options[:currency] || currency(money)
|
44
|
+
|
45
|
+
xml = Builder::XmlMarkup.new :indent => 2
|
46
|
+
xml.tag! transaction_type + 'Req', 'xmlns' => PAYPAL_NAMESPACE do
|
47
|
+
xml.tag! transaction_type + 'Request', 'xmlns:n2' => EBAY_NAMESPACE do
|
48
|
+
xml.tag! 'n2:Version', API_VERSION
|
49
|
+
xml.tag! 'n2:' + transaction_type + 'RequestDetails' do
|
50
|
+
xml.tag! 'n2:ReferenceID', reference_id if transaction_type == 'DoReferenceTransaction'
|
51
|
+
xml.tag! 'n2:PaymentAction', action
|
52
|
+
xml.tag! 'n2:PaymentDetails' do
|
53
|
+
xml.tag! 'n2:OrderTotal', amount(money), 'currencyID' => currency_code
|
54
|
+
|
55
|
+
# All of the values must be included together and add up to the order total
|
56
|
+
if [:subtotal, :shipping, :handling, :tax].all?{ |o| options.has_key?(o) }
|
57
|
+
xml.tag! 'n2:ItemTotal', amount(options[:subtotal]), 'currencyID' => currency_code
|
58
|
+
xml.tag! 'n2:ShippingTotal', amount(options[:shipping]),'currencyID' => currency_code
|
59
|
+
xml.tag! 'n2:HandlingTotal', amount(options[:handling]),'currencyID' => currency_code
|
60
|
+
xml.tag! 'n2:TaxTotal', amount(options[:tax]), 'currencyID' => currency_code
|
61
|
+
end
|
62
|
+
|
63
|
+
xml.tag! 'n2:NotifyURL', options[:notify_url]
|
64
|
+
xml.tag! 'n2:OrderDescription', options[:description]
|
65
|
+
xml.tag! 'n2:InvoiceID', options[:order_id]
|
66
|
+
xml.tag! 'n2:ButtonSource', application_id.to_s.slice(0,32) unless application_id.blank?
|
67
|
+
|
68
|
+
add_address(xml, 'n2:ShipToAddress', options[:shipping_address]) if options[:shipping_address]
|
69
|
+
end
|
70
|
+
add_credit_card(xml, credit_card_or_referenced_id, billing_address, options) unless transaction_type == 'DoReferenceTransaction'
|
71
|
+
xml.tag! 'n2:IPAddress', options[:ip]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
xml.target!
|
77
|
+
end
|
78
|
+
|
79
|
+
def add_credit_card(xml, credit_card, address, options)
|
80
|
+
xml.tag! 'n2:CreditCard' do
|
81
|
+
xml.tag! 'n2:CreditCardType', credit_card_type(card_brand(credit_card))
|
82
|
+
xml.tag! 'n2:CreditCardNumber', credit_card.number
|
83
|
+
xml.tag! 'n2:ExpMonth', format(credit_card.month, :two_digits)
|
84
|
+
xml.tag! 'n2:ExpYear', format(credit_card.year, :four_digits)
|
85
|
+
xml.tag! 'n2:CVV2', credit_card.verification_value
|
86
|
+
|
87
|
+
if [ 'switch', 'solo' ].include?(card_brand(credit_card).to_s)
|
88
|
+
xml.tag! 'n2:StartMonth', format(credit_card.start_month, :two_digits) unless credit_card.start_month.blank?
|
89
|
+
xml.tag! 'n2:StartYear', format(credit_card.start_year, :four_digits) unless credit_card.start_year.blank?
|
90
|
+
xml.tag! 'n2:IssueNumber', format(credit_card.issue_number, :two_digits) unless credit_card.issue_number.blank?
|
91
|
+
end
|
92
|
+
|
93
|
+
xml.tag! 'n2:CardOwner' do
|
94
|
+
xml.tag! 'n2:PayerName' do
|
95
|
+
xml.tag! 'n2:FirstName', credit_card.first_name
|
96
|
+
xml.tag! 'n2:LastName', credit_card.last_name
|
97
|
+
end
|
98
|
+
|
99
|
+
xml.tag! 'n2:Payer', options[:email]
|
100
|
+
add_address(xml, 'n2:Address', address)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def credit_card_type(type)
|
106
|
+
case type
|
107
|
+
when 'visa' then 'Visa'
|
108
|
+
when 'master' then 'MasterCard'
|
109
|
+
when 'discover' then 'Discover'
|
110
|
+
when 'american_express' then 'Amex'
|
111
|
+
when 'switch' then 'Switch'
|
112
|
+
when 'solo' then 'Solo'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def build_response(success, message, response, options = {})
|
117
|
+
Response.new(success, message, response, options)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/paypal'
|
2
|
+
|
3
|
+
module ActiveMerchant #:nodoc:
|
4
|
+
module Billing #:nodoc:
|
5
|
+
# The PayPal gateway for PayPal Website Payments Pro Canada only supports Visa and MasterCard
|
6
|
+
class PaypalCaGateway < PaypalGateway
|
7
|
+
self.supported_cardtypes = [:visa, :master, :american_express]
|
8
|
+
self.supported_countries = ['CA']
|
9
|
+
self.homepage_url = 'https://www.paypal.com/cgi-bin/webscr?cmd=_wp-pro-overview-outside'
|
10
|
+
self.display_name = 'PayPal Website Payments Pro (CA)'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/paypal/paypal_common_api'
|
2
|
+
require File.dirname(__FILE__) + '/paypal/paypal_express_response'
|
3
|
+
require File.dirname(__FILE__) + '/paypal_express_common'
|
4
|
+
|
5
|
+
module ActiveMerchant #:nodoc:
|
6
|
+
module Billing #:nodoc:
|
7
|
+
class PaypalExpressGateway < Gateway
|
8
|
+
include PaypalCommonAPI
|
9
|
+
include PaypalExpressCommon
|
10
|
+
|
11
|
+
self.test_redirect_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token='
|
12
|
+
self.supported_countries = ['US']
|
13
|
+
self.homepage_url = 'https://www.paypal.com/cgi-bin/webscr?cmd=xpt/merchant/ExpressCheckoutIntro-outside'
|
14
|
+
self.display_name = 'PayPal Express Checkout'
|
15
|
+
|
16
|
+
def setup_authorization(money, options = {})
|
17
|
+
requires!(options, :return_url, :cancel_return_url)
|
18
|
+
|
19
|
+
commit 'SetExpressCheckout', build_setup_request('Authorization', money, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def setup_purchase(money, options = {})
|
23
|
+
requires!(options, :return_url, :cancel_return_url)
|
24
|
+
|
25
|
+
commit 'SetExpressCheckout', build_setup_request('Sale', money, options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def details_for(token)
|
29
|
+
commit 'GetExpressCheckoutDetails', build_get_details_request(token)
|
30
|
+
end
|
31
|
+
|
32
|
+
def authorize(money, options = {})
|
33
|
+
requires!(options, :token, :payer_id)
|
34
|
+
|
35
|
+
commit 'DoExpressCheckoutPayment', build_sale_or_authorization_request('Authorization', money, options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def purchase(money, options = {})
|
39
|
+
requires!(options, :token, :payer_id)
|
40
|
+
|
41
|
+
commit 'DoExpressCheckoutPayment', build_sale_or_authorization_request('Sale', money, options)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def build_get_details_request(token)
|
46
|
+
xml = Builder::XmlMarkup.new :indent => 2
|
47
|
+
xml.tag! 'GetExpressCheckoutDetailsReq', 'xmlns' => PAYPAL_NAMESPACE do
|
48
|
+
xml.tag! 'GetExpressCheckoutDetailsRequest', 'xmlns:n2' => EBAY_NAMESPACE do
|
49
|
+
xml.tag! 'n2:Version', API_VERSION
|
50
|
+
xml.tag! 'Token', token
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
xml.target!
|
55
|
+
end
|
56
|
+
|
57
|
+
def build_sale_or_authorization_request(action, money, options)
|
58
|
+
currency_code = options[:currency] || currency(money)
|
59
|
+
|
60
|
+
xml = Builder::XmlMarkup.new :indent => 2
|
61
|
+
xml.tag! 'DoExpressCheckoutPaymentReq', 'xmlns' => PAYPAL_NAMESPACE do
|
62
|
+
xml.tag! 'DoExpressCheckoutPaymentRequest', 'xmlns:n2' => EBAY_NAMESPACE do
|
63
|
+
xml.tag! 'n2:Version', API_VERSION
|
64
|
+
xml.tag! 'n2:DoExpressCheckoutPaymentRequestDetails' do
|
65
|
+
xml.tag! 'n2:PaymentAction', action
|
66
|
+
xml.tag! 'n2:Token', options[:token]
|
67
|
+
xml.tag! 'n2:PayerID', options[:payer_id]
|
68
|
+
xml.tag! 'n2:PaymentDetails' do
|
69
|
+
xml.tag! 'n2:OrderTotal', amount(money), 'currencyID' => currency_code
|
70
|
+
|
71
|
+
# All of the values must be included together and add up to the order total
|
72
|
+
if [:subtotal, :shipping, :handling, :tax].all?{ |o| options.has_key?(o) }
|
73
|
+
xml.tag! 'n2:ItemTotal', amount(options[:subtotal]), 'currencyID' => currency_code
|
74
|
+
xml.tag! 'n2:ShippingTotal', amount(options[:shipping]),'currencyID' => currency_code
|
75
|
+
xml.tag! 'n2:HandlingTotal', amount(options[:handling]),'currencyID' => currency_code
|
76
|
+
xml.tag! 'n2:TaxTotal', amount(options[:tax]), 'currencyID' => currency_code
|
77
|
+
end
|
78
|
+
|
79
|
+
xml.tag! 'n2:NotifyURL', options[:notify_url]
|
80
|
+
xml.tag! 'n2:ButtonSource', application_id.to_s.slice(0,32) unless application_id.blank?
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
xml.target!
|
87
|
+
end
|
88
|
+
|
89
|
+
def build_setup_request(action, money, options)
|
90
|
+
xml = Builder::XmlMarkup.new :indent => 2
|
91
|
+
xml.tag! 'SetExpressCheckoutReq', 'xmlns' => PAYPAL_NAMESPACE do
|
92
|
+
xml.tag! 'SetExpressCheckoutRequest', 'xmlns:n2' => EBAY_NAMESPACE do
|
93
|
+
xml.tag! 'n2:Version', API_VERSION
|
94
|
+
xml.tag! 'n2:SetExpressCheckoutRequestDetails' do
|
95
|
+
xml.tag! 'n2:PaymentAction', action
|
96
|
+
xml.tag! 'n2:OrderTotal', amount(money).to_f.zero? ? amount(100) : amount(money), 'currencyID' => options[:currency] || currency(money)
|
97
|
+
if options[:max_amount]
|
98
|
+
xml.tag! 'n2:MaxAmount', amount(options[:max_amount]), 'currencyID' => options[:currency] || currency(options[:max_amount])
|
99
|
+
end
|
100
|
+
add_address(xml, 'n2:Address', options[:shipping_address] || options[:address])
|
101
|
+
xml.tag! 'n2:AddressOverride', options[:address_override] ? '1' : '0'
|
102
|
+
xml.tag! 'n2:NoShipping', options[:no_shipping] ? '1' : '0'
|
103
|
+
xml.tag! 'n2:ReturnURL', options[:return_url]
|
104
|
+
xml.tag! 'n2:CancelURL', options[:cancel_return_url]
|
105
|
+
xml.tag! 'n2:IPAddress', options[:ip]
|
106
|
+
xml.tag! 'n2:OrderDescription', options[:description]
|
107
|
+
xml.tag! 'n2:BuyerEmail', options[:email] unless options[:email].blank?
|
108
|
+
xml.tag! 'n2:InvoiceID', options[:order_id]
|
109
|
+
|
110
|
+
# Customization of the payment page
|
111
|
+
xml.tag! 'n2:PageStyle', options[:page_style] unless options[:page_style].blank?
|
112
|
+
xml.tag! 'n2:cpp-image-header', options[:header_image] unless options[:header_image].blank?
|
113
|
+
xml.tag! 'n2:cpp-header-back-color', options[:header_background_color] unless options[:header_background_color].blank?
|
114
|
+
xml.tag! 'n2:cpp-header-border-color', options[:header_border_color] unless options[:header_border_color].blank?
|
115
|
+
xml.tag! 'n2:cpp-payflow-color', options[:background_color] unless options[:background_color].blank?
|
116
|
+
|
117
|
+
xml.tag! 'n2:LocaleCode', options[:locale] unless options[:locale].blank?
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
xml.target!
|
123
|
+
end
|
124
|
+
|
125
|
+
def build_response(success, message, response, options = {})
|
126
|
+
PaypalExpressResponse.new(success, message, response, options)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ActiveMerchant
|
2
|
+
module Billing
|
3
|
+
module PaypalExpressCommon
|
4
|
+
def self.included(base)
|
5
|
+
base.cattr_accessor :test_redirect_url
|
6
|
+
base.cattr_accessor :live_redirect_url
|
7
|
+
base.live_redirect_url = 'https://www.paypal.com/cgibin/webscr?cmd=_express-checkout&token='
|
8
|
+
end
|
9
|
+
|
10
|
+
def redirect_url
|
11
|
+
test? ? test_redirect_url : live_redirect_url
|
12
|
+
end
|
13
|
+
|
14
|
+
def redirect_url_for(token, options = {})
|
15
|
+
options = {:review => true}.update(options)
|
16
|
+
options[:review] ? "#{redirect_url}#{token}" : "#{redirect_url}#{token}&useraction=commit"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|