activemerchant 1.21.0 → 1.22.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/CHANGELOG +63 -0
- data/CONTRIBUTORS +29 -0
- data/README.md +195 -0
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +2 -0
- data/lib/active_merchant/billing/gateways/barclays_epdq.rb +2 -2
- data/lib/active_merchant/billing/gateways/blue_pay.rb +492 -11
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +46 -19
- data/lib/active_merchant/billing/gateways/certo_direct.rb +1 -1
- data/lib/active_merchant/billing/gateways/cyber_source.rb +342 -106
- data/lib/active_merchant/billing/gateways/elavon.rb +2 -0
- data/lib/active_merchant/billing/gateways/epay.rb +3 -1
- data/lib/active_merchant/billing/gateways/itransact.rb +450 -0
- data/lib/active_merchant/billing/gateways/migs.rb +259 -0
- data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +100 -0
- data/lib/active_merchant/billing/gateways/moneris_us.rb +211 -0
- data/lib/active_merchant/billing/gateways/ogone.rb +104 -12
- data/lib/active_merchant/billing/gateways/orbital.rb +15 -6
- data/lib/active_merchant/billing/gateways/paybox_direct.rb +1 -4
- data/lib/active_merchant/billing/gateways/payflow.rb +8 -3
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +4 -1
- data/lib/active_merchant/billing/gateways/payflow_express.rb +4 -2
- data/lib/active_merchant/billing/gateways/payment_express.rb +1 -1
- data/lib/active_merchant/billing/gateways/paypal.rb +3 -18
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +287 -1
- data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +245 -0
- data/lib/active_merchant/billing/gateways/paypal_express.rb +14 -66
- data/lib/active_merchant/billing/gateways/realex.rb +5 -7
- data/lib/active_merchant/billing/gateways/stripe.rb +1 -9
- data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +2 -2
- data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +1 -5
- data/lib/active_merchant/billing/gateways/viaklix.rb +7 -2
- data/lib/active_merchant/billing/gateways/vindicia.rb +359 -0
- data/lib/active_merchant/billing/integrations/dotpay.rb +22 -0
- data/lib/active_merchant/billing/integrations/dotpay/helper.rb +77 -0
- data/lib/active_merchant/billing/integrations/dotpay/notification.rb +86 -0
- data/lib/active_merchant/billing/integrations/dotpay/return.rb +11 -0
- data/lib/active_merchant/billing/integrations/epay.rb +21 -0
- data/lib/active_merchant/billing/integrations/epay/helper.rb +55 -0
- data/lib/active_merchant/billing/integrations/epay/notification.rb +110 -0
- data/lib/active_merchant/billing/integrations/paypal/notification.rb +2 -1
- data/lib/active_merchant/billing/integrations/quickpay/helper.rb +2 -3
- data/lib/active_merchant/billing/integrations/robokassa.rb +49 -0
- data/lib/active_merchant/billing/integrations/robokassa/common.rb +19 -0
- data/lib/active_merchant/billing/integrations/robokassa/helper.rb +50 -0
- data/lib/active_merchant/billing/integrations/robokassa/notification.rb +55 -0
- data/lib/active_merchant/billing/integrations/robokassa/return.rb +17 -0
- data/lib/active_merchant/billing/integrations/two_checkout.rb +25 -3
- data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +15 -0
- data/lib/active_merchant/billing/integrations/verkkomaksut.rb +20 -0
- data/lib/active_merchant/billing/integrations/verkkomaksut/helper.rb +87 -0
- data/lib/active_merchant/billing/integrations/verkkomaksut/notification.rb +59 -0
- data/lib/active_merchant/version.rb +1 -1
- metadata +59 -26
- metadata.gz.sig +0 -0
@@ -9,17 +9,23 @@ module ActiveMerchant #:nodoc:
|
|
9
9
|
# communication between Ogone systems and your e-commerce website.
|
10
10
|
#
|
11
11
|
# This implementation follows the specification provided in the DirectLink integration
|
12
|
-
# guide version 4.
|
12
|
+
# guide version 4.3.0 (25 April 2012), available here:
|
13
13
|
# https://secure.ogone.com/ncol/Ogone_DirectLink_EN.pdf
|
14
14
|
#
|
15
15
|
# It also features aliases, which allow to store/unstore credit cards, as specified in
|
16
|
-
# the Alias Manager Option guide version 3.2.
|
16
|
+
# the Alias Manager Option guide version 3.2.1 (25 April 2012) available here:
|
17
17
|
# https://secure.ogone.com/ncol/Ogone_Alias_EN.pdf
|
18
18
|
#
|
19
|
-
# It
|
19
|
+
# It also implements the 3-D Secure feature, as specified in the DirectLink with
|
20
|
+
# 3-D Secure guide version 3.0 (25 April 2012) available here:
|
21
|
+
# https://secure.ogone.com/ncol/Ogone_DirectLink-3-D_EN.pdf
|
22
|
+
#
|
23
|
+
# It was last tested on Release 4.92 of Ogone DirectLink + AliasManager + Direct Link 3D
|
24
|
+
# (25 April 2012).
|
20
25
|
#
|
21
26
|
# For any questions or comments, please contact one of the following:
|
22
|
-
# -
|
27
|
+
# - Joel Cogen (joel.cogen@belighted.com)
|
28
|
+
# - Nicolas Jacobeus (nicolas.jacobeus@belighted.com),
|
23
29
|
# - Sébastien Grosjean (public@zencocoon.com),
|
24
30
|
# - Rémy Coutable (remy@jilion.com).
|
25
31
|
#
|
@@ -52,17 +58,46 @@ module ActiveMerchant #:nodoc:
|
|
52
58
|
# puts response.success? # Check whether the transaction was successful
|
53
59
|
# puts response.message # Retrieve the message returned by Ogone
|
54
60
|
# puts response.authorization # Retrieve the unique transaction ID returned by Ogone
|
61
|
+
# puts response.order_id # Retrieve the order ID
|
55
62
|
#
|
56
63
|
# == Alias feature
|
57
64
|
#
|
58
|
-
# To use the alias feature, simply add :
|
65
|
+
# To use the alias feature, simply add :billing_id in the options hash:
|
59
66
|
#
|
60
67
|
# # Associate the alias to that credit card
|
61
|
-
# gateway.purchase(1000, creditcard, :order_id => "1", :
|
68
|
+
# gateway.purchase(1000, creditcard, :order_id => "1", :billing_id => "myawesomecustomer")
|
62
69
|
#
|
63
70
|
# # You can use the alias instead of the credit card for subsequent orders
|
64
71
|
# gateway.purchase(2000, "myawesomecustomer", :order_id => "2")
|
65
72
|
#
|
73
|
+
# # You can also create an alias without making a purchase using store
|
74
|
+
# gateway.store(creditcard, :billing_id => "myawesomecustomer")
|
75
|
+
#
|
76
|
+
# # When using store, you can also let Ogone generate the alias for you
|
77
|
+
# response = gateway.store(creditcard)
|
78
|
+
# puts response.billing_id # Retrieve the generated alias
|
79
|
+
#
|
80
|
+
# == 3-D Secure feature
|
81
|
+
#
|
82
|
+
# To use the 3-D Secure feature, simply add :d3d => true in the options hash:
|
83
|
+
# gateway.purchase(2000, "myawesomecustomer", :order_id => "2", :d3d => true)
|
84
|
+
#
|
85
|
+
# Specific 3-D Secure request options are (please refer to the documentation for more infos about these options):
|
86
|
+
# :win_3ds => :main_window (default), :pop_up or :pop_ix.
|
87
|
+
# :http_accept => "*/*" (default), or any other HTTP_ACCEPT header value.
|
88
|
+
# :http_user_agent => The cardholder's User-Agent string
|
89
|
+
# :accept_url => URL of the web page to show the customer when the payment is authorized.
|
90
|
+
# (or waiting to be authorized).
|
91
|
+
# :decline_url => URL of the web page to show the customer when the acquirer rejects the authorization
|
92
|
+
# more than the maximum permitted number of authorization attempts (10 by default, but can
|
93
|
+
# be changed in the "Global transaction parameters" tab, "Payment retry" section of the
|
94
|
+
# Technical Information page).
|
95
|
+
# :exception_url => URL of the web page to show the customer when the payment result is uncertain.
|
96
|
+
# :paramplus => Field to submit the miscellaneous parameters and their values that you wish to be
|
97
|
+
# returned in the post sale request or final redirection.
|
98
|
+
# :complus => Field to submit a value you wish to be returned in the post sale request or output.
|
99
|
+
# :language => Customer's language, for example: "en_EN"
|
100
|
+
#
|
66
101
|
class OgoneGateway < Gateway
|
67
102
|
|
68
103
|
URLS = {
|
@@ -80,8 +115,16 @@ module ActiveMerchant #:nodoc:
|
|
80
115
|
|
81
116
|
SUCCESS_MESSAGE = "The transaction was successful"
|
82
117
|
|
118
|
+
THREE_D_SECURE_DISPLAY_WAYS = { :main_window => 'MAINW', # display the identification page in the main window
|
119
|
+
# (default value).
|
120
|
+
:pop_up => 'POPUP', # display the identification page in a pop-up window
|
121
|
+
# and return to the main window at the end.
|
122
|
+
:pop_ix => 'POPIX' } # display the identification page in a pop-up window
|
123
|
+
# and remain in the pop-up window.
|
124
|
+
|
83
125
|
OGONE_NO_SIGNATURE_DEPRECATION_MESSAGE = "Signature usage will be required from a future release of ActiveMerchant's Ogone Gateway. Please update your Ogone account to use it."
|
84
126
|
OGONE_LOW_ENCRYPTION_DEPRECATION_MESSAGE = "SHA512 signature encryptor will be required from a future release of ActiveMerchant's Ogone Gateway. Please update your Ogone account to use it."
|
127
|
+
OGONE_STORE_OPTION_DEPRECATION_MESSAGE = "The 'store' option has been renamed to 'billing_id', and its usage is deprecated."
|
85
128
|
|
86
129
|
self.supported_countries = ['BE', 'DE', 'FR', 'NL', 'AT', 'CH']
|
87
130
|
# also supports Airplus and UATP
|
@@ -152,6 +195,14 @@ module ActiveMerchant #:nodoc:
|
|
152
195
|
perform_reference_credit(money, reference, options)
|
153
196
|
end
|
154
197
|
|
198
|
+
# Store a credit card by creating an Ogone Alias
|
199
|
+
def store(payment_source, options = {})
|
200
|
+
options.merge!(:alias_operation => 'BYOGONE') unless options.has_key?(:billing_id) || options.has_key?(:store)
|
201
|
+
response = authorize(1, payment_source, options)
|
202
|
+
void(response.authorization) if response.success?
|
203
|
+
response
|
204
|
+
end
|
205
|
+
|
155
206
|
def test?
|
156
207
|
@options[:test] || super
|
157
208
|
end
|
@@ -164,7 +215,7 @@ module ActiveMerchant #:nodoc:
|
|
164
215
|
|
165
216
|
def reference_transaction?(identifier)
|
166
217
|
return false unless identifier.is_a?(String)
|
167
|
-
|
218
|
+
_, action = identifier.split(";")
|
168
219
|
!action.nil?
|
169
220
|
end
|
170
221
|
|
@@ -188,21 +239,44 @@ module ActiveMerchant #:nodoc:
|
|
188
239
|
|
189
240
|
def add_payment_source(post, payment_source, options)
|
190
241
|
if payment_source.is_a?(String)
|
191
|
-
add_alias(post, payment_source)
|
242
|
+
add_alias(post, payment_source, options[:alias_operation])
|
192
243
|
add_eci(post, options[:eci] || '9')
|
193
244
|
else
|
194
|
-
|
245
|
+
if options.has_key?(:store)
|
246
|
+
deprecated OGONE_STORE_OPTION_DEPRECATION_MESSAGE
|
247
|
+
options[:billing_id] ||= options[:store]
|
248
|
+
end
|
249
|
+
add_alias(post, options[:billing_id], options[:alias_operation])
|
195
250
|
add_eci(post, options[:eci] || '7')
|
251
|
+
add_d3d(post, options) if options[:d3d]
|
196
252
|
add_creditcard(post, payment_source)
|
197
253
|
end
|
198
254
|
end
|
199
255
|
|
256
|
+
def add_d3d(post, options)
|
257
|
+
add_pair post, 'FLAG3D', 'Y'
|
258
|
+
win_3ds = THREE_D_SECURE_DISPLAY_WAYS.key?(options[:win_3ds]) ?
|
259
|
+
THREE_D_SECURE_DISPLAY_WAYS[options[:win_3ds]] :
|
260
|
+
THREE_D_SECURE_DISPLAY_WAYS[:main_window]
|
261
|
+
add_pair post, 'WIN3DS', win_3ds
|
262
|
+
|
263
|
+
add_pair post, 'HTTP_ACCEPT', options[:http_accept] || "*/*"
|
264
|
+
add_pair post, 'HTTP_USER_AGENT', options[:http_user_agent] if options[:http_user_agent]
|
265
|
+
add_pair post, 'ACCEPTURL', options[:accept_url] if options[:accept_url]
|
266
|
+
add_pair post, 'DECLINEURL', options[:decline_url] if options[:decline_url]
|
267
|
+
add_pair post, 'EXCEPTIONURL', options[:exception_url] if options[:exception_url]
|
268
|
+
add_pair post, 'PARAMPLUS', options[:paramplus] if options[:paramplus]
|
269
|
+
add_pair post, 'COMPLUS', options[:complus] if options[:complus]
|
270
|
+
add_pair post, 'LANGUAGE', options[:language] if options[:language]
|
271
|
+
end
|
272
|
+
|
200
273
|
def add_eci(post, eci)
|
201
274
|
add_pair post, 'ECI', eci.to_s
|
202
275
|
end
|
203
276
|
|
204
|
-
def add_alias(post, _alias)
|
277
|
+
def add_alias(post, _alias, alias_operation = nil)
|
205
278
|
add_pair post, 'ALIAS', _alias
|
279
|
+
add_pair post, 'ALIASOPERATION', alias_operation unless alias_operation.nil?
|
206
280
|
end
|
207
281
|
|
208
282
|
def add_authorization(post, authorization)
|
@@ -242,7 +316,15 @@ module ActiveMerchant #:nodoc:
|
|
242
316
|
|
243
317
|
def parse(body)
|
244
318
|
xml_root = REXML::Document.new(body).root
|
245
|
-
convert_attributes_to_hash(xml_root.attributes)
|
319
|
+
response = convert_attributes_to_hash(xml_root.attributes)
|
320
|
+
|
321
|
+
# Add HTML_ANSWER element (3-D Secure specific to the response's params)
|
322
|
+
# Note: HTML_ANSWER is not an attribute so we add it "by hand" to the response
|
323
|
+
if html_answer = REXML::XPath.first(xml_root, "//HTML_ANSWER")
|
324
|
+
response["HTML_ANSWER"] = html_answer.text
|
325
|
+
end
|
326
|
+
|
327
|
+
response
|
246
328
|
end
|
247
329
|
|
248
330
|
def commit(action, parameters)
|
@@ -259,7 +341,7 @@ module ActiveMerchant #:nodoc:
|
|
259
341
|
:avs_result => { :code => AVS_MAPPING[response["AAVCheck"]] },
|
260
342
|
:cvv_result => CVV_MAPPING[response["CVCCheck"]]
|
261
343
|
}
|
262
|
-
|
344
|
+
OgoneResponse.new(successful?(response), message_from(response), response, options)
|
263
345
|
end
|
264
346
|
|
265
347
|
def successful?(response)
|
@@ -326,5 +408,15 @@ module ActiveMerchant #:nodoc:
|
|
326
408
|
response_hash
|
327
409
|
end
|
328
410
|
end
|
411
|
+
|
412
|
+
class OgoneResponse < Response
|
413
|
+
def order_id
|
414
|
+
@params['orderID']
|
415
|
+
end
|
416
|
+
|
417
|
+
def billing_id
|
418
|
+
@params['ALIAS']
|
419
|
+
end
|
420
|
+
end
|
329
421
|
end
|
330
422
|
end
|
@@ -77,6 +77,8 @@ module ActiveMerchant #:nodoc:
|
|
77
77
|
"EUR" => '978'
|
78
78
|
}
|
79
79
|
|
80
|
+
AVS_SUPPORTED_COUNTRIES = ['US', 'CA', 'UK', 'GB']
|
81
|
+
|
80
82
|
def initialize(options = {})
|
81
83
|
requires!(options, :merchant_id)
|
82
84
|
requires!(options, :login, :password) unless options[:ip_authentication]
|
@@ -148,12 +150,7 @@ module ActiveMerchant #:nodoc:
|
|
148
150
|
|
149
151
|
def add_address(xml, creditcard, options)
|
150
152
|
if address = options[:billing_address] || options[:address]
|
151
|
-
xml
|
152
|
-
xml.tag! :AVSaddress1, address[:address1]
|
153
|
-
xml.tag! :AVSaddress2, address[:address2]
|
154
|
-
xml.tag! :AVScity, address[:city]
|
155
|
-
xml.tag! :AVSstate, address[:state]
|
156
|
-
xml.tag! :AVSphoneNum, address[:phone] ? address[:phone].scan(/\d/).join.to_s : nil
|
153
|
+
add_avs_details(xml, address)
|
157
154
|
xml.tag! :AVSname, creditcard.name
|
158
155
|
xml.tag! :AVScountryCode, address[:country]
|
159
156
|
end
|
@@ -177,6 +174,18 @@ module ActiveMerchant #:nodoc:
|
|
177
174
|
xml.tag! :CurrencyExponent, '2' # Will need updating to support currencies such as the Yen.
|
178
175
|
end
|
179
176
|
|
177
|
+
def add_avs_details(xml, address)
|
178
|
+
return unless AVS_SUPPORTED_COUNTRIES.include?(address[:country].to_s)
|
179
|
+
|
180
|
+
xml.tag! :AVSzip, address[:zip]
|
181
|
+
xml.tag! :AVSaddress1, address[:address1]
|
182
|
+
xml.tag! :AVSaddress2, address[:address2]
|
183
|
+
xml.tag! :AVScity, address[:city]
|
184
|
+
xml.tag! :AVSstate, address[:state]
|
185
|
+
xml.tag! :AVSphoneNum, address[:phone] ? address[:phone].scan(/\d/).join.to_s : nil
|
186
|
+
end
|
187
|
+
|
188
|
+
|
180
189
|
def parse(body)
|
181
190
|
response = {}
|
182
191
|
xml = REXML::Document.new(body)
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'iconv'
|
2
|
-
|
3
1
|
module ActiveMerchant #:nodoc:
|
4
2
|
module Billing #:nodoc:
|
5
3
|
class PayboxDirectGateway < Gateway
|
@@ -39,7 +37,7 @@ module ActiveMerchant #:nodoc:
|
|
39
37
|
|
40
38
|
SUCCESS_CODES = ['00000']
|
41
39
|
UNAVAILABILITY_CODES = ['00001', '00097', '00098']
|
42
|
-
FRAUD_CODES = ['00102','00104','
|
40
|
+
FRAUD_CODES = ['00102','00104','00134','00138','00141','00143','00157','00159']
|
43
41
|
SUCCESS_MESSAGE = 'The transaction was approved'
|
44
42
|
FAILURE_MESSAGE = 'The transaction failed'
|
45
43
|
|
@@ -132,7 +130,6 @@ module ActiveMerchant #:nodoc:
|
|
132
130
|
end
|
133
131
|
|
134
132
|
def parse(body)
|
135
|
-
body = Iconv.iconv("UTF-8","LATIN1", body.to_s).join
|
136
133
|
results = {}
|
137
134
|
body.split(/&/).each do |pair|
|
138
135
|
key,val = pair.split(/\=/)
|
@@ -198,7 +198,7 @@ module ActiveMerchant #:nodoc:
|
|
198
198
|
xml.tag! 'PayPeriod', get_pay_period(options)
|
199
199
|
xml.tag! 'Term', options[:payments] unless options[:payments].nil?
|
200
200
|
xml.tag! 'Comment', options[:comment] unless options[:comment].nil?
|
201
|
-
|
201
|
+
xml.tag! 'RetryNumDays', options[:retry_num_days] unless options[:retry_num_days].nil?
|
202
202
|
|
203
203
|
if initial_tx = options[:initial_transaction]
|
204
204
|
requires!(initial_tx, [:type, :authorization, :purchase])
|
@@ -207,8 +207,13 @@ module ActiveMerchant #:nodoc:
|
|
207
207
|
xml.tag! 'OptionalTrans', TRANSACTIONS[initial_tx[:type]]
|
208
208
|
xml.tag! 'OptionalTransAmt', amount(initial_tx[:amount]) unless initial_tx[:amount].blank?
|
209
209
|
end
|
210
|
-
|
211
|
-
|
210
|
+
|
211
|
+
if action == :add
|
212
|
+
xml.tag! 'Start', format_rp_date(options[:starting_at] || Date.today + 1 )
|
213
|
+
else
|
214
|
+
xml.tag! 'Start', format_rp_date(options[:starting_at]) unless options[:starting_at].nil?
|
215
|
+
end
|
216
|
+
|
212
217
|
xml.tag! 'EMail', options[:email] unless options[:email].nil?
|
213
218
|
|
214
219
|
billing_address = options[:billing_address] || options[:address]
|
@@ -111,7 +111,10 @@ module ActiveMerchant #:nodoc:
|
|
111
111
|
|
112
112
|
unless money.nil?
|
113
113
|
xml.tag! 'Invoice' do
|
114
|
-
xml.tag!
|
114
|
+
xml.tag!('TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money))
|
115
|
+
xml.tag!('Description', options[:description]) unless options[:description].blank?
|
116
|
+
xml.tag!('Comment', options[:comment]) unless options[:comment].blank?
|
117
|
+
xml.tag!('ExtData', 'Name'=> 'COMMENT2', 'Value'=> options[:comment2]) unless options[:comment2].blank?
|
115
118
|
end
|
116
119
|
end
|
117
120
|
end
|
@@ -33,6 +33,7 @@ module ActiveMerchant #:nodoc:
|
|
33
33
|
# [<tt>:notify_url</tt>] (opt) Your URL for receiving Instant Payment Notification (IPN) about this transaction.
|
34
34
|
# [<tt>:comment</tt>] (opt) Comment field which will be reported to Payflow backend (at manager.paypal.com) as Comment1
|
35
35
|
# [<tt>:comment2</tt>] (opt) Comment field which will be reported to Payflow backend (at manager.paypal.com) as Comment2
|
36
|
+
# [<tt>:discount</tt>] (opt) Total discounts in cents
|
36
37
|
#
|
37
38
|
# ==Line Items
|
38
39
|
# Support for order line items is available, but has to be enabled on the PayFlow backend. This is what I was told by Todd Sieber at Technical Support:
|
@@ -177,10 +178,11 @@ module ActiveMerchant #:nodoc:
|
|
177
178
|
end
|
178
179
|
if items.any?
|
179
180
|
xml.tag! 'ExtData', 'Name' => 'CURRENCY', 'Value' => options[:currency] || currency(money)
|
180
|
-
xml.tag! 'ExtData', 'Name' => "ITEMAMT", 'Value' => amount(money)
|
181
|
+
xml.tag! 'ExtData', 'Name' => "ITEMAMT", 'Value' => amount(options[:subtotal] || money)
|
181
182
|
end
|
182
|
-
|
183
|
+
xml.tag! 'DiscountAmt', amount(options[:discount]) if options[:discount]
|
183
184
|
xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
|
185
|
+
|
184
186
|
end
|
185
187
|
|
186
188
|
xml.tag! 'Tender' do
|
@@ -16,7 +16,7 @@ module ActiveMerchant #:nodoc:
|
|
16
16
|
# However, regular accounts with DPS only support VISA and Mastercard
|
17
17
|
self.supported_cardtypes = [ :visa, :master, :american_express, :diners_club, :jcb ]
|
18
18
|
|
19
|
-
self.supported_countries = [
|
19
|
+
self.supported_countries = %w[ AU MY NZ SG ZA GB US ]
|
20
20
|
|
21
21
|
self.homepage_url = 'http://www.paymentexpress.com/'
|
22
22
|
self.display_name = 'PaymentExpress'
|
@@ -1,10 +1,12 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/paypal/paypal_common_api'
|
2
|
+
require File.dirname(__FILE__) + '/paypal/paypal_recurring_api'
|
2
3
|
require File.dirname(__FILE__) + '/paypal_express'
|
3
4
|
|
4
5
|
module ActiveMerchant #:nodoc:
|
5
6
|
module Billing #:nodoc:
|
6
7
|
class PaypalGateway < Gateway
|
7
8
|
include PaypalCommonAPI
|
9
|
+
include PaypalRecurringApi
|
8
10
|
|
9
11
|
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
10
12
|
self.supported_countries = ['US']
|
@@ -49,24 +51,7 @@ module ActiveMerchant #:nodoc:
|
|
49
51
|
xml.tag! 'n2:' + transaction_type + 'RequestDetails' do
|
50
52
|
xml.tag! 'n2:ReferenceID', reference_id if transaction_type == 'DoReferenceTransaction'
|
51
53
|
xml.tag! 'n2:PaymentAction', action
|
52
|
-
xml
|
53
|
-
xml.tag! 'n2:OrderTotal', localized_amount(money, currency_code), 'currencyID' => currency_code
|
54
|
-
|
55
|
-
# All of the values must be included together and add up to the order total
|
56
|
-
if [:subtotal, :shipping, :handling, :tax].all?{ |o| options.has_key?(o) }
|
57
|
-
xml.tag! 'n2:ItemTotal', localized_amount(options[:subtotal], currency_code), 'currencyID' => currency_code
|
58
|
-
xml.tag! 'n2:ShippingTotal', localized_amount(options[:shipping], currency_code),'currencyID' => currency_code
|
59
|
-
xml.tag! 'n2:HandlingTotal', localized_amount(options[:handling], currency_code),'currencyID' => currency_code
|
60
|
-
xml.tag! 'n2:TaxTotal', localized_amount(options[:tax], currency_code), 'currencyID' => currency_code
|
61
|
-
end
|
62
|
-
|
63
|
-
xml.tag! 'n2:NotifyURL', options[:notify_url]
|
64
|
-
xml.tag! 'n2:OrderDescription', options[:description]
|
65
|
-
xml.tag! 'n2:InvoiceID', options[:order_id]
|
66
|
-
xml.tag! 'n2:ButtonSource', application_id.to_s.slice(0,32) unless application_id.blank?
|
67
|
-
|
68
|
-
add_address(xml, 'n2:ShipToAddress', options[:shipping_address]) if options[:shipping_address]
|
69
|
-
end
|
54
|
+
add_payment_details(xml, money, currency_code, options)
|
70
55
|
add_credit_card(xml, credit_card_or_referenced_id, billing_address, options) unless transaction_type == 'DoReferenceTransaction'
|
71
56
|
xml.tag! 'n2:IPAddress', options[:ip]
|
72
57
|
end
|
@@ -117,7 +117,158 @@ module ActiveMerchant #:nodoc:
|
|
117
117
|
refund(money, identification, options)
|
118
118
|
end
|
119
119
|
|
120
|
+
# ==== For full documentation see {Paypal API Reference:}[https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_soap_r_DoReferenceTransaction]
|
121
|
+
# ==== Parameter:
|
122
|
+
# * <tt>:money</tt> -- (Required) The amount of this new transaction,
|
123
|
+
# required fo the payment details portion of this request
|
124
|
+
#
|
125
|
+
# ==== Options:
|
126
|
+
# * <tt>:reference_id</tt> -- (Required) A transaction ID from a previous purchase, such as a credit card charge using the DoDirectPayment API, or a billing agreement ID.
|
127
|
+
# * <tt>:payment_action</tt> -- (Optional) How you want to obtain payment. It is one of the following values:
|
128
|
+
#
|
129
|
+
# Authorization – This payment is a basic authorization subject to settlement with PayPal Authorization and Capture.
|
130
|
+
# Sale – This is a final sale for which you are requesting payment.
|
131
|
+
#
|
132
|
+
# * <tt>:ip_address</tt> -- (Optional) IP address of the buyer’s browser.
|
133
|
+
# Note: PayPal records this IP addresses as a means to detect possible fraud.
|
134
|
+
# * <tt>:req_confirm_shipping</tt> -- Whether you require that the buyer’s shipping address on file with PayPal be a confirmed address. You must have permission from PayPal to not require a confirmed address. It is one of the following values:
|
135
|
+
#
|
136
|
+
# 0 – You do not require that the buyer’s shipping address be a confirmed address.
|
137
|
+
# 1 – You require that the buyer’s shipping address be a confirmed address.
|
138
|
+
#
|
139
|
+
# * <tt>:merchant_session_id</tt> -- (Optional) Your buyer session identification token.
|
140
|
+
# * <tt>:return_fmf_details</tt> -- (Optional) Flag to indicate whether you want the results returned by Fraud Management Filters. By default, you do not receive this information. It is one of the following values:
|
141
|
+
#
|
142
|
+
# 0 – Do not receive FMF details (default)
|
143
|
+
# 1 – Receive FMF details
|
144
|
+
#
|
145
|
+
# * <tt>:soft_descriptor</tt> -- (Optional) Per transaction description of the payment that is passed to the consumer’s credit card statement. If the API request provides a value for the soft descriptor field, the full descriptor displayed on the buyer’s statement has the following format:
|
146
|
+
#
|
147
|
+
# <PP * | PAYPAL *><Merchant descriptor as set in the Payment Receiving Preferences><1 space><soft descriptor>
|
148
|
+
# The soft descriptor can contain only the following characters:
|
149
|
+
#
|
150
|
+
# Alphanumeric characters
|
151
|
+
# - (dash)
|
152
|
+
# * (asterisk)
|
153
|
+
# . (period)
|
154
|
+
# {space}
|
155
|
+
#
|
156
|
+
def reference_transaction(money, options = {})
|
157
|
+
requires!(options, :reference_id)
|
158
|
+
commit 'DoReferenceTransaction', build_reference_transaction_request(money, options)
|
159
|
+
end
|
160
|
+
|
161
|
+
def transaction_details(transaction_id)
|
162
|
+
commit 'GetTransactionDetails', build_get_transaction_details(transaction_id)
|
163
|
+
end
|
164
|
+
|
165
|
+
# ==== For full documentation see {PayPal API Reference}[https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_soap_r_TransactionSearch]
|
166
|
+
# ==== Options:
|
167
|
+
# * <tt>:payer </tt> -- (Optional) Search by the buyer’s email address.
|
168
|
+
# * <tt>:receipt_id </tt> -- (Optional) Search by the PayPal Account Optional receipt ID.
|
169
|
+
# * <tt>:receiver </tt> -- (Optional) Search by the receiver’s email address. If the merchant account has only one email address, this is the primary email. It can also be a non-primary email address.
|
170
|
+
# * <tt>:transaction_id</tt> -- (Optional) Search by the transaction ID. The returned results are from the merchant’s transaction records.
|
171
|
+
# * <tt>:invoice_id</tt> -- (Optional) Search by invoice identification key, as set by you for the original transaction. This field searches the records for items the merchant sells, not the items purchased.
|
172
|
+
# * <tt>:card_number </tt> -- (Optional) Search by credit card number, as set by you for the original transaction. This field searches the records for items the merchant sells, not the items purchased.
|
173
|
+
# * <tt>:auction_item_number </tt> -- (Optional) Search by auction item number of the purchased goods.
|
174
|
+
# * <tt>:transaction_class </tt> -- (Optional) Search by classification of transaction. Some kinds of possible classes of transactions are not searchable with this field. You cannot search for bank transfer withdrawals, for example. It is one of the following values:
|
175
|
+
# All – All transaction classifications
|
176
|
+
# Sent – Only payments sent
|
177
|
+
# Received – Only payments received
|
178
|
+
# MassPay – Only mass payments
|
179
|
+
# MoneyRequest – Only money requests
|
180
|
+
# FundsAdded – Only funds added to balance
|
181
|
+
# FundsWithdrawn – Only funds withdrawn from balance
|
182
|
+
# Referral – Only transactions involving referrals
|
183
|
+
# Fee – Only transactions involving fees
|
184
|
+
# Subscription – Only transactions involving subscriptions
|
185
|
+
# Dividend – Only transactions involving dividends
|
186
|
+
# Billpay – Only transactions involving BillPay Transactions
|
187
|
+
# Refund – Only transactions involving funds
|
188
|
+
# CurrencyConversions – Only transactions involving currency conversions
|
189
|
+
# BalanceTransfer – Only transactions involving balance transfers
|
190
|
+
# Reversal – Only transactions involving BillPay reversals
|
191
|
+
# Shipping – Only transactions involving UPS shipping fees
|
192
|
+
# BalanceAffecting – Only transactions that affect the account balance
|
193
|
+
# ECheck – Only transactions involving eCheck
|
194
|
+
#
|
195
|
+
# * <tt>:currency_code </tt> -- (Optional) Search by currency code.
|
196
|
+
# * <tt>:status</tt> -- (Optional) Search by transaction status. It is one of the following values:
|
197
|
+
# One of:
|
198
|
+
# Pending – The payment is pending. The specific reason the payment is pending is returned by the GetTransactionDetails API PendingReason field.
|
199
|
+
# Processing – The payment is being processed.
|
200
|
+
# Success – The payment has been completed and the funds have been added successfully to your account balance.
|
201
|
+
# Denied – You denied the payment. This happens only if the payment was previously pending.
|
202
|
+
# Reversed – A payment was reversed due to a chargeback or other type of reversal. The funds have been removed from your account balance and returned to the buyer.
|
203
|
+
#
|
204
|
+
def transaction_search(options)
|
205
|
+
requires!(options, :start_date)
|
206
|
+
commit 'TransactionSearch', build_transaction_search(options)
|
207
|
+
end
|
208
|
+
|
209
|
+
# ==== Parameters:
|
210
|
+
# * <tt>:return_all_currencies</tt> -- Either '1' or '0'
|
211
|
+
# 0 – Return only the balance for the primary currency holding.
|
212
|
+
# 1 – Return the balance for each currency holding.
|
213
|
+
#
|
214
|
+
def balance(return_all_currencies = false)
|
215
|
+
clean_currency_argument = case return_all_currencies
|
216
|
+
when 1, '1' , true; '1'
|
217
|
+
else
|
218
|
+
'0'
|
219
|
+
end
|
220
|
+
commit 'GetBalance', build_get_balance(clean_currency_argument)
|
221
|
+
end
|
222
|
+
|
223
|
+
# DoAuthorization takes the transaction_id returned when you call
|
224
|
+
# DoExpressCheckoutPayment with a PaymentAction of 'Order'.
|
225
|
+
# When you did that, you created an order authorization subject to settlement
|
226
|
+
# with PayPal DoAuthorization and DoCapture
|
227
|
+
#
|
228
|
+
# ==== Parameters:
|
229
|
+
# * <tt>:transaction_id</tt> -- The ID returned by DoExpressCheckoutPayment with a PaymentAction of 'Order'.
|
230
|
+
# * <tt>:money</tt> -- The amount of money to be authorized for this purchase.
|
231
|
+
#
|
232
|
+
def authorize_transaction(transaction_id, money, options = {})
|
233
|
+
commit 'DoAuthorization', build_do_authorize(transaction_id, money, options)
|
234
|
+
end
|
235
|
+
|
236
|
+
# The ManagePendingTransactionStatus API operation accepts or denys a
|
237
|
+
# pending transaction held by Fraud Management Filters.
|
238
|
+
#
|
239
|
+
# ==== Parameters:
|
240
|
+
# * <tt>:transaction_id</tt> -- The ID of the transaction held by Fraud Management Filters.
|
241
|
+
# * <tt>:action</tt> -- Either 'Accept' or 'Deny'
|
242
|
+
#
|
243
|
+
def manage_pending_transaction(transaction_id, action)
|
244
|
+
commit 'ManagePendingTransactionStatus', build_manage_pending_transaction_status(transaction_id, action)
|
245
|
+
end
|
246
|
+
|
120
247
|
private
|
248
|
+
def build_request_wrapper(action, options = {})
|
249
|
+
xml = Builder::XmlMarkup.new :indent => 2
|
250
|
+
xml.tag! action + 'Req', 'xmlns' => PAYPAL_NAMESPACE do
|
251
|
+
xml.tag! action + 'Request', 'xmlns:n2' => EBAY_NAMESPACE do
|
252
|
+
xml.tag! 'n2:Version', API_VERSION
|
253
|
+
if options[:request_details]
|
254
|
+
xml.tag! 'n2:' + action + 'RequestDetails' do
|
255
|
+
yield(xml)
|
256
|
+
end
|
257
|
+
else
|
258
|
+
yield(xml)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
xml.target!
|
263
|
+
end
|
264
|
+
|
265
|
+
def build_do_authorize(transaction_id, money, options = {})
|
266
|
+
build_request_wrapper('DoAuthorization') do |xml|
|
267
|
+
xml.tag! 'TransactionID', transaction_id
|
268
|
+
xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
121
272
|
def build_reauthorize_request(money, authorization, options)
|
122
273
|
xml = Builder::XmlMarkup.new
|
123
274
|
|
@@ -204,6 +355,55 @@ module ActiveMerchant #:nodoc:
|
|
204
355
|
xml.target!
|
205
356
|
end
|
206
357
|
|
358
|
+
def build_manage_pending_transaction_status(transaction_id, action)
|
359
|
+
build_request_wrapper('ManagePendingTransactionStatus') do |xml|
|
360
|
+
xml.tag! 'TransactionID', transaction_id
|
361
|
+
xml.tag! 'Action', action
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
def build_reference_transaction_request(money, options)
|
366
|
+
opts = options.dup
|
367
|
+
opts[:ip_address] ||= opts[:ip]
|
368
|
+
currency_code = opts[:currency] || currency(money)
|
369
|
+
reference_transaction_optional_fields = %w{ n2:ReferenceID n2:PaymentAction
|
370
|
+
n2:PaymentType n2:IPAddress
|
371
|
+
n2:ReqConfirmShipping n2:MerchantSessionId
|
372
|
+
n2:ReturnFMFDetails n2:SoftDescriptor }
|
373
|
+
build_request_wrapper('DoReferenceTransaction', :request_details => true) do |xml|
|
374
|
+
add_optional_fields(xml, reference_transaction_optional_fields, opts)
|
375
|
+
add_payment_details(xml, money, currency_code, opts)
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
def build_get_transaction_details(transaction_id)
|
380
|
+
build_request_wrapper('GetTransactionDetails') do |xml|
|
381
|
+
xml.tag! 'TransactionID', transaction_id
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
def build_transaction_search(options)
|
386
|
+
currency_code = options[:currency_code]
|
387
|
+
currency_code ||= currency(options[:amount]) if options[:amount]
|
388
|
+
transaction_search_optional_fields = %w{ Payer ReceiptID Receiver
|
389
|
+
TransactionID InvoiceID CardNumber
|
390
|
+
AuctionItemNumber TransactionClass
|
391
|
+
CurrencyCode Status }
|
392
|
+
build_request_wrapper('TransactionSearch') do |xml|
|
393
|
+
xml.tag! 'StartDate', date_to_iso(options[:start_date])
|
394
|
+
xml.tag! 'EndDate', date_to_iso(options[:end_date]) unless options[:end_date].blank?
|
395
|
+
add_optional_fields(xml, transaction_search_optional_fields, options)
|
396
|
+
xml.tag! 'Amount', localized_amount(options[:amount], currency_code), 'currencyID' => currency_code unless options[:amount].blank?
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
|
401
|
+
def build_get_balance(return_all_currencies)
|
402
|
+
build_request_wrapper('GetBalance') do |xml|
|
403
|
+
xml.tag! 'ReturnAllCurrencies', return_all_currencies unless return_all_currencies.nil?
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
207
407
|
def parse(action, xml)
|
208
408
|
legacy_hash = legacy_parse(action, xml)
|
209
409
|
xml = strip_attributes(xml)
|
@@ -313,11 +513,93 @@ module ActiveMerchant #:nodoc:
|
|
313
513
|
xml.tag! 'n2:CityName', address[:city]
|
314
514
|
xml.tag! 'n2:StateOrProvince', address[:state].blank? ? 'N/A' : address[:state]
|
315
515
|
xml.tag! 'n2:Country', address[:country]
|
316
|
-
xml.tag! 'n2:Phone', address[:phone]
|
516
|
+
xml.tag! 'n2:Phone', address[:phone] unless address[:phone].blank?
|
317
517
|
xml.tag! 'n2:PostalCode', address[:zip]
|
318
518
|
end
|
319
519
|
end
|
320
520
|
|
521
|
+
def add_payment_details_items_xml(xml, options, currency_code)
|
522
|
+
options[:items].each do |item|
|
523
|
+
xml.tag! 'n2:PaymentDetailsItem' do
|
524
|
+
xml.tag! 'n2:Name', item[:name]
|
525
|
+
xml.tag! 'n2:Number', item[:number]
|
526
|
+
xml.tag! 'n2:Quantity', item[:quantity]
|
527
|
+
if item[:amount]
|
528
|
+
xml.tag! 'n2:Amount', localized_amount(item[:amount], currency_code), 'currencyID' => currency_code
|
529
|
+
end
|
530
|
+
xml.tag! 'n2:Description', item[:description]
|
531
|
+
xml.tag! 'n2:ItemURL', item[:url]
|
532
|
+
xml.tag! 'n2:ItemCategory', item[:category] if item[:category]
|
533
|
+
end
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
def add_payment_details(xml, money, currency_code, options = {})
|
538
|
+
xml.tag! 'n2:PaymentDetails' do
|
539
|
+
xml.tag! 'n2:OrderTotal', localized_amount(money, currency_code), 'currencyID' => currency_code
|
540
|
+
|
541
|
+
# All of the values must be included together and add up to the order total
|
542
|
+
if [:subtotal, :shipping, :handling, :tax].all?{ |o| options.has_key?(o) }
|
543
|
+
xml.tag! 'n2:ItemTotal', localized_amount(options[:subtotal], currency_code), 'currencyID' => currency_code
|
544
|
+
xml.tag! 'n2:ShippingTotal', localized_amount(options[:shipping], currency_code),'currencyID' => currency_code
|
545
|
+
xml.tag! 'n2:HandlingTotal', localized_amount(options[:handling], currency_code),'currencyID' => currency_code
|
546
|
+
xml.tag! 'n2:TaxTotal', localized_amount(options[:tax], currency_code), 'currencyID' => currency_code
|
547
|
+
end
|
548
|
+
|
549
|
+
xml.tag! 'n2:InsuranceTotal', localized_amount(options[:insurance_total], currency_code),'currencyID' => currency_code unless options[:insurance_total].blank?
|
550
|
+
xml.tag! 'n2:ShippingDiscount', localized_amount(options[:shipping_discount], currency_code),'currencyID' => currency_code unless options[:shipping_discount].blank?
|
551
|
+
xml.tag! 'n2:InsuranceOptionOffered', options[:insurance_option_offered] if options.has_key?(:insurance_option_offered)
|
552
|
+
|
553
|
+
xml.tag! 'n2:OrderDescription', options[:description] unless options[:description].blank?
|
554
|
+
|
555
|
+
# Custom field Character length and limitations: 256 single-byte alphanumeric characters
|
556
|
+
xml.tag! 'n2:Custom', options[:custom] unless options[:custom].blank?
|
557
|
+
|
558
|
+
xml.tag! 'n2:InvoiceID', (options[:order_id] || options[:invoice_id]) unless (options[:order_id] || options[:invoice_id]).blank?
|
559
|
+
xml.tag! 'n2:ButtonSource', application_id.to_s.slice(0,32) unless application_id.blank?
|
560
|
+
|
561
|
+
# The notify URL applies only to DoExpressCheckoutPayment.
|
562
|
+
# This value is ignored when set in SetExpressCheckout or GetExpressCheckoutDetails
|
563
|
+
xml.tag! 'n2:NotifyURL', options[:notify_url] unless options[:notify_url].blank?
|
564
|
+
|
565
|
+
add_address(xml, 'n2:ShipToAddress', options[:shipping_address]) unless options[:shipping_address].blank?
|
566
|
+
|
567
|
+
add_payment_details_items_xml(xml, options, currency_code) unless options[:items].blank?
|
568
|
+
|
569
|
+
add_express_only_payment_details(xml, options) if options[:express_request]
|
570
|
+
|
571
|
+
# Any value other than Y – This is not a recurring transaction
|
572
|
+
# To pass Y in this field, you must have established a billing agreement with
|
573
|
+
# the buyer specifying the amount, frequency, and duration of the recurring payment.
|
574
|
+
# requires version 80.0 of the API
|
575
|
+
xml.tag! 'n2:Recurring', options[:recurring] unless options[:recurring].blank?
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
def add_express_only_payment_details(xml, options = {})
|
580
|
+
add_optional_fields(xml,
|
581
|
+
%w{n2:NoteText n2:SoftDescriptor
|
582
|
+
n2:TransactionId n2:AllowedPaymentMethodType
|
583
|
+
n2:PaymentRequestID n2:PaymentAction},
|
584
|
+
options)
|
585
|
+
end
|
586
|
+
|
587
|
+
def add_optional_fields(xml, optional_fields, options = {})
|
588
|
+
optional_fields.each do |optional_text_field|
|
589
|
+
if optional_text_field =~ /(\w+:)(\w+)/
|
590
|
+
ns = $1
|
591
|
+
field = $2
|
592
|
+
field_as_symbol = field.underscore.to_sym
|
593
|
+
else
|
594
|
+
ns = ''
|
595
|
+
field = optional_text_field
|
596
|
+
field_as_symbol = optional_text_field.underscore.to_sym
|
597
|
+
end
|
598
|
+
xml.tag! ns + field, options[field_as_symbol] unless options[field_as_symbol].blank?
|
599
|
+
end
|
600
|
+
xml
|
601
|
+
end
|
602
|
+
|
321
603
|
def endpoint_url
|
322
604
|
URLS[test? ? :test : :live][@options[:signature].blank? ? :certificate : :signature]
|
323
605
|
end
|
@@ -349,6 +631,10 @@ module ActiveMerchant #:nodoc:
|
|
349
631
|
def message_from(response)
|
350
632
|
response[:message] || response[:ack]
|
351
633
|
end
|
634
|
+
|
635
|
+
def date_to_iso(date)
|
636
|
+
(date.is_a?(Date) ? date.to_time : date).utc.iso8601
|
637
|
+
end
|
352
638
|
end
|
353
639
|
end
|
354
640
|
end
|