spree-paypal_platform 5.1.3 → 5.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +2 -1
- data/app/models/spree/paypal_platform/gateway/payment_sessions.rb +29 -3
- data/app/models/spree/paypal_platform/gateway.rb +16 -0
- data/app/presenters/spree/paypal_platform/order_presenter.rb +23 -5
- data/app/serializers/spree/api/v3/payment_method_serializer_decorator.rb +20 -0
- data/app/views/spree/admin/payment_methods/configuration_guides/_spree_paypal_platform.html.erb +2 -1
- data/lib/spree/paypal_platform/factories.rb +2 -1
- data/lib/spree/paypal_platform/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4de54a54a0c7dd935c012bf20c72711b0bf78959b2556acebfacc3c0eb14e930
|
|
4
|
+
data.tar.gz: 94c506e594ce38d454fdff782d1dad610582b81e59e658d1bce232b29cf7344c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: abdce2d88ae3c2fb939cb6ce006bf1b6a654fadf98436df56ff3771c7f79b55c66e28fea31934bdc20310877b2f875c4e19fe0be873b4d15b1a05cfeee45af7c
|
|
7
|
+
data.tar.gz: 8c4d81e0b0f6e8d80da95c7febc3c6acaefda891d4dce1c5722ea0a9a0700da8f9beaeccb2dbe046ce0fd320b4dac0bccffa6a555cbd2e1356d719ce3d7077dc
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.1.5
|
|
4
|
+
|
|
5
|
+
- Gift cards and store credit: the PayPal order is created for the amount
|
|
6
|
+
due (`total_minus_store_credits`), not the full order total. Before, PayPal
|
|
7
|
+
charged the full total while Spree recorded the gift card as paying part
|
|
8
|
+
of it, so the customer paid twice for that part. A partial charge is sent
|
|
9
|
+
as a plain amount, without the breakdown and items that sum to the full
|
|
10
|
+
total.
|
|
11
|
+
- Completing a session refuses to capture when the PayPal order amount no
|
|
12
|
+
longer matches the amount due (e.g. a gift card was applied, or the cart
|
|
13
|
+
changed, after the PayPal order was created). The session fails and the
|
|
14
|
+
storefront creates a new one.
|
|
15
|
+
|
|
16
|
+
## 5.1.4
|
|
17
|
+
|
|
18
|
+
- `enable_card_fields` preference (checkbox, default on).
|
|
19
|
+
- Storefront sees `enable_apple_pay` and `enable_card_fields` on the payment
|
|
20
|
+
method (`public_preferences`) and on the payment session `external_data`.
|
|
21
|
+
- Client token is not requested when Card Fields are disabled.
|
|
22
|
+
|
|
3
23
|
## 5.1.3
|
|
4
24
|
|
|
5
25
|
Remove the unused `apple_pay_domain` preference. Domain registration is
|
data/README.md
CHANGED
|
@@ -48,7 +48,8 @@ Then add a **PayPal** payment method in admin and set:
|
|
|
48
48
|
| `client_id` / `client_secret` | REST app credentials from the PayPal Developer Dashboard |
|
|
49
49
|
| `webhook_secret` | PayPal webhook ID (used as the verify-webhook `webhook_id`) |
|
|
50
50
|
| `test_mode` | Sandbox vs live |
|
|
51
|
-
| `enable_apple_pay` |
|
|
51
|
+
| `enable_apple_pay` | Offer Apple Pay on the storefront (default on) |
|
|
52
|
+
| `enable_card_fields` | Offer Card Fields on the storefront (default on) |
|
|
52
53
|
|
|
53
54
|
Webhook URL is the standard Spree v3 payments webhook:
|
|
54
55
|
|
|
@@ -42,13 +42,18 @@ module Spree
|
|
|
42
42
|
return nil if total.zero?
|
|
43
43
|
|
|
44
44
|
protect_from_error do
|
|
45
|
-
order_presenter = OrderPresenter.new(order)
|
|
45
|
+
order_presenter = OrderPresenter.new(order, amount: total)
|
|
46
46
|
paypal_response = client.orders.create_order(order_presenter.to_json)
|
|
47
47
|
|
|
48
48
|
session_data = paypal_response.data.as_json
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
session_data['
|
|
50
|
+
session_data['enable_apple_pay'] = apple_pay_enabled?
|
|
51
|
+
session_data['enable_card_fields'] = card_fields_enabled?
|
|
52
|
+
|
|
53
|
+
if card_fields_enabled?
|
|
54
|
+
client_token = generate_client_token
|
|
55
|
+
session_data['client_token'] = client_token if client_token.present?
|
|
56
|
+
end
|
|
52
57
|
|
|
53
58
|
payment_session_class.create!(
|
|
54
59
|
order: order,
|
|
@@ -96,6 +101,7 @@ module Spree
|
|
|
96
101
|
if fetched.data.status == 'COMPLETED'
|
|
97
102
|
fetched
|
|
98
103
|
else
|
|
104
|
+
ensure_amount_matches!(payment_session, fetched)
|
|
99
105
|
client.orders.capture_order(lookup)
|
|
100
106
|
end
|
|
101
107
|
|
|
@@ -161,6 +167,26 @@ module Spree
|
|
|
161
167
|
|
|
162
168
|
private
|
|
163
169
|
|
|
170
|
+
##
|
|
171
|
+
# Refuses to capture a PayPal order whose amount no longer matches what
|
|
172
|
+
# is due — e.g. a gift card was applied, or the cart changed, after the
|
|
173
|
+
# PayPal order was created. Capturing would take the stale amount; the
|
|
174
|
+
# storefront recreates the session instead.
|
|
175
|
+
#
|
|
176
|
+
# @raise [Spree::Core::GatewayError]
|
|
177
|
+
#
|
|
178
|
+
def ensure_amount_matches!(payment_session, fetched)
|
|
179
|
+
value = fetched.data.as_json.dig('purchase_units', 0, 'amount', 'value')
|
|
180
|
+
return if value.blank?
|
|
181
|
+
|
|
182
|
+
due = payment_session.order.total_minus_store_credits
|
|
183
|
+
return if value.to_d == due.to_d
|
|
184
|
+
|
|
185
|
+
payment_session.fail if payment_session.can_fail?
|
|
186
|
+
raise Spree::Core::GatewayError,
|
|
187
|
+
"PayPal order amount #{value} does not match the amount due #{due}"
|
|
188
|
+
end
|
|
189
|
+
|
|
164
190
|
def extract_order_id_from_webhook(event_type, resource)
|
|
165
191
|
case event_type
|
|
166
192
|
when /\ACHECKOUT\.ORDER\./
|
|
@@ -23,6 +23,7 @@ module Spree
|
|
|
23
23
|
preference :webhook_secret, :string
|
|
24
24
|
preference :test_mode, :boolean, default: true
|
|
25
25
|
preference :enable_apple_pay, :boolean, default: true
|
|
26
|
+
preference :enable_card_fields, :boolean, default: true
|
|
26
27
|
|
|
27
28
|
validates :preferred_client_id, :preferred_client_secret, presence: true
|
|
28
29
|
|
|
@@ -105,6 +106,15 @@ module Spree
|
|
|
105
106
|
preferred_enable_apple_pay
|
|
106
107
|
end
|
|
107
108
|
|
|
109
|
+
##
|
|
110
|
+
# Whether the storefront should offer Card Fields for this method.
|
|
111
|
+
#
|
|
112
|
+
# @return [TrueClass, FalseClass]
|
|
113
|
+
#
|
|
114
|
+
def card_fields_enabled?
|
|
115
|
+
preferred_enable_card_fields
|
|
116
|
+
end
|
|
117
|
+
|
|
108
118
|
##
|
|
109
119
|
# @return [String, NilClass]
|
|
110
120
|
#
|
|
@@ -279,6 +289,12 @@ module Spree
|
|
|
279
289
|
end
|
|
280
290
|
end
|
|
281
291
|
|
|
292
|
+
protected
|
|
293
|
+
|
|
294
|
+
def public_preference_keys
|
|
295
|
+
%i[enable_apple_pay enable_card_fields]
|
|
296
|
+
end
|
|
297
|
+
|
|
282
298
|
private
|
|
283
299
|
|
|
284
300
|
def find_order(order_id)
|
|
@@ -18,9 +18,13 @@ module Spree
|
|
|
18
18
|
|
|
19
19
|
##
|
|
20
20
|
# @param order [Spree::Order]
|
|
21
|
+
# @param amount [Numeric, NilClass] what PayPal should charge; defaults to
|
|
22
|
+
# the order total. Less than the total when a gift card or store credit
|
|
23
|
+
# pays part of the order.
|
|
21
24
|
#
|
|
22
|
-
def initialize(order)
|
|
25
|
+
def initialize(order, amount: nil)
|
|
23
26
|
@order = order
|
|
27
|
+
@amount = amount
|
|
24
28
|
end
|
|
25
29
|
|
|
26
30
|
attr_reader :order
|
|
@@ -41,15 +45,29 @@ module Spree
|
|
|
41
45
|
private
|
|
42
46
|
|
|
43
47
|
def purchase_unit
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
# A gift card or store credit pays part of the order, so the breakdown
|
|
49
|
+
# and items (which always sum to the full order total) can't describe
|
|
50
|
+
# what PayPal charges. PayPal accepts an amount on its own.
|
|
51
|
+
args =
|
|
52
|
+
if partial_charge?
|
|
53
|
+
{ amount: PaypalServerSdk::AmountWithBreakdown.new(currency_code: order.currency.upcase,
|
|
54
|
+
value: charge_amount.to_s) }
|
|
55
|
+
else
|
|
56
|
+
{ amount: amount_with_breakdown, items: items }
|
|
57
|
+
end
|
|
48
58
|
args[:shipping] = shipping_details if shipping_details
|
|
49
59
|
|
|
50
60
|
PaypalServerSdk::PurchaseUnitRequest.new(**args)
|
|
51
61
|
end
|
|
52
62
|
|
|
63
|
+
def charge_amount
|
|
64
|
+
@amount.presence || order.total
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def partial_charge?
|
|
68
|
+
charge_amount.to_d != order.total.to_d
|
|
69
|
+
end
|
|
70
|
+
|
|
53
71
|
# NOTE: the breakdown only sends additional (non-included) tax — sending
|
|
54
72
|
# the full tax_total double-counts VAT on tax-inclusive markets and
|
|
55
73
|
# trips AMOUNT_MISMATCH. Do not change to order.tax_total.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
module Api
|
|
5
|
+
module V3
|
|
6
|
+
##
|
|
7
|
+
# Exposes non-secret payment-method preferences to the storefront.
|
|
8
|
+
#
|
|
9
|
+
module PaymentMethodSerializerDecorator
|
|
10
|
+
def self.prepended(base)
|
|
11
|
+
base.attribute :public_preferences, &:public_preferences
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
Spree::Api::V3::PaymentMethodSerializer.prepend(
|
|
19
|
+
Spree::Api::V3::PaymentMethodSerializerDecorator
|
|
20
|
+
)
|
data/app/views/spree/admin/payment_methods/configuration_guides/_spree_paypal_platform.html.erb
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
<strong>Apple Pay</strong> is a funding source of this same PayPal method, not a second payment method.
|
|
12
12
|
Register every storefront domain that will show the Apple Pay button in the
|
|
13
13
|
<%= external_link_to 'PayPal Apple Pay domain registration', 'https://developer.paypal.com/docs/checkout/apm/apple-pay/', class: 'alert-link' %>
|
|
14
|
-
dashboard.
|
|
14
|
+
dashboard. Use the checkboxes below to offer Card Fields and Apple Pay
|
|
15
|
+
on the storefront. The Apple Pay button only renders in Safari over HTTPS.
|
|
15
16
|
</p>
|
|
16
17
|
</div>
|
|
@@ -9,7 +9,8 @@ FactoryBot.define do
|
|
|
9
9
|
client_id: ENV.fetch('PAYPAL_CLIENT_ID', 'client_id_test'),
|
|
10
10
|
client_secret: ENV.fetch('PAYPAL_CLIENT_SECRET', 'client_secret_test'),
|
|
11
11
|
test_mode: true,
|
|
12
|
-
enable_apple_pay: true
|
|
12
|
+
enable_apple_pay: true,
|
|
13
|
+
enable_card_fields: true
|
|
13
14
|
}
|
|
14
15
|
end
|
|
15
16
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spree-paypal_platform
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.1.
|
|
4
|
+
version: 5.1.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aypex
|
|
@@ -131,6 +131,7 @@ files:
|
|
|
131
131
|
- app/models/spree/paypal_platform/payment_sources/paypal.rb
|
|
132
132
|
- app/models/spree/paypal_platform/store_decorator.rb
|
|
133
133
|
- app/presenters/spree/paypal_platform/order_presenter.rb
|
|
134
|
+
- app/serializers/spree/api/v3/payment_method_serializer_decorator.rb
|
|
134
135
|
- app/services/spree/paypal_platform/capture_order.rb
|
|
135
136
|
- app/services/spree/paypal_platform/create_payment.rb
|
|
136
137
|
- app/services/spree/paypal_platform/create_source.rb
|
|
@@ -168,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
168
169
|
- !ruby/object:Gem::Version
|
|
169
170
|
version: '0'
|
|
170
171
|
requirements: []
|
|
171
|
-
rubygems_version: 4.0.
|
|
172
|
+
rubygems_version: 4.0.20
|
|
172
173
|
specification_version: 4
|
|
173
174
|
summary: PayPal Checkout for Spree, including Apple Pay and Card Fields
|
|
174
175
|
test_files: []
|