solidus_paypal_braintree 0.4.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +40 -0
- data/.gem_release.yml +5 -0
- data/.github/stale.yml +17 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.rubocop.yml +68 -0
- data/CHANGELOG.md +258 -0
- data/Gemfile +43 -0
- data/LICENSE +2 -2
- data/README.md +77 -23
- data/Rakefile +4 -25
- data/app/assets/javascripts/solidus_paypal_braintree/checkout.js +32 -3
- data/app/assets/javascripts/solidus_paypal_braintree/client.js +23 -4
- data/app/assets/javascripts/solidus_paypal_braintree/constants.js +19 -0
- data/app/assets/javascripts/solidus_paypal_braintree/frontend.js +1 -0
- data/app/assets/javascripts/solidus_paypal_braintree/hosted_form.js +15 -5
- data/app/assets/javascripts/solidus_paypal_braintree/paypal_button.js +47 -20
- data/app/assets/javascripts/solidus_paypal_braintree/paypal_messaging.js +22 -0
- data/app/decorators/controllers/solidus_paypal_braintree/admin_payments_controller_decorator.rb +11 -0
- data/app/decorators/controllers/solidus_paypal_braintree/checkout_controller_decorator.rb +11 -0
- data/app/decorators/controllers/solidus_paypal_braintree/client_tokens_controller.rb +41 -0
- data/app/decorators/controllers/solidus_paypal_braintree/orders_controller_decorator.rb +11 -0
- data/app/decorators/models/solidus_paypal_braintree/spree/store_decorator.rb +20 -0
- data/app/decorators/models/solidus_paypal_braintree/spree/user_decorator.rb +13 -0
- data/app/helpers/solidus_paypal_braintree/braintree_admin_helper.rb +23 -0
- data/app/helpers/solidus_paypal_braintree/braintree_checkout_helper.rb +46 -0
- data/app/models/application_record.rb +2 -0
- data/app/models/solidus_paypal_braintree/address.rb +30 -0
- data/app/models/solidus_paypal_braintree/configuration.rb +26 -3
- data/app/models/solidus_paypal_braintree/customer.rb +7 -3
- data/app/models/solidus_paypal_braintree/gateway.rb +52 -20
- data/app/models/solidus_paypal_braintree/response.rb +3 -2
- data/app/models/solidus_paypal_braintree/source.rb +21 -7
- data/app/models/solidus_paypal_braintree/transaction.rb +2 -0
- data/app/models/solidus_paypal_braintree/transaction_address.rb +30 -12
- data/app/models/solidus_paypal_braintree/transaction_import.rb +13 -9
- data/app/views/spree/api/payments/source_views/_paypal_braintree.json.jbuilder +3 -0
- data/app/views/spree/checkout/existing_payment/_paypal_braintree.html.erb +10 -0
- data/app/views/spree/shared/_apple_pay_button.html.erb +2 -2
- data/app/views/spree/shared/_braintree_errors.html.erb +11 -17
- data/app/views/spree/shared/_braintree_hosted_fields.html.erb +24 -9
- data/app/views/spree/shared/_paypal_braintree_head_scripts.html.erb +9 -6
- data/app/views/spree/shared/_paypal_cart_button.html.erb +16 -2
- data/app/views/spree/shared/_paypal_messaging.html.erb +13 -0
- data/bin/console +17 -0
- data/bin/rails +15 -0
- data/bin/setup +8 -0
- data/config/locales/en.yml +10 -0
- data/config/locales/it.yml +51 -8
- data/config/routes.rb +2 -0
- data/db/migrate/20160906201711_create_solidus_paypal_braintree_customers.rb +3 -1
- data/db/migrate/20161125172005_add_braintree_configuration_to_stores.rb +5 -7
- data/db/migrate/20170505193712_add_null_constraint_to_sources.rb +3 -1
- data/db/migrate/20190705115327_add_paypal_button_preferences_to_braintree_configurations.rb +5 -0
- data/db/migrate/20190911141712_add_3d_secure_to_braintree_configuration.rb +5 -0
- data/lib/controllers/backend/solidus_paypal_braintree/configurations_controller.rb +20 -5
- data/lib/controllers/frontend/solidus_paypal_braintree/checkouts_controller.rb +25 -21
- data/lib/controllers/frontend/solidus_paypal_braintree/transactions_controller.rb +55 -51
- data/lib/generators/solidus_paypal_braintree/install/install_generator.rb +7 -5
- data/lib/solidus_paypal_braintree.rb +4 -0
- data/lib/solidus_paypal_braintree/country_mapper.rb +4 -2
- data/lib/solidus_paypal_braintree/engine.rb +11 -11
- data/lib/solidus_paypal_braintree/factories.rb +8 -4
- data/lib/solidus_paypal_braintree/request_protection.rb +3 -0
- data/lib/solidus_paypal_braintree/version.rb +3 -1
- data/lib/views/backend/solidus_paypal_braintree/configurations/list.html.erb +30 -5
- data/lib/views/backend/spree/admin/payments/source_forms/_paypal_braintree.html.erb +2 -2
- data/lib/views/backend/spree/admin/payments/source_views/_paypal_braintree.html.erb +2 -2
- data/lib/views/backend_v1.2/spree/admin/payments/source_forms/_paypal_braintree.html.erb +2 -2
- data/lib/views/frontend/spree/checkout/payment/_paypal_braintree.html.erb +4 -2
- data/lib/views/frontend/spree/shared/_paypal_checkout_button.html.erb +30 -0
- data/solidus_paypal_braintree.gemspec +42 -0
- data/spec/controllers/solidus_paypal_braintree/checkouts_controller_spec.rb +99 -0
- data/spec/controllers/solidus_paypal_braintree/client_tokens_controller_spec.rb +55 -0
- data/spec/controllers/solidus_paypal_braintree/configurations_controller_spec.rb +73 -0
- data/spec/controllers/solidus_paypal_braintree/transactions_controller_spec.rb +183 -0
- data/spec/features/backend/configuration_spec.rb +23 -0
- data/spec/features/backend/new_payment_spec.rb +137 -0
- data/spec/features/frontend/braintree_credit_card_checkout_spec.rb +187 -0
- data/spec/features/frontend/paypal_checkout_spec.rb +166 -0
- data/spec/fixtures/cassettes/admin/invalid_credit_card.yml +63 -0
- data/spec/fixtures/cassettes/admin/resubmit_credit_card.yml +352 -0
- data/spec/fixtures/cassettes/admin/valid_credit_card.yml +412 -0
- data/spec/fixtures/cassettes/braintree/create_profile.yml +71 -0
- data/spec/fixtures/cassettes/braintree/generate_token.yml +63 -0
- data/spec/fixtures/cassettes/braintree/token.yml +63 -0
- data/spec/fixtures/cassettes/checkout/invalid_credit_card.yml +63 -0
- data/spec/fixtures/cassettes/checkout/resubmit_credit_card.yml +216 -0
- data/spec/fixtures/cassettes/checkout/update.yml +71 -0
- data/spec/fixtures/cassettes/checkout/valid_credit_card.yml +156 -0
- data/spec/fixtures/cassettes/gateway/authorize.yml +86 -0
- data/spec/fixtures/cassettes/gateway/authorize/credit_card/address.yml +86 -0
- data/spec/fixtures/cassettes/gateway/authorize/merchant_account/EUR.yml +154 -0
- data/spec/fixtures/cassettes/gateway/authorize/paypal/EUR.yml +90 -0
- data/spec/fixtures/cassettes/gateway/authorize/paypal/address.yml +90 -0
- data/spec/fixtures/cassettes/gateway/authorized_transaction.yml +73 -0
- data/spec/fixtures/cassettes/gateway/cancel/missing.yml +63 -0
- data/spec/fixtures/cassettes/gateway/cancel/refunds.yml +272 -0
- data/spec/fixtures/cassettes/gateway/cancel/void.yml +201 -0
- data/spec/fixtures/cassettes/gateway/capture.yml +141 -0
- data/spec/fixtures/cassettes/gateway/complete.yml +157 -0
- data/spec/fixtures/cassettes/gateway/credit.yml +208 -0
- data/spec/fixtures/cassettes/gateway/purchase.yml +87 -0
- data/spec/fixtures/cassettes/gateway/settled_transaction.yml +140 -0
- data/spec/fixtures/cassettes/gateway/void.yml +137 -0
- data/spec/fixtures/cassettes/source/card_type.yml +267 -0
- data/spec/fixtures/cassettes/source/last4.yml +267 -0
- data/spec/fixtures/cassettes/transaction/import/valid.yml +71 -0
- data/spec/fixtures/cassettes/transaction/import/valid/capture.yml +224 -0
- data/spec/fixtures/views/spree/orders/edit.html.erb +50 -0
- data/spec/helpers/solidus_paypal_braintree/braintree_admin_helper_spec.rb +17 -0
- data/spec/models/solidus_paypal_braintree/address_spec.rb +51 -0
- data/spec/models/solidus_paypal_braintree/avs_result_spec.rb +317 -0
- data/spec/models/solidus_paypal_braintree/gateway_spec.rb +692 -0
- data/spec/models/solidus_paypal_braintree/response_spec.rb +280 -0
- data/spec/models/solidus_paypal_braintree/source_spec.rb +360 -0
- data/spec/models/solidus_paypal_braintree/transaction_address_spec.rb +253 -0
- data/spec/models/solidus_paypal_braintree/transaction_import_spec.rb +283 -0
- data/spec/models/solidus_paypal_braintree/transaction_spec.rb +85 -0
- data/spec/models/spree/store_spec.rb +14 -0
- data/spec/requests/spree/api/orders_controller_spec.rb +36 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/capybara.rb +7 -0
- data/spec/support/factories.rb +2 -0
- data/spec/support/gateway_helpers.rb +29 -0
- data/spec/support/order_ready_for_payment.rb +37 -0
- data/spec/support/vcr.rb +42 -0
- data/spec/support/views.rb +1 -0
- metadata +182 -194
- data/app/controllers/solidus_paypal_braintree/client_tokens_controller.rb +0 -22
- data/app/helpers/braintree_admin_helper.rb +0 -18
- data/app/models/spree/store_decorator.rb +0 -11
- data/app/views/spree/shared/_paypal_checkout_button.html.erb +0 -27
- data/config/initializers/braintree.rb +0 -1
data/LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2016 Stembolt
|
1
|
+
Copyright (c) 2016-2020 Stembolt and other contributors
|
2
2
|
All rights reserved.
|
3
3
|
|
4
4
|
Redistribution and use in source and binary forms, with or without modification,
|
@@ -9,7 +9,7 @@ are permitted provided that the following conditions are met:
|
|
9
9
|
* Redistributions in binary form must reproduce the above copyright notice,
|
10
10
|
this list of conditions and the following disclaimer in the documentation
|
11
11
|
and/or other materials provided with the distribution.
|
12
|
-
* Neither the name
|
12
|
+
* Neither the name Solidus nor the names of its contributors may be used to
|
13
13
|
endorse or promote products derived from this software without specific
|
14
14
|
prior written permission.
|
15
15
|
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
SolidusPaypalBraintree
|
2
2
|
======================
|
3
3
|
|
4
|
-
[![
|
4
|
+
[![CircleCI](https://circleci.com/gh/solidusio/solidus_paypal_braintree.svg?style=svg)](https://circleci.com/gh/solidusio/solidus_paypal_braintree)
|
5
5
|
|
6
6
|
`solidus_paypal_braintree` is an extension that adds support for using [Braintree](https://www.braintreepayments.com) as a payment source in your [Solidus](https://solidus.io/) store. It supports Apple Pay, PayPal, and credit card transactions.
|
7
7
|
|
@@ -39,14 +39,15 @@ Payment methods can accept preferences either directly entered in admin, or from
|
|
39
39
|
1. Set static preferences in an initializer
|
40
40
|
```ruby
|
41
41
|
# config/initializers/spree.rb
|
42
|
-
Spree::Config.
|
42
|
+
Spree::Config.config do |config|
|
43
43
|
config.static_model_preferences.add(
|
44
44
|
SolidusPaypalBraintree::Gateway,
|
45
45
|
'braintree_credentials', {
|
46
46
|
environment: Rails.env.production? ? 'production' : 'sandbox',
|
47
47
|
merchant_id: ENV['BRAINTREE_MERCHANT_ID'],
|
48
48
|
public_key: ENV['BRAINTREE_PUBLIC_KEY'],
|
49
|
-
private_key: ENV['BRAINTREE_PRIVATE_KEY']
|
49
|
+
private_key: ENV['BRAINTREE_PRIVATE_KEY'],
|
50
|
+
paypal_flow: 'vault', # 'checkout' is accepted too
|
50
51
|
}
|
51
52
|
)
|
52
53
|
end
|
@@ -72,7 +73,7 @@ SolidusPaypalBraintree::Gateway.new(
|
|
72
73
|
```
|
73
74
|
|
74
75
|
### Configure payment types
|
75
|
-
Your payment method can accept payments in three ways: through Paypal, through ApplePay, or with credit card details entered directly by the customer. By default all are disabled for all your site's stores.
|
76
|
+
Your payment method can accept payments in three ways: through Paypal, through ApplePay, or with credit card details entered directly by the customer. By default all are disabled for all your site's stores. Before proceeding to checkout, ensure you've created a Braintree configuration for your store:
|
76
77
|
|
77
78
|
1. Visit /solidus_paypal_braintree/configurations/list
|
78
79
|
|
@@ -122,8 +123,8 @@ The following is a relatively bare-bones implementation to enable Apple Pay on t
|
|
122
123
|
amount: "<%= current_order.total %>",
|
123
124
|
shippingContact: {
|
124
125
|
emailAddress: '<%= current_order.email %>',
|
125
|
-
|
126
|
-
|
126
|
+
givenName: '<%= address.firstname %>',
|
127
|
+
familyName: '<%= address.lastname %>',
|
127
128
|
phoneNumber: '<%= address.phone %>',
|
128
129
|
addressLines: ['<%= address.address1 %>','<%= address.address2 %>'],
|
129
130
|
locality: '<%= address.city %>',
|
@@ -152,32 +153,32 @@ It will only be displayed if the `SolidusPaypalBraintree::Gateway` payment
|
|
152
153
|
method is configured to display on the frontend and PayPal is enabled in the
|
153
154
|
store's configuration.
|
154
155
|
|
156
|
+
You can find button configuration options in
|
157
|
+
`/solidus_paypal_braintree/configurations/list` if you want to change the color,
|
158
|
+
shape, layout, and a few other options. Keep in mind that the `paypal_button_tagline`
|
159
|
+
does not work when the `paypal_button_layout` is set to `vertical`, and will be
|
160
|
+
ignored in that case.
|
161
|
+
|
155
162
|
The checkout view
|
156
163
|
[initializes the PayPal button](/lib/views/frontend/spree/checkout/payment/_paypal_braintree.html.erb)
|
157
164
|
using the
|
158
|
-
[
|
159
|
-
which allows the source to be reused.
|
165
|
+
[Vault flow](https://developers.braintreepayments.com/guides/paypal/overview/javascript/v3),
|
166
|
+
which allows the source to be reused. Please note that PayPal messaging is disabled with vault flow. If you want, you can use [Checkout with PayPal](https://developers.braintreepayments.com/guides/paypal/checkout-with-paypal/javascript/v3)
|
167
|
+
instead, which doesn't allow you to reuse sources but allows your customers to pay with their PayPal
|
168
|
+
balance and with PayPal financing options ([see setup instructions](#create-a-new-payment-method)).
|
160
169
|
|
161
170
|
If you are creating your own checkout view or would like to customize the
|
162
171
|
[options that get passed to tokenize](https://braintree.github.io/braintree-web/3.6.3/PayPal.html#tokenize)
|
163
|
-
, you can initialize your own using the `
|
172
|
+
, you can initialize your own using the `CreatePaypalButton` JS object:
|
164
173
|
|
165
174
|
```javascript
|
166
|
-
var
|
167
|
-
|
168
|
-
button.initialize({
|
175
|
+
var paypalOptions = {
|
169
176
|
// your configuration options here
|
170
|
-
}
|
171
|
-
```
|
177
|
+
}
|
172
178
|
|
173
|
-
|
174
|
-
transaction via AJAX and advances the order to confirm. It is possible to provide
|
175
|
-
your own callback function to customize the behaviour after tokenize as follows:
|
179
|
+
var button = new SolidusPaypalBraintree.createPaypalButton(document.querySelector("#your-button-id"), paypalOptions);
|
176
180
|
|
177
|
-
|
178
|
-
var button = new PaypalButton(document.querySelector("#your-button-id"));
|
179
|
-
|
180
|
-
button.setTokenizeCallback(your-callback);
|
181
|
+
button.initialize();
|
181
182
|
```
|
182
183
|
|
183
184
|
### Express checkout from the cart
|
@@ -187,6 +188,30 @@ A PayPal button can also be included on the cart view to enable express checkout
|
|
187
188
|
render "spree/shared/paypal_cart_button"
|
188
189
|
```
|
189
190
|
|
191
|
+
### PayPal Financing Messaging
|
192
|
+
|
193
|
+
PayPal offers an [on-site messaging component](https://www.paypal.com/us/webapps/mpp/on-site-messaging) to notify the customer that there are financing options available. This component is included in both the cart and checkout partials, but is disabled by default. To enable this option, you'll need to use the `checkout` flow, and set the `paypal button messaging` option to `true` in your Braintree configuration.
|
194
|
+
|
195
|
+
You can also include this view partial to implement this messaging component anywhere - for instance, on the product page:
|
196
|
+
```ruby
|
197
|
+
render "spree/shared/paypal_messaging, options: {total: @product.price, placement: "product", currency: 'USD'}"
|
198
|
+
```
|
199
|
+
|
200
|
+
While we provide the messaging component on the payment buttons for cart and checkout, you're expected to move these to where they make the most sense for your frontend. PayPal recommends keeping the messaging directly below wherever the order or product total is located.
|
201
|
+
|
202
|
+
#### PayPal configuration
|
203
|
+
|
204
|
+
If your store requires the [phone number into user addresses](https://github.com/solidusio/solidus/blob/859143f3f061de79cc1b385234599422b8ae8e21/core/app/models/spree/address.rb#L151-L153)
|
205
|
+
you'll need to configure PayPal to return the phone back when it returns the
|
206
|
+
address used by the user:
|
207
|
+
|
208
|
+
1. Log into your PayPal account
|
209
|
+
2. Go to Profile -> My Selling Tools -> Website preferences
|
210
|
+
3. Set Contact Telephone to `On (Required Field)` or `On (Optional Field)`
|
211
|
+
|
212
|
+
Using the option `Off` will not make the address valid and will raise a
|
213
|
+
validation error.
|
214
|
+
|
190
215
|
## Optional configuration
|
191
216
|
|
192
217
|
### Accepting multiple currencies
|
@@ -195,7 +220,12 @@ This preference allows users to provide different Merchant Account Ids for
|
|
195
220
|
different currencies. If you only plan to accept payment in one currency, the
|
196
221
|
defaut Merchant Account Id will be used and you can omit this option.
|
197
222
|
An example of setting this preference can be found
|
198
|
-
[here](https://github.com/solidusio/solidus_paypal_braintree/blob/
|
223
|
+
[here](https://github.com/solidusio/solidus_paypal_braintree/blob/bf5fe0e154d38f7c498f1c54450bb4de7608ff04/spec/support/gateway_helpers.rb#L11-L13).
|
224
|
+
|
225
|
+
In addition to this, you can also specify different PayPal accounts for each
|
226
|
+
currency by using the `paypal_payee_email_map` preference. If you only want
|
227
|
+
to use one PayPal account for all currencies, then you can ignore this option.
|
228
|
+
You can find an example of setting this preference [here](https://github.com/solidusio/solidus_paypal_braintree/blob/bf5fe0e154d38f7c498f1c54450bb4de7608ff04/spec/support/gateway_helpers.rb#L14-L16).
|
199
229
|
|
200
230
|
### Default store configuration
|
201
231
|
The migrations for this gem will add a default configuration to all stores that
|
@@ -204,9 +234,33 @@ has each payment type disabled. It also adds a `before_create` callback to
|
|
204
234
|
default configuration that gets created by overriding the private
|
205
235
|
`build_default_configuration` method on `Spree::Store`.
|
206
236
|
|
237
|
+
### Hosted Fields Styling
|
238
|
+
You can style the Braintree credit card fields by using the `credit_card_fields_style` preference on the payment method. The `credit_card_fields_style` will be passed to the `style` key when initializing the credit card fields. You can find more information about styling hosted fields can be found [here.](https://developers.braintreepayments.com/guides/hosted-fields/styling/javascript/v3)
|
239
|
+
|
240
|
+
You can also use the `placeholder_text` preference on the payment method to set the placeholder text you'd like to use for each of the hosted fields. You'll pass the field name in as the key, and the placeholder text you'd like to use as the value. For example:
|
241
|
+
```ruby
|
242
|
+
{ number: "Enter card number", cvv: "Enter CVV", expirationDate: "mm/yy" }
|
243
|
+
```
|
244
|
+
|
245
|
+
### 3D Secure
|
246
|
+
|
247
|
+
This gem supports [3D Secure 2](https://developers.braintreepayments.com/guides/3d-secure/overview),
|
248
|
+
which satisfies the [Strong Customer Authentication (SCA)](https://www.braintreepayments.com/blog/getting-up-to-speed-on-psd2-regulation-2/)
|
249
|
+
requirements introduced by PSD2.
|
250
|
+
|
251
|
+
3D Secure can be enabled from Solidus Admin -> Braintree (left-side menu) ->
|
252
|
+
tick _3D Secure_ checkbox.
|
253
|
+
|
254
|
+
Once enabled, you can use the following card numbers to test 3DS 2 on your
|
255
|
+
client side in sandbox:
|
256
|
+
https://developers.braintreepayments.com/guides/3d-secure/migration/javascript/v3#client-side-sandbox-testing.
|
257
|
+
|
207
258
|
Testing
|
208
259
|
-------
|
209
260
|
|
261
|
+
To run the specs it is required to set the Braintree test account data in these environment variables:
|
262
|
+
`BRAINTREE_PUBLIC_KEY`, `BRAINTREE_PRIVATE_KEY`, `BRAINTREE_MERCHANT_ID` and `BRAINTREE_PAYPAL_PAYEE_EMAIL`
|
263
|
+
|
210
264
|
First bundle your dependencies, then run `rake`. `rake` will default to building the dummy app if it does not exist, then it will run specs, and [Rubocop](https://github.com/bbatsov/rubocop) static code analysis. The dummy app can be regenerated by using `rake test_app`.
|
211
265
|
|
212
266
|
```shell
|
@@ -221,4 +275,4 @@ Simply add this require statement to your spec_helper:
|
|
221
275
|
require 'solidus_paypal_braintree/factories'
|
222
276
|
```
|
223
277
|
|
224
|
-
Copyright (c) 2016 Stembolt, released under the New BSD License
|
278
|
+
Copyright (c) 2016-2020 Stembolt and others contributors, released under the New BSD License
|
data/Rakefile
CHANGED
@@ -1,27 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'solidus_dev_support/rake_tasks'
|
4
|
+
SolidusDevSupport::RakeTasks.install
|
4
5
|
|
5
|
-
|
6
|
-
require 'spree/testing_support/extension_rake'
|
7
|
-
require 'rspec/core/rake_task'
|
8
|
-
|
9
|
-
RSpec::Core::RakeTask.new(:spec)
|
10
|
-
|
11
|
-
task default: %i(first_run spec)
|
12
|
-
rescue LoadError
|
13
|
-
# no rspec available
|
14
|
-
end
|
15
|
-
|
16
|
-
task :first_run do
|
17
|
-
if Dir['spec/dummy'].empty?
|
18
|
-
Rake::Task[:test_app].invoke
|
19
|
-
Dir.chdir('../../')
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
desc 'Generates a dummy app for testing'
|
24
|
-
task :test_app do
|
25
|
-
ENV['LIB_NAME'] = 'solidus_paypal_braintree'
|
26
|
-
Rake::Task['extension:test_app'].invoke
|
27
|
-
end
|
6
|
+
task default: 'extension:specs'
|
@@ -15,11 +15,18 @@ $(function() {
|
|
15
15
|
* https://github.com/rails/jquery-rails/blob/master/vendor/assets/javascripts/jquery_ujs.js#L517
|
16
16
|
* The only way we can re-enable it is by delaying longer than that timeout
|
17
17
|
* or stopping propagation so their submit handler doesn't run. */
|
18
|
-
if ($.rails) {
|
18
|
+
if ($.rails && typeof $.rails.enableFormElement !== 'undefined') {
|
19
19
|
setTimeout(function () {
|
20
20
|
$.rails.enableFormElement($submitButton);
|
21
21
|
$submitButton.attr("disabled", false).removeClass("disabled").addClass("primary");
|
22
22
|
}, 100);
|
23
|
+
} else if (typeof Rails !== 'undefined' && typeof Rails.enableElement !== 'undefined') {
|
24
|
+
/* Indicates that we have rails-ujs instead of jquery-ujs. Rails-ujs was added to rails
|
25
|
+
* core in Rails 5.1.0 */
|
26
|
+
setTimeout(function () {
|
27
|
+
Rails.enableElement($submitButton[0]);
|
28
|
+
$submitButton.attr("disabled", false).removeClass("disabled").addClass("primary");
|
29
|
+
}, 100);
|
23
30
|
} else {
|
24
31
|
$submitButton.attr("disabled", false).removeClass("disabled").addClass("primary");
|
25
32
|
}
|
@@ -37,16 +44,38 @@ $(function() {
|
|
37
44
|
var $nonce = $("#payment_method_nonce", $field);
|
38
45
|
|
39
46
|
if ($nonce.length > 0 && $nonce.val() === "") {
|
47
|
+
var client = braintreeForm._merchantConfigurationOptions._solidusClient;
|
48
|
+
|
40
49
|
event.preventDefault();
|
41
50
|
disableSubmit();
|
42
51
|
|
43
52
|
braintreeForm.tokenize(function(error, payload) {
|
44
53
|
if (error) {
|
45
54
|
braintreeError(error);
|
46
|
-
|
47
|
-
|
55
|
+
return;
|
56
|
+
}
|
57
|
+
|
58
|
+
$nonce.val(payload.nonce);
|
59
|
+
|
60
|
+
if (!client.useThreeDSecure) {
|
48
61
|
$paymentForm.submit();
|
62
|
+
return;
|
63
|
+
}
|
64
|
+
|
65
|
+
threeDSecureOptions.nonce = payload.nonce;
|
66
|
+
threeDSecureOptions.bin = payload.details.bin;
|
67
|
+
threeDSecureOptions.onLookupComplete = function(data, next) {
|
68
|
+
next();
|
49
69
|
}
|
70
|
+
client._threeDSecureInstance.verifyCard(threeDSecureOptions, function(error, response) {
|
71
|
+
if (error === null && (!response.liabilityShiftPossible || response.liabilityShifted)) {
|
72
|
+
$nonce.val(response.nonce);
|
73
|
+
$paymentForm.submit();
|
74
|
+
} else {
|
75
|
+
$nonce.val('');
|
76
|
+
braintreeError(error || { code: 'THREEDS_AUTHENTICATION_FAILED' });
|
77
|
+
}
|
78
|
+
});
|
50
79
|
});
|
51
80
|
}
|
52
81
|
}
|
@@ -57,10 +57,12 @@ SolidusPaypalBraintree.Client = function(config) {
|
|
57
57
|
this.useDataCollector = config.useDataCollector;
|
58
58
|
this.usePaypal = config.usePaypal;
|
59
59
|
this.useApplepay = config.useApplepay;
|
60
|
+
this.useThreeDSecure = config.useThreeDSecure;
|
60
61
|
|
61
62
|
this._braintreeInstance = null;
|
62
63
|
this._dataCollectorInstance = null;
|
63
64
|
this._paypalInstance = null;
|
65
|
+
this._threeDSecureInstance = null;
|
64
66
|
};
|
65
67
|
|
66
68
|
/**
|
@@ -71,18 +73,22 @@ SolidusPaypalBraintree.Client.prototype.initialize = function() {
|
|
71
73
|
var initializationPromise = this._fetchToken().
|
72
74
|
then(this._createBraintreeInstance.bind(this));
|
73
75
|
|
74
|
-
if(this.useDataCollector) {
|
76
|
+
if (this.useDataCollector) {
|
75
77
|
initializationPromise = initializationPromise.then(this._createDataCollector.bind(this));
|
76
78
|
}
|
77
79
|
|
78
|
-
if(this.usePaypal) {
|
80
|
+
if (this.usePaypal) {
|
79
81
|
initializationPromise = initializationPromise.then(this._createPaypal.bind(this));
|
80
82
|
}
|
81
83
|
|
82
|
-
if(this.useApplepay) {
|
84
|
+
if (this.useApplepay) {
|
83
85
|
initializationPromise = initializationPromise.then(this._createApplepay.bind(this));
|
84
86
|
}
|
85
87
|
|
88
|
+
if (this.useThreeDSecure) {
|
89
|
+
initializationPromise = initializationPromise.then(this._createThreeDSecure.bind(this));
|
90
|
+
}
|
91
|
+
|
86
92
|
return initializationPromise.then(this._invokeReadyCallback.bind(this));
|
87
93
|
};
|
88
94
|
|
@@ -173,7 +179,9 @@ SolidusPaypalBraintree.Client.prototype._createPaypal = function() {
|
|
173
179
|
}]).then(function (paypalInstance) {
|
174
180
|
this._paypalInstance = paypalInstance;
|
175
181
|
return paypalInstance;
|
176
|
-
}.bind(this))
|
182
|
+
}.bind(this), function(error) {
|
183
|
+
console.error(error.name + ':', error.message);
|
184
|
+
});
|
177
185
|
};
|
178
186
|
|
179
187
|
SolidusPaypalBraintree.Client.prototype._createApplepay = function() {
|
@@ -184,3 +192,14 @@ SolidusPaypalBraintree.Client.prototype._createApplepay = function() {
|
|
184
192
|
return applePayInstance;
|
185
193
|
}.bind(this));
|
186
194
|
};
|
195
|
+
|
196
|
+
SolidusPaypalBraintree.Client.prototype._createThreeDSecure = function() {
|
197
|
+
return SolidusPaypalBraintree.PromiseShim.convertBraintreePromise(braintree.threeDSecure.create, [{
|
198
|
+
client: this._braintreeInstance,
|
199
|
+
version: 2
|
200
|
+
}]).then(function (threeDSecureInstance) {
|
201
|
+
this._threeDSecureInstance = threeDSecureInstance;
|
202
|
+
}.bind(this), function(error) {
|
203
|
+
console.log(error);
|
204
|
+
});
|
205
|
+
};
|
@@ -26,6 +26,10 @@ SolidusPaypalBraintree = {
|
|
26
26
|
return SolidusPaypalBraintree.PaypalButton;
|
27
27
|
},
|
28
28
|
|
29
|
+
paypalMessaging: function() {
|
30
|
+
return SolidusPaypalBraintree.PaypalMessaging;
|
31
|
+
},
|
32
|
+
|
29
33
|
applepayButton: function() {
|
30
34
|
return SolidusPaypalBraintree.ApplepayButton;
|
31
35
|
}
|
@@ -51,6 +55,10 @@ SolidusPaypalBraintree = {
|
|
51
55
|
return SolidusPaypalBraintree._factory(SolidusPaypalBraintree.config.classes.paypalButton(), arguments);
|
52
56
|
},
|
53
57
|
|
58
|
+
createPaypalMessaging: function() {
|
59
|
+
return SolidusPaypalBraintree._factory(SolidusPaypalBraintree.config.classes.paypalMessaging(), arguments);
|
60
|
+
},
|
61
|
+
|
54
62
|
createApplePayButton: function() {
|
55
63
|
return SolidusPaypalBraintree._factory(SolidusPaypalBraintree.config.classes.applepayButton(), arguments);
|
56
64
|
},
|
@@ -60,3 +68,14 @@ SolidusPaypalBraintree = {
|
|
60
68
|
return new (Function.prototype.bind.apply(klass, [null].concat(normalizedArgs)));
|
61
69
|
}
|
62
70
|
};
|
71
|
+
|
72
|
+
BraintreeError = {
|
73
|
+
DEFAULT: "Something bad happened!",
|
74
|
+
|
75
|
+
getErrorFromSlug: function(slug) {
|
76
|
+
error = BraintreeError.DEFAULT
|
77
|
+
if (slug in BraintreeError)
|
78
|
+
error = BraintreeError[slug]
|
79
|
+
return error
|
80
|
+
}
|
81
|
+
}
|
@@ -4,7 +4,11 @@ SolidusPaypalBraintree.HostedForm = function(paymentMethodId) {
|
|
4
4
|
};
|
5
5
|
|
6
6
|
SolidusPaypalBraintree.HostedForm.prototype.initialize = function() {
|
7
|
-
this.client = SolidusPaypalBraintree.createClient({
|
7
|
+
this.client = SolidusPaypalBraintree.createClient({
|
8
|
+
paymentMethodId: this.paymentMethodId,
|
9
|
+
useThreeDSecure: (typeof(window.threeDSecureOptions) !== 'undefined'),
|
10
|
+
});
|
11
|
+
|
8
12
|
return this.client.initialize().
|
9
13
|
then(this._createHostedFields.bind(this));
|
10
14
|
};
|
@@ -15,21 +19,27 @@ SolidusPaypalBraintree.HostedForm.prototype._createHostedFields = function () {
|
|
15
19
|
}
|
16
20
|
|
17
21
|
var opts = {
|
22
|
+
_solidusClient: this.client,
|
18
23
|
client: this.client.getBraintreeInstance(),
|
19
24
|
|
20
25
|
fields: {
|
21
26
|
number: {
|
22
|
-
selector: "#card_number" + this.paymentMethodId
|
27
|
+
selector: "#card_number" + this.paymentMethodId,
|
28
|
+
placeholder: placeholder_text["number"]
|
23
29
|
},
|
24
30
|
|
25
31
|
cvv: {
|
26
|
-
selector: "#card_code" + this.paymentMethodId
|
32
|
+
selector: "#card_code" + this.paymentMethodId,
|
33
|
+
placeholder: placeholder_text["cvv"]
|
27
34
|
},
|
28
35
|
|
29
36
|
expirationDate: {
|
30
|
-
selector: "#card_expiry" + this.paymentMethodId
|
37
|
+
selector: "#card_expiry" + this.paymentMethodId,
|
38
|
+
placeholder: placeholder_text["expirationDate"]
|
31
39
|
}
|
32
|
-
}
|
40
|
+
},
|
41
|
+
|
42
|
+
styles: credit_card_fields_style
|
33
43
|
};
|
34
44
|
|
35
45
|
return SolidusPaypalBraintree.PromiseShim.convertBraintreePromise(braintree.hostedFields.create, [opts]);
|
@@ -7,6 +7,12 @@
|
|
7
7
|
SolidusPaypalBraintree.PaypalButton = function(element, paypalOptions, options) {
|
8
8
|
this._element = element;
|
9
9
|
this._paypalOptions = paypalOptions || {};
|
10
|
+
|
11
|
+
this.locale = paypalOptions['locale'] || "en_US";
|
12
|
+
this.style = paypalOptions['style'] || {};
|
13
|
+
delete paypalOptions['locale'];
|
14
|
+
delete paypalOptions['style'];
|
15
|
+
|
10
16
|
this._options = options || {};
|
11
17
|
this._client = null;
|
12
18
|
this._environment = this._paypalOptions.environment || 'sandbox';
|
@@ -34,17 +40,27 @@ SolidusPaypalBraintree.PaypalButton.prototype.initialize = function() {
|
|
34
40
|
SolidusPaypalBraintree.PaypalButton.prototype.initializeCallback = function() {
|
35
41
|
this._paymentMethodId = this._client.paymentMethodId;
|
36
42
|
|
37
|
-
|
38
|
-
|
43
|
+
this._client.getPaypalInstance().loadPayPalSDK({
|
44
|
+
currency: this._paypalOptions.currency,
|
45
|
+
commit: true,
|
46
|
+
vault: this._paypalOptions.flow == "vault",
|
47
|
+
components: this.style['messaging'] == "true" && this._paypalOptions.flow != "vault" ? "buttons,messages" : "buttons",
|
48
|
+
intent: this._paypalOptions.flow == "vault" ? "tokenize" : "authorize"
|
49
|
+
}).then(() => {
|
50
|
+
var create_method = this._paypalOptions.flow == "vault" ? "createBillingAgreement" : "createOrder"
|
39
51
|
|
40
|
-
|
41
|
-
|
42
|
-
|
52
|
+
var render_config = {
|
53
|
+
style: this.style,
|
54
|
+
[create_method]: function () {
|
55
|
+
return this._client.getPaypalInstance().createPayment(this._paypalOptions);
|
56
|
+
}.bind(this),
|
57
|
+
onApprove: function (data, actions) {
|
58
|
+
return this._client.getPaypalInstance().tokenizePayment(data, this._tokenizeCallback.bind(this));
|
59
|
+
}.bind(this)
|
60
|
+
};
|
43
61
|
|
44
|
-
|
45
|
-
|
46
|
-
}.bind(this)
|
47
|
-
}, this._element);
|
62
|
+
paypal.Buttons(render_config).render(this._element);
|
63
|
+
})
|
48
64
|
};
|
49
65
|
|
50
66
|
/**
|
@@ -70,7 +86,22 @@ SolidusPaypalBraintree.PaypalButton.prototype._tokenizeCallback = function(token
|
|
70
86
|
window.location.href = response.redirectUrl;
|
71
87
|
},
|
72
88
|
error: function(xhr) {
|
73
|
-
|
89
|
+
var errorText = BraintreeError.DEFAULT;
|
90
|
+
|
91
|
+
if (xhr.responseJSON && xhr.responseJSON.errors) {
|
92
|
+
var errors = [];
|
93
|
+
$.each(xhr.responseJSON.errors, function(key, values) {
|
94
|
+
$.each(values, function(index, value) {
|
95
|
+
errors.push(key + " " + value)
|
96
|
+
});
|
97
|
+
});
|
98
|
+
|
99
|
+
if (errors.length > 0)
|
100
|
+
errorText = errors.join(", ");
|
101
|
+
}
|
102
|
+
|
103
|
+
console.error("Error submitting transaction: " + errorText);
|
104
|
+
SolidusPaypalBraintree.showError(errorText);
|
74
105
|
},
|
75
106
|
});
|
76
107
|
};
|
@@ -102,22 +133,18 @@ SolidusPaypalBraintree.PaypalButton.prototype._transactionParams = function(payl
|
|
102
133
|
* @param {object} payload - The payload returned by Braintree after tokenization
|
103
134
|
*/
|
104
135
|
SolidusPaypalBraintree.PaypalButton.prototype._addressParams = function(payload) {
|
105
|
-
var
|
136
|
+
var name;
|
106
137
|
var payload_address = payload.details.shippingAddress || payload.details.billingAddress;
|
138
|
+
if (!payload_address) return {};
|
107
139
|
|
108
140
|
if (payload_address.recipientName) {
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
if (!first_name || !last_name) {
|
114
|
-
first_name = payload.details.firstName;
|
115
|
-
last_name = payload.details.lastName;
|
141
|
+
name = payload_address.recipientName
|
142
|
+
} else {
|
143
|
+
name = payload.details.firstName + " " + payload.details.lastName;
|
116
144
|
}
|
117
145
|
|
118
146
|
return {
|
119
|
-
"
|
120
|
-
"last_name" : last_name,
|
147
|
+
"name" : name,
|
121
148
|
"address_line_1" : payload_address.line1,
|
122
149
|
"address_line_2" : payload_address.line2,
|
123
150
|
"city" : payload_address.city,
|