activemerchant 1.1.0 → 1.2.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.
- data.tar.gz.sig +0 -0
- data/CHANGELOG +226 -0
- data/CONTRIBUTERS +52 -0
- data/README +34 -24
- data/Rakefile +152 -0
- data/gem-public_cert.pem +20 -0
- data/init.rb +3 -0
- data/lib/active_merchant.rb +3 -1
- data/lib/active_merchant/billing/credit_card.rb +21 -17
- data/lib/active_merchant/billing/credit_card_methods.rb +17 -19
- data/lib/active_merchant/billing/gateway.rb +160 -44
- data/lib/active_merchant/billing/gateways.rb +2 -15
- data/lib/active_merchant/billing/gateways/authorize_net.rb +21 -21
- data/lib/active_merchant/billing/gateways/bogus.rb +6 -6
- data/lib/active_merchant/billing/gateways/brain_tree.rb +191 -0
- data/lib/active_merchant/billing/gateways/card_stream.rb +207 -0
- data/lib/active_merchant/billing/gateways/cyber_source.rb +402 -0
- data/lib/active_merchant/billing/gateways/data_cash.rb +41 -97
- data/lib/active_merchant/billing/gateways/efsnet.rb +256 -0
- data/lib/active_merchant/billing/gateways/eway.rb +77 -29
- data/lib/active_merchant/billing/gateways/exact.rb +230 -0
- data/lib/active_merchant/billing/gateways/linkpoint.rb +6 -33
- data/lib/active_merchant/billing/gateways/moneris.rb +155 -125
- data/lib/active_merchant/billing/gateways/net_registry.rb +257 -0
- data/lib/active_merchant/billing/gateways/pay_junction.rb +407 -0
- data/lib/active_merchant/billing/gateways/payflow.rb +163 -25
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +56 -38
- data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +10 -1
- data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +9 -0
- data/lib/active_merchant/billing/gateways/payflow_express.rb +36 -11
- data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +6 -0
- data/lib/active_merchant/billing/gateways/payflow_uk.rb +7 -3
- data/lib/active_merchant/billing/gateways/payment_express.rb +261 -0
- data/lib/active_merchant/billing/gateways/paypal.rb +18 -4
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +31 -15
- data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +8 -0
- data/lib/active_merchant/billing/gateways/paypal_express.rb +33 -8
- data/lib/active_merchant/billing/gateways/plugnpay.rb +300 -0
- data/lib/active_merchant/billing/gateways/protx.rb +285 -0
- data/lib/active_merchant/billing/gateways/psigate.rb +13 -12
- data/lib/active_merchant/billing/gateways/psl_card.rb +297 -0
- data/lib/active_merchant/billing/gateways/quickpay.rb +197 -0
- data/lib/active_merchant/billing/gateways/realex.rb +212 -0
- data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
- data/lib/active_merchant/billing/gateways/trans_first.rb +136 -0
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +43 -20
- data/lib/active_merchant/billing/gateways/usa_epay.rb +6 -5
- data/lib/active_merchant/billing/gateways/verifi.rb +235 -0
- data/lib/active_merchant/billing/gateways/viaklix.rb +171 -0
- data/lib/active_merchant/billing/integrations/gestpay/helper.rb +0 -2
- data/lib/active_merchant/billing/integrations/helper.rb +8 -1
- data/lib/active_merchant/billing/integrations/nochex.rb +62 -1
- data/lib/active_merchant/billing/integrations/nochex/notification.rb +9 -16
- data/lib/active_merchant/billing/integrations/notification.rb +1 -1
- data/lib/active_merchant/billing/integrations/paypal/helper.rb +59 -46
- data/lib/active_merchant/billing/integrations/paypal/notification.rb +14 -47
- 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 +23 -5
- data/lib/active_merchant/lib/requires_parameters.rb +2 -2
- data/lib/active_merchant/lib/validateable.rb +1 -1
- data/lib/support/gateway_support.rb +45 -0
- data/lib/tasks/cia.rb +1 -1
- data/script/generate +14 -0
- data/script/generator/base.rb +45 -0
- data/script/generator/generator.rb +24 -0
- data/script/generator/generators/gateway/gateway_generator.rb +14 -0
- data/script/generator/generators/gateway/templates/gateway.rb +73 -0
- data/script/generator/generators/gateway/templates/gateway_test.rb +41 -0
- data/script/generator/generators/gateway/templates/remote_gateway_test.rb +56 -0
- data/script/generator/generators/integration/integration_generator.rb +25 -0
- data/script/generator/generators/integration/templates/helper.rb +34 -0
- data/script/generator/generators/integration/templates/helper_test.rb +54 -0
- data/script/generator/generators/integration/templates/integration.rb +18 -0
- data/script/generator/generators/integration/templates/module_test.rb +9 -0
- data/script/generator/generators/integration/templates/notification.rb +100 -0
- data/script/generator/generators/integration/templates/notification_test.rb +41 -0
- data/script/generator/manifest.rb +20 -0
- data/test/extra/binding_of_caller.rb +80 -0
- data/test/extra/breakpoint.rb +547 -0
- data/test/fixtures.yml +251 -0
- data/test/remote_tests/remote_authorize_net_test.rb +113 -0
- data/test/remote_tests/remote_brain_tree_test.rb +78 -0
- data/test/remote_tests/remote_card_stream_test.rb +160 -0
- data/test/remote_tests/remote_cyber_source_test.rb +130 -0
- data/test/remote_tests/remote_data_cash_test.rb +155 -0
- data/test/remote_tests/remote_efsnet_test.rb +93 -0
- data/test/remote_tests/remote_eway_test.rb +71 -0
- data/test/remote_tests/remote_exact_test.rb +59 -0
- data/test/remote_tests/remote_gestpay_integration_test.rb +37 -0
- data/test/remote_tests/remote_linkpoint_test.rb +144 -0
- data/test/remote_tests/remote_moneris_test.rb +110 -0
- data/test/remote_tests/remote_net_registry_test.rb +120 -0
- data/test/remote_tests/remote_pay_junction_test.rb +162 -0
- data/test/remote_tests/remote_payflow_express_test.rb +50 -0
- data/test/remote_tests/remote_payflow_test.rb +241 -0
- data/test/remote_tests/remote_payflow_uk_test.rb +172 -0
- data/test/remote_tests/remote_payment_express_test.rb +136 -0
- data/test/remote_tests/remote_paypal_express_test.rb +49 -0
- data/test/remote_tests/remote_paypal_integration_test.rb +14 -0
- data/test/remote_tests/remote_paypal_test.rb +163 -0
- data/test/remote_tests/remote_plugnpay_test.rb +70 -0
- data/test/remote_tests/remote_protx_test.rb +184 -0
- data/test/remote_tests/remote_psigate_test.rb +87 -0
- data/test/remote_tests/remote_psl_card_test.rb +105 -0
- data/test/remote_tests/remote_quickpay_test.rb +182 -0
- data/test/remote_tests/remote_realex_test.rb +227 -0
- data/test/remote_tests/remote_secure_pay_test.rb +36 -0
- data/test/remote_tests/remote_trans_first_test.rb +37 -0
- data/test/remote_tests/remote_trust_commerce_test.rb +136 -0
- data/test/remote_tests/remote_usa_epay_test.rb +57 -0
- data/test/remote_tests/remote_verifi_test.rb +107 -0
- data/test/remote_tests/remote_viaklix_test.rb +53 -0
- data/test/test_helper.rb +132 -0
- data/test/unit/base_test.rb +61 -0
- data/test/unit/country_code_test.rb +33 -0
- data/test/unit/country_test.rb +64 -0
- data/test/unit/credit_card_formatting_test.rb +24 -0
- data/test/unit/credit_card_methods_test.rb +37 -0
- data/test/unit/credit_card_test.rb +365 -0
- data/test/unit/gateways/authorize_net_test.rb +140 -0
- data/test/unit/gateways/bogus_test.rb +43 -0
- data/test/unit/gateways/brain_tree_test.rb +77 -0
- data/test/unit/gateways/card_stream_test.rb +37 -0
- data/test/unit/gateways/cyber_source_test.rb +151 -0
- data/test/unit/gateways/data_cash_test.rb +23 -0
- data/test/unit/gateways/efsnet_test.rb +70 -0
- data/test/unit/gateways/eway_test.rb +105 -0
- data/test/unit/gateways/exact_test.rb +118 -0
- data/test/unit/gateways/gateway_test.rb +24 -0
- data/test/unit/gateways/linkpoint_test.rb +165 -0
- data/test/unit/gateways/moneris_test.rb +167 -0
- data/test/unit/gateways/net_registry_test.rb +478 -0
- data/test/unit/gateways/pay_junction_test.rb +61 -0
- data/test/unit/gateways/payflow_express_test.rb +165 -0
- data/test/unit/gateways/payflow_express_uk_test.rb +14 -0
- data/test/unit/gateways/payflow_test.rb +230 -0
- data/test/unit/gateways/payflow_uk_test.rb +68 -0
- data/test/unit/gateways/payment_express_test.rb +215 -0
- data/test/unit/gateways/paypal_express_test.rb +222 -0
- data/test/unit/gateways/paypal_test.rb +241 -0
- data/test/unit/gateways/plugnpay_test.rb +79 -0
- data/test/unit/gateways/protx_test.rb +110 -0
- data/test/unit/gateways/psigate_test.rb +110 -0
- data/test/unit/gateways/psl_card_test.rb +51 -0
- data/test/unit/gateways/quickpay_test.rb +125 -0
- data/test/unit/gateways/realex_test.rb +150 -0
- data/test/unit/gateways/secure_pay_test.rb +78 -0
- data/test/unit/gateways/trans_first_test.rb +125 -0
- data/test/unit/gateways/trust_commerce_test.rb +57 -0
- data/test/unit/gateways/usa_epay_test.rb +117 -0
- data/test/unit/gateways/verifi_test.rb +91 -0
- data/test/unit/gateways/viaklix_test.rb +72 -0
- data/test/unit/integrations/action_view_helper_test.rb +54 -0
- data/test/unit/integrations/bogus_module_test.rb +16 -0
- data/test/unit/integrations/chronopay_module_test.rb +9 -0
- data/test/unit/integrations/gestpay_module_test.rb +10 -0
- data/test/unit/integrations/helpers/bogus_helper_test.rb +28 -0
- data/test/unit/integrations/helpers/chronopay_helper_test.rb +67 -0
- data/test/unit/integrations/helpers/gestpay_helper_test.rb +100 -0
- data/test/unit/integrations/helpers/nochex_helper_test.rb +53 -0
- data/test/unit/integrations/helpers/paypal_helper_test.rb +162 -0
- data/test/unit/integrations/helpers/two_checkout_helper_test.rb +92 -0
- data/test/unit/integrations/nochex_module_test.rb +9 -0
- data/test/unit/integrations/notifications/chronopay_notification_test.rb +66 -0
- data/test/unit/integrations/notifications/gestpay_notification_test.rb +60 -0
- data/test/unit/integrations/notifications/nochex_notification_test.rb +51 -0
- data/test/unit/integrations/notifications/notification_test.rb +41 -0
- data/test/unit/integrations/notifications/paypal_notification_test.rb +85 -0
- data/test/unit/integrations/notifications/two_checkout_notification_test.rb +55 -0
- data/test/unit/integrations/paypal_module_test.rb +24 -0
- data/test/unit/integrations/two_checkout_module_test.rb +9 -0
- data/test/unit/post_data_test.rb +55 -0
- data/test/unit/response_test.rb +14 -0
- data/test/unit/validateable_test.rb +56 -0
- metadata +160 -7
- metadata.gz.sig +0 -0
- data/lib/active_merchant/billing/gateways/payflow/f73e89fd.0 +0 -17
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
require File.dirname(__FILE__) + '/paypal/paypal_common_api'
|
|
2
|
+
require File.dirname(__FILE__) + '/paypal_express'
|
|
2
3
|
|
|
3
4
|
module ActiveMerchant #:nodoc:
|
|
4
5
|
module Billing #:nodoc:
|
|
5
6
|
class PaypalGateway < Gateway
|
|
6
7
|
include PaypalCommonAPI
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
|
10
|
+
self.supported_countries = ['US']
|
|
11
|
+
|
|
12
|
+
self.homepage_url = 'https://www.paypal.com/cgi-bin/webscr?cmd=_wp-pro-overview-outside'
|
|
13
|
+
self.display_name = 'PayPal Website Payments Pro (US)'
|
|
11
14
|
|
|
12
15
|
def authorize(money, credit_card, options = {})
|
|
13
16
|
requires!(options, :ip)
|
|
@@ -36,6 +39,7 @@ module ActiveMerchant #:nodoc:
|
|
|
36
39
|
private
|
|
37
40
|
def build_sale_or_authorization_request(action, money, credit_card, options)
|
|
38
41
|
shipping_address = options[:shipping_address] || options[:address]
|
|
42
|
+
currency_code = options[:currency] || currency(money)
|
|
39
43
|
|
|
40
44
|
xml = Builder::XmlMarkup.new :indent => 2
|
|
41
45
|
xml.tag! 'DoDirectPaymentReq', 'xmlns' => PAYPAL_NAMESPACE do
|
|
@@ -44,10 +48,20 @@ module ActiveMerchant #:nodoc:
|
|
|
44
48
|
xml.tag! 'n2:DoDirectPaymentRequestDetails' do
|
|
45
49
|
xml.tag! 'n2:PaymentAction', action
|
|
46
50
|
xml.tag! 'n2:PaymentDetails' do
|
|
47
|
-
xml.tag! 'n2:OrderTotal', amount(money), 'currencyID' =>
|
|
51
|
+
xml.tag! 'n2:OrderTotal', amount(money), 'currencyID' => currency_code
|
|
52
|
+
|
|
53
|
+
# All of the values must be included together and add up to the order total
|
|
54
|
+
if [:subtotal, :shipping, :handling, :tax].all?{ |o| options.has_key?(o) }
|
|
55
|
+
xml.tag! 'n2:ItemTotal', amount(options[:subtotal]), 'currencyID' => currency_code
|
|
56
|
+
xml.tag! 'n2:ShippingTotal', amount(options[:shipping]),'currencyID' => currency_code
|
|
57
|
+
xml.tag! 'n2:HandlingTotal', amount(options[:handling]),'currencyID' => currency_code
|
|
58
|
+
xml.tag! 'n2:TaxTotal', amount(options[:tax]), 'currencyID' => currency_code
|
|
59
|
+
end
|
|
60
|
+
|
|
48
61
|
xml.tag! 'n2:NotifyURL', options[:notify_url]
|
|
49
62
|
xml.tag! 'n2:OrderDescription', options[:description]
|
|
50
63
|
xml.tag! 'n2:InvoiceID', options[:order_id]
|
|
64
|
+
xml.tag! 'n2:ButtonSource', application_id.to_s.slice(0,32) unless application_id.blank?
|
|
51
65
|
|
|
52
66
|
add_address(xml, 'n2:ShipToAddress', shipping_address)
|
|
53
67
|
end
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
module ActiveMerchant #:nodoc:
|
|
2
2
|
module Billing #:nodoc:
|
|
3
|
-
# This class is
|
|
4
|
-
# PaypalExpressGateway
|
|
3
|
+
# This class is included in both PaypalGateway and PaypalExpressGateway
|
|
5
4
|
module PaypalCommonAPI
|
|
6
5
|
def self.included(base)
|
|
6
|
+
|
|
7
|
+
base.default_currency = 'USD'
|
|
7
8
|
base.cattr_accessor :pem_file
|
|
9
|
+
base.cattr_accessor :signature
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
API_VERSION = '2.0'
|
|
@@ -33,18 +35,29 @@ module ActiveMerchant #:nodoc:
|
|
|
33
35
|
'VIC' => 'Victoria',
|
|
34
36
|
'WA' => 'Western Australia'
|
|
35
37
|
}
|
|
36
|
-
|
|
38
|
+
|
|
39
|
+
# The gateway must be configured with either your PayPal PEM file
|
|
40
|
+
# or your PayPal API Signature. Only one is required.
|
|
41
|
+
#
|
|
37
42
|
# <tt>:pem</tt> The text of your PayPal PEM file. Note
|
|
38
43
|
# this is not the path to file, but its
|
|
39
44
|
# contents. If you are only using one PEM
|
|
40
45
|
# file on your site you can declare it
|
|
41
46
|
# globally and then you won't need to
|
|
42
47
|
# include this option
|
|
48
|
+
#
|
|
49
|
+
# <tt>:signature</tt> The text of your PayPal signature.
|
|
50
|
+
# If you are only using one API Signature
|
|
51
|
+
# on your site you can declare it
|
|
52
|
+
# globally and then you won't need to
|
|
53
|
+
# include this option
|
|
54
|
+
|
|
43
55
|
def initialize(options = {})
|
|
44
56
|
requires!(options, :login, :password)
|
|
45
57
|
|
|
46
58
|
@options = {
|
|
47
|
-
:pem =>
|
|
59
|
+
:pem => pem_file,
|
|
60
|
+
:signature => signature
|
|
48
61
|
}.update(options)
|
|
49
62
|
|
|
50
63
|
super
|
|
@@ -92,7 +105,7 @@ module ActiveMerchant #:nodoc:
|
|
|
92
105
|
xml.tag! 'DoReauthorizationRequest', 'xmlns:n2' => EBAY_NAMESPACE do
|
|
93
106
|
xml.tag! 'n2:Version', API_VERSION
|
|
94
107
|
xml.tag! 'AuthorizationID', authorization
|
|
95
|
-
xml.tag! 'Amount', amount(money), 'currencyID' => currency(money)
|
|
108
|
+
xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
|
|
96
109
|
end
|
|
97
110
|
end
|
|
98
111
|
|
|
@@ -106,7 +119,7 @@ module ActiveMerchant #:nodoc:
|
|
|
106
119
|
xml.tag! 'DoCaptureRequest', 'xmlns:n2' => EBAY_NAMESPACE do
|
|
107
120
|
xml.tag! 'n2:Version', API_VERSION
|
|
108
121
|
xml.tag! 'AuthorizationID', authorization
|
|
109
|
-
xml.tag! 'Amount', amount(money), 'currencyID' => currency(money)
|
|
122
|
+
xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
|
|
110
123
|
xml.tag! 'CompleteType', 'Complete'
|
|
111
124
|
xml.tag! 'Note', options[:description]
|
|
112
125
|
end
|
|
@@ -122,7 +135,7 @@ module ActiveMerchant #:nodoc:
|
|
|
122
135
|
xml.tag! 'RefundTransactionRequest', 'xmlns:n2' => EBAY_NAMESPACE do
|
|
123
136
|
xml.tag! 'n2:Version', API_VERSION
|
|
124
137
|
xml.tag! 'TransactionID', identification
|
|
125
|
-
xml.tag! 'Amount', amount(money), 'currencyID' => currency(money)
|
|
138
|
+
xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
|
|
126
139
|
xml.tag! 'RefundType', 'Partial'
|
|
127
140
|
xml.tag! 'Memo', options[:note] unless options[:note].blank?
|
|
128
141
|
end
|
|
@@ -159,7 +172,7 @@ module ActiveMerchant #:nodoc:
|
|
|
159
172
|
options ||= default_options
|
|
160
173
|
xml.tag! 'MassPayItem' do
|
|
161
174
|
xml.tag! 'ReceiverEmail', recipient
|
|
162
|
-
xml.tag! 'Amount', amount(money), 'currencyID' => currency(money)
|
|
175
|
+
xml.tag! 'Amount', amount(money), 'currencyID' => options[:currency] || currency(money)
|
|
163
176
|
xml.tag! 'Note', options[:note] if options[:note]
|
|
164
177
|
xml.tag! 'UniqueId', options[:unique_id] if options[:unique_id]
|
|
165
178
|
end
|
|
@@ -234,6 +247,7 @@ module ActiveMerchant #:nodoc:
|
|
|
234
247
|
xml.tag! 'Username', @options[:login]
|
|
235
248
|
xml.tag! 'Password', @options[:password]
|
|
236
249
|
xml.tag! 'Subject', @options[:subject]
|
|
250
|
+
xml.tag! 'Signature', @options[:signature] unless @options[:signature].blank?
|
|
237
251
|
end
|
|
238
252
|
end
|
|
239
253
|
end
|
|
@@ -253,21 +267,23 @@ module ActiveMerchant #:nodoc:
|
|
|
253
267
|
end
|
|
254
268
|
|
|
255
269
|
def lookup_state(address)
|
|
256
|
-
|
|
270
|
+
country = Country.find(address[:country]) rescue nil
|
|
271
|
+
return '' if country.nil?
|
|
272
|
+
|
|
273
|
+
case country.code(:alpha2).to_s
|
|
274
|
+
when 'AU'
|
|
257
275
|
AUSTRALIAN_STATES[address[:state]] || address[:state]
|
|
276
|
+
when 'GB'
|
|
277
|
+
address[:state].blank? ? 'N/A' : address[:state]
|
|
258
278
|
else
|
|
259
279
|
address[:state]
|
|
260
280
|
end
|
|
261
281
|
end
|
|
262
282
|
|
|
263
|
-
def currency(money)
|
|
264
|
-
money.respond_to?(:currency) ? money.currency : 'USD'
|
|
265
|
-
end
|
|
266
|
-
|
|
267
283
|
def commit(action, request)
|
|
268
284
|
url = test? ? TEST_URL : LIVE_URL
|
|
285
|
+
|
|
269
286
|
data = ssl_post(url, build_request(request))
|
|
270
|
-
|
|
271
287
|
@response = parse(action, data)
|
|
272
288
|
|
|
273
289
|
success = @response[:ack] == "Success"
|
|
@@ -280,4 +296,4 @@ module ActiveMerchant #:nodoc:
|
|
|
280
296
|
end
|
|
281
297
|
end
|
|
282
298
|
end
|
|
283
|
-
end
|
|
299
|
+
end
|
|
@@ -5,6 +5,10 @@ module ActiveMerchant #:nodoc:
|
|
|
5
5
|
@params['payer']
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
+
def name
|
|
9
|
+
[@params['first_name'], @params['middle_name'], @params['last_name']].compact.join(' ')
|
|
10
|
+
end
|
|
11
|
+
|
|
8
12
|
def token
|
|
9
13
|
@params['token']
|
|
10
14
|
end
|
|
@@ -13,6 +17,10 @@ module ActiveMerchant #:nodoc:
|
|
|
13
17
|
@params['payer_id']
|
|
14
18
|
end
|
|
15
19
|
|
|
20
|
+
def payer_country
|
|
21
|
+
@params['payer_country']
|
|
22
|
+
end
|
|
23
|
+
|
|
16
24
|
def address
|
|
17
25
|
{ 'name' => @params['name'],
|
|
18
26
|
'company' => @params['payer_business'],
|
|
@@ -6,8 +6,13 @@ module ActiveMerchant #:nodoc:
|
|
|
6
6
|
class PaypalExpressGateway < Gateway
|
|
7
7
|
include PaypalCommonAPI
|
|
8
8
|
|
|
9
|
+
self.supported_countries = ['US']
|
|
10
|
+
|
|
9
11
|
LIVE_REDIRECT_URL = 'https://www.paypal.com/cgibin/webscr?cmd=_express-checkout&token='
|
|
10
12
|
TEST_REDIRECT_URL = 'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token='
|
|
13
|
+
|
|
14
|
+
self.homepage_url = 'https://www.paypal.com/cgi-bin/webscr?cmd=xpt/merchant/ExpressCheckoutIntro-outside'
|
|
15
|
+
self.display_name = 'PayPal Express Checkout'
|
|
11
16
|
|
|
12
17
|
def redirect_url
|
|
13
18
|
test? ? TEST_REDIRECT_URL : LIVE_REDIRECT_URL
|
|
@@ -39,7 +44,7 @@ module ActiveMerchant #:nodoc:
|
|
|
39
44
|
commit 'DoExpressCheckoutPayment', build_sale_or_authorization_request('Authorization', money, options)
|
|
40
45
|
end
|
|
41
46
|
|
|
42
|
-
def purchase(money,
|
|
47
|
+
def purchase(money, options = {})
|
|
43
48
|
requires!(options, :token, :payer_id)
|
|
44
49
|
|
|
45
50
|
commit 'DoExpressCheckoutPayment', build_sale_or_authorization_request('Sale', money, options)
|
|
@@ -59,6 +64,8 @@ module ActiveMerchant #:nodoc:
|
|
|
59
64
|
end
|
|
60
65
|
|
|
61
66
|
def build_sale_or_authorization_request(action, money, options)
|
|
67
|
+
currency_code = options[:currency] || currency(money)
|
|
68
|
+
|
|
62
69
|
xml = Builder::XmlMarkup.new :indent => 2
|
|
63
70
|
xml.tag! 'DoExpressCheckoutPaymentReq', 'xmlns' => PAYPAL_NAMESPACE do
|
|
64
71
|
xml.tag! 'DoExpressCheckoutPaymentRequest', 'xmlns:n2' => EBAY_NAMESPACE do
|
|
@@ -68,8 +75,18 @@ module ActiveMerchant #:nodoc:
|
|
|
68
75
|
xml.tag! 'n2:Token', options[:token]
|
|
69
76
|
xml.tag! 'n2:PayerID', options[:payer_id]
|
|
70
77
|
xml.tag! 'n2:PaymentDetails' do
|
|
71
|
-
xml.tag! 'n2:OrderTotal', amount(money), 'currencyID' =>
|
|
78
|
+
xml.tag! 'n2:OrderTotal', amount(money), 'currencyID' => currency_code
|
|
79
|
+
|
|
80
|
+
# All of the values must be included together and add up to the order total
|
|
81
|
+
if [:subtotal, :shipping, :handling, :tax].all?{ |o| options.has_key?(o) }
|
|
82
|
+
xml.tag! 'n2:ItemTotal', amount(options[:subtotal]), 'currencyID' => currency_code
|
|
83
|
+
xml.tag! 'n2:ShippingTotal', amount(options[:shipping]),'currencyID' => currency_code
|
|
84
|
+
xml.tag! 'n2:HandlingTotal', amount(options[:handling]),'currencyID' => currency_code
|
|
85
|
+
xml.tag! 'n2:TaxTotal', amount(options[:tax]), 'currencyID' => currency_code
|
|
86
|
+
end
|
|
87
|
+
|
|
72
88
|
xml.tag! 'n2:NotifyURL', options[:notify_url]
|
|
89
|
+
xml.tag! 'n2:ButtonSource', application_id.to_s.slice(0,32) unless application_id.blank?
|
|
73
90
|
end
|
|
74
91
|
end
|
|
75
92
|
end
|
|
@@ -85,12 +102,13 @@ module ActiveMerchant #:nodoc:
|
|
|
85
102
|
xml.tag! 'n2:Version', API_VERSION
|
|
86
103
|
xml.tag! 'n2:SetExpressCheckoutRequestDetails' do
|
|
87
104
|
xml.tag! 'n2:PaymentAction', action
|
|
88
|
-
xml.tag! 'n2:OrderTotal', amount(money), 'currencyID' => currency(money)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
xml.tag! 'n2:
|
|
105
|
+
xml.tag! 'n2:OrderTotal', amount(money).to_f.zero? ? amount(100) : amount(money), 'currencyID' => options[:currency] || currency(money)
|
|
106
|
+
if options[:max_amount]
|
|
107
|
+
xml.tag! 'n2:MaxAmount', amount(options[:max_amount]), 'currencyID' => options[:currency] || currency(options[:max_amount])
|
|
108
|
+
end
|
|
109
|
+
add_address(xml, 'n2:Address', options[:shipping_address] || options[:address])
|
|
110
|
+
xml.tag! 'n2:AddressOverride', options[:address_override] ? '1' : '0'
|
|
111
|
+
xml.tag! 'n2:NoShipping', options[:no_shipping] ? '1' : '0'
|
|
94
112
|
xml.tag! 'n2:ReturnURL', options[:return_url]
|
|
95
113
|
xml.tag! 'n2:CancelURL', options[:cancel_return_url]
|
|
96
114
|
xml.tag! 'n2:IPAddress', options[:ip]
|
|
@@ -98,6 +116,13 @@ module ActiveMerchant #:nodoc:
|
|
|
98
116
|
xml.tag! 'n2:BuyerEmail', options[:email] unless options[:email].blank?
|
|
99
117
|
xml.tag! 'n2:InvoiceID', options[:order_id]
|
|
100
118
|
|
|
119
|
+
# Customization of the payment page
|
|
120
|
+
xml.tag! 'n2:PageStyle', options[:page_style] unless options[:page_style].blank?
|
|
121
|
+
xml.tag! 'n2:cpp-image-header', options[:header_image] unless options[:header_image].blank?
|
|
122
|
+
xml.tag! 'n2:cpp-header-back-color', options[:header_background_color] unless options[:header_background_color].blank?
|
|
123
|
+
xml.tag! 'n2:cpp-header-border-color', options[:header_border_color] unless options[:header_border_color].blank?
|
|
124
|
+
xml.tag! 'n2:cpp-payflow-color', options[:background_color] unless options[:background_color].blank?
|
|
125
|
+
|
|
101
126
|
# This should be set to the same locale as the shop
|
|
102
127
|
# localeCode - String
|
|
103
128
|
end
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
module ActiveMerchant
|
|
2
|
+
module Billing
|
|
3
|
+
|
|
4
|
+
class PlugnpayGateway < Gateway
|
|
5
|
+
class PlugnpayPostData < PostData
|
|
6
|
+
# Fields that will be sent even if they are blank
|
|
7
|
+
self.required_fields = [ :publisher_name, :publisher_password,
|
|
8
|
+
:card_amount, :card_name, :card_number, :card_exp, :orderID ]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
URL = 'https://pay1.plugnpay.com/payment/pnpremote.cgi'
|
|
12
|
+
|
|
13
|
+
CARD_CODE_MESSAGES = {
|
|
14
|
+
"M" => "Card verification number matched",
|
|
15
|
+
"N" => "Card verification number didn't match",
|
|
16
|
+
"P" => "Card verification number was not processed",
|
|
17
|
+
"S" => "Card verification number should be on card but was not indicated",
|
|
18
|
+
"U" => "Issuer was not certified for card verification"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
CARD_CODE_ERRORS = %w( N S )
|
|
22
|
+
|
|
23
|
+
AVS_MESSAGES = {
|
|
24
|
+
"A" => "Street address matches billing information, zip/postal code does not",
|
|
25
|
+
"B" => "Address information not provided for address verification check",
|
|
26
|
+
"E" => "Address verification service error",
|
|
27
|
+
"G" => "Non-U.S. card-issuing bank",
|
|
28
|
+
"N" => "Neither street address nor zip/postal match billing information",
|
|
29
|
+
"P" => "Address verification not applicable for this transaction",
|
|
30
|
+
"R" => "Payment gateway was unavailable or timed out",
|
|
31
|
+
"S" => "Address verification service not supported by issuer",
|
|
32
|
+
"U" => "Address information is unavailable",
|
|
33
|
+
"W" => "9-digit zip/postal code matches billing information, street address does not",
|
|
34
|
+
"X" => "Street address and 9-digit zip/postal code matches billing information",
|
|
35
|
+
"Y" => "Street address and 5-digit zip/postal code matches billing information",
|
|
36
|
+
"Z" => "5-digit zip/postal code matches billing information, street address does not",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
AVS_ERRORS = %w( A E N R W Z )
|
|
40
|
+
|
|
41
|
+
PAYMENT_GATEWAY_RESPONSES = {
|
|
42
|
+
"P01" => "AVS Mismatch Failure",
|
|
43
|
+
"P02" => "CVV2 Mismatch Failure",
|
|
44
|
+
"P21" => "Transaction may not be marked",
|
|
45
|
+
"P30" => "Test Tran. Bad Card",
|
|
46
|
+
"P35" => "Test Tran. Problem",
|
|
47
|
+
"P40" => "Username already exists",
|
|
48
|
+
"P41" => "Username is blank",
|
|
49
|
+
"P50" => "Fraud Screen Failure",
|
|
50
|
+
"P51" => "Missing PIN Code",
|
|
51
|
+
"P52" => "Invalid Bank Acct. No.",
|
|
52
|
+
"P53" => "Invalid Bank Routing No.",
|
|
53
|
+
"P54" => "Invalid/Missing Check No.",
|
|
54
|
+
"P55" => "Invalid Credit Card No.",
|
|
55
|
+
"P56" => "Invalid CVV2/CVC2 No.",
|
|
56
|
+
"P57" => "Expired. CC Exp. Date",
|
|
57
|
+
"P58" => "Missing Data",
|
|
58
|
+
"P59" => "Missing Email Address",
|
|
59
|
+
"P60" => "Zip Code does not match Billing State.",
|
|
60
|
+
"P61" => "Invalid Billing Zip Code",
|
|
61
|
+
"P62" => "Zip Code does not match Shipping State.",
|
|
62
|
+
"P63" => "Invalid Shipping Zip Code",
|
|
63
|
+
"P64" => "Invalid Credit Card CVV2/CVC2 Format.",
|
|
64
|
+
"P65" => "Maximum number of attempts has been exceeded.",
|
|
65
|
+
"P66" => "Credit Card number has been flagged and can not be used to access this service.",
|
|
66
|
+
"P67" => "IP Address is on Blocked List.",
|
|
67
|
+
"P68" => "Billing country does not match ipaddress country.",
|
|
68
|
+
"P69" => "US based ipaddresses are currently blocked.",
|
|
69
|
+
"P70" => "Credit Cards issued from this bank are currently not being accepted.",
|
|
70
|
+
"P71" => "Credit Cards issued from this bank are currently not being accepted.",
|
|
71
|
+
"P72" => "Daily volume exceeded.",
|
|
72
|
+
"P73" => "Too many transactions within allotted time.",
|
|
73
|
+
"P91" => "Missing/incorrect password",
|
|
74
|
+
"P92" => "Account not configured for mobil administration",
|
|
75
|
+
"P93" => "IP Not registered to username.",
|
|
76
|
+
"P94" => "Mode not permitted for this account.",
|
|
77
|
+
"P95" => "Currently Blank",
|
|
78
|
+
"P96" => "Currently Blank",
|
|
79
|
+
"P97" => "Processor not responding",
|
|
80
|
+
"P98" => "Missing merchant/publisher name",
|
|
81
|
+
"P99" => "Currently Blank"
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
TRANSACTIONS = {
|
|
85
|
+
:authorization => 'auth',
|
|
86
|
+
:purchase => 'auth',
|
|
87
|
+
:capture => 'mark',
|
|
88
|
+
:void => 'void',
|
|
89
|
+
:refund => 'return',
|
|
90
|
+
:credit => 'newreturn'
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
SUCCESS_CODES = [ 'pending', 'success' ]
|
|
94
|
+
FAILURE_CODES = [ 'badcard', 'fraud' ]
|
|
95
|
+
|
|
96
|
+
# URL
|
|
97
|
+
attr_reader :response
|
|
98
|
+
attr_reader :options
|
|
99
|
+
|
|
100
|
+
self.default_currency = 'USD'
|
|
101
|
+
self.supported_countries = ['US']
|
|
102
|
+
self.supported_cardtypes = [:visa, :master, :american_express, :discover]
|
|
103
|
+
self.homepage_url = 'http://www.plugnpay.com/'
|
|
104
|
+
self.display_name = "Plug'n Pay"
|
|
105
|
+
|
|
106
|
+
def initialize(options = {})
|
|
107
|
+
requires!(options, :login, :password)
|
|
108
|
+
@options = options
|
|
109
|
+
super
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def purchase(money, creditcard, options = {})
|
|
113
|
+
post = PlugnpayPostData.new
|
|
114
|
+
|
|
115
|
+
add_amount(post, money, options)
|
|
116
|
+
add_creditcard(post, creditcard)
|
|
117
|
+
add_addresses(post, options)
|
|
118
|
+
add_invoice_data(post, options)
|
|
119
|
+
add_customer_data(post, options)
|
|
120
|
+
|
|
121
|
+
post[:authtype] = 'authpostauth'
|
|
122
|
+
commit(:authorization, post)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def authorize(money, creditcard, options = {})
|
|
126
|
+
post = PlugnpayPostData.new
|
|
127
|
+
|
|
128
|
+
add_amount(post, money, options)
|
|
129
|
+
add_creditcard(post, creditcard)
|
|
130
|
+
add_addresses(post, options)
|
|
131
|
+
add_invoice_data(post, options)
|
|
132
|
+
add_customer_data(post, options)
|
|
133
|
+
|
|
134
|
+
post[:authtype] = 'authonly'
|
|
135
|
+
commit(:authorization, post)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def capture(money, authorization, options = {})
|
|
139
|
+
post = PlugnpayPostData.new
|
|
140
|
+
|
|
141
|
+
post[:orderID] = authorization
|
|
142
|
+
|
|
143
|
+
add_amount(post, money, options)
|
|
144
|
+
add_customer_data(post, options)
|
|
145
|
+
|
|
146
|
+
commit(:capture, post)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def void(authorization, options = {})
|
|
150
|
+
post = PlugnpayPostData.new
|
|
151
|
+
|
|
152
|
+
post[:orderID] = authorization
|
|
153
|
+
post[:txn_type] = 'auth'
|
|
154
|
+
|
|
155
|
+
commit(:void, post)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def credit(money, identification_or_creditcard, options = {})
|
|
159
|
+
post = PlugnpayPostData.new
|
|
160
|
+
add_amount(post, money, options)
|
|
161
|
+
|
|
162
|
+
if identification_or_creditcard.is_a?(String)
|
|
163
|
+
post[:orderID] = identification_or_creditcard
|
|
164
|
+
|
|
165
|
+
commit(:refund, post)
|
|
166
|
+
else
|
|
167
|
+
add_creditcard(post, identification_or_creditcard)
|
|
168
|
+
add_addresses(post, options)
|
|
169
|
+
add_customer_data(post, options)
|
|
170
|
+
|
|
171
|
+
commit(:credit, post)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
private
|
|
176
|
+
def commit(action, post)
|
|
177
|
+
if result = test_result_from_cc_number(post[:card_number])
|
|
178
|
+
return result
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
data = ssl_post(URL, post_data(action, post))
|
|
182
|
+
|
|
183
|
+
@response = parse(data)
|
|
184
|
+
|
|
185
|
+
success = SUCCESS_CODES.include?(@response[:finalstatus])
|
|
186
|
+
message = success ? 'Success' : message_from(@response)
|
|
187
|
+
|
|
188
|
+
Response.new(success, message, @response,
|
|
189
|
+
:test => test?,
|
|
190
|
+
:authorization => @response[:orderid]
|
|
191
|
+
)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def parse(body)
|
|
195
|
+
body = CGI.unescape(body)
|
|
196
|
+
results = {}
|
|
197
|
+
body.split('&').collect { |e| e.split('=') }.each do |key,value|
|
|
198
|
+
results[key.downcase.to_sym] = normalize(value.to_s.strip)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
results.delete(:publisher_password)
|
|
202
|
+
results[:avs_message] = AVS_MESSAGES[results[:avs_code]] if results[:avs_code]
|
|
203
|
+
results[:card_code_message] = CARD_CODE_MESSAGES[results[:cvvresp]] if results[:cvvresp]
|
|
204
|
+
|
|
205
|
+
results
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def post_data(action, post)
|
|
209
|
+
post[:mode] = TRANSACTIONS[action]
|
|
210
|
+
post[:convert] = 'underscores'
|
|
211
|
+
post[:app_level] = 0
|
|
212
|
+
post[:publisher_name] = @options[:login]
|
|
213
|
+
post[:publisher_password] = @options[:password]
|
|
214
|
+
|
|
215
|
+
post.to_s
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def add_creditcard(post, creditcard)
|
|
219
|
+
post[:card_number] = creditcard.number
|
|
220
|
+
post[:card_cvv] = creditcard.verification_value
|
|
221
|
+
post[:card_exp] = expdate(creditcard)
|
|
222
|
+
post[:card_name] = creditcard.name.slice(0..38)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def add_customer_data(post, options)
|
|
226
|
+
post[:email] = options[:email]
|
|
227
|
+
post[:dontsndmail] = 'yes' unless options[:send_email_confirmation]
|
|
228
|
+
post[:ipaddress] = options[:ip]
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def add_invoice_data(post, options)
|
|
232
|
+
post[:shipping] = amount(options[:shipping]) unless options[:shipping].blank?
|
|
233
|
+
post[:tax] = amount(options[:tax]) unless options[:tax].blank?
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def add_addresses(post, options)
|
|
237
|
+
if address = options[:billing_address] || options[:address]
|
|
238
|
+
post[:card_address1] = address[:address1]
|
|
239
|
+
post[:card_zip] = address[:zip]
|
|
240
|
+
post[:card_city] = address[:city]
|
|
241
|
+
post[:card_country] = address[:country]
|
|
242
|
+
post[:phone] = address[:phone]
|
|
243
|
+
|
|
244
|
+
case address[:country]
|
|
245
|
+
when 'US', 'CA'
|
|
246
|
+
post[:card_state] = address[:state]
|
|
247
|
+
else
|
|
248
|
+
post[:card_state] = 'ZZ'
|
|
249
|
+
post[:card_prov] = address[:state]
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
if shipping_address = options[:shipping_address] || address
|
|
254
|
+
post[:shipname] = shipping_address[:name]
|
|
255
|
+
post[:address1] = shipping_address[:address1]
|
|
256
|
+
post[:address2] = shipping_address[:address2]
|
|
257
|
+
post[:city] = shipping_address[:city]
|
|
258
|
+
|
|
259
|
+
case shipping_address[:country]
|
|
260
|
+
when 'US', 'CA'
|
|
261
|
+
post[:state] = shipping_address[:state]
|
|
262
|
+
else
|
|
263
|
+
post[:state] = 'ZZ'
|
|
264
|
+
post[:province] = shipping_address[:state]
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
post[:country] = shipping_address[:country]
|
|
268
|
+
post[:zip] = shipping_address[:zip]
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def add_amount(post, money, options)
|
|
273
|
+
post[:card_amount] = amount(money)
|
|
274
|
+
post[:currency] = options[:currency] || currency(money)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Make a ruby type out of the response string
|
|
278
|
+
def normalize(field)
|
|
279
|
+
case field
|
|
280
|
+
when "true" then true
|
|
281
|
+
when "false" then false
|
|
282
|
+
when "" then nil
|
|
283
|
+
when "null" then nil
|
|
284
|
+
else field
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def message_from(results)
|
|
289
|
+
PAYMENT_GATEWAY_RESPONSES[results[:resp_code]]
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def expdate(creditcard)
|
|
293
|
+
year = sprintf("%.4i", creditcard.year)
|
|
294
|
+
month = sprintf("%.2i", creditcard.month)
|
|
295
|
+
|
|
296
|
+
"#{month}/#{year[-2..-1]}"
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
end
|