spree_paypal_checkout 0.7.0 → 0.7.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef8dfb2e6f13c0334979a7af8f62d21882bdb0e2a717e36328aec335194d8767
4
- data.tar.gz: d2f23cb259fb7f2c55b3c4af5fc98d48e9625f46ec239a351f9e6896f002302e
3
+ metadata.gz: bfb11878efce5e64446b6e83c9f16a69b64203ff47ce9ca601f1404b6f0ed017
4
+ data.tar.gz: 86705e3ed5b1c0314c2518c793edc022d5149e64a32d710919455b0e3ef4fe42
5
5
  SHA512:
6
- metadata.gz: 2bca77d66f83220e40a00cb7edbba2560948384c46a4033ac2b3e1dd92d7e08235f366f3f57c867e1a790539e3e48d521ca61fd1899f9283dd5ad9105c1ed4b2
7
- data.tar.gz: ec63da1f24b8a6885f8da03d4a7fcbd212eb5cfba45d1f42922dffe167549fd84625b97ab2349820d8bc484c5cf97b5423b842044dc34e5819f6d5e2815cff0d
6
+ metadata.gz: fb11160ee57ed3ab5275c2e32c8d13463b74149002ed2297cd95045f53dc3101c8a1f97fff3b8424c4d0ae6e5ba5003c623ad2d12d4226183dbb30aa8c5a4c95
7
+ data.tar.gz: 74a7e4a0cdd3f55e976bb948ee8bc8a9799b55ec076319c353720342ffb0b80e087f0229e1046e0fc6f4b74dae88ff019ad1231f6a8f127ddbdfff1cc73dba23
@@ -0,0 +1,22 @@
1
+ module Spree
2
+ class PaymentSetupSessions::PaypalCheckout < PaymentSetupSession
3
+ # PayPal's Vault API uses setup tokens (temporary) that get exchanged for
4
+ # permanent payment tokens once the buyer approves on PayPal.
5
+ def paypal_setup_token_id
6
+ external_id
7
+ end
8
+
9
+ def paypal_payment_token_id
10
+ external_data&.dig('payment_token', 'id')
11
+ end
12
+
13
+ # The approve link the storefront redirects the buyer to.
14
+ def approve_link
15
+ Array(external_data&.dig('links')).find { |l| l['rel'] == 'approve' }&.dig('href')
16
+ end
17
+
18
+ def successful?
19
+ status == 'completed'
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,116 @@
1
+ module SpreePaypalCheckout
2
+ class Gateway < ::Spree::Gateway
3
+ module PaymentSetupSessions
4
+ extend ActiveSupport::Concern
5
+
6
+ def setup_session_supported?
7
+ true
8
+ end
9
+
10
+ def payment_setup_session_class
11
+ Spree::PaymentSetupSessions::PaypalCheckout
12
+ end
13
+
14
+ # Creates a PayPal Vault setup token (temporary, awaiting buyer approval)
15
+ # and persists a Spree::PaymentSetupSessions::PaypalCheckout record.
16
+ #
17
+ # @param customer [Spree::User] the customer to vault the PayPal account for
18
+ # @param external_data [Hash] optional :return_url and :cancel_url for the
19
+ # PayPal redirect flow
20
+ # @return [Spree::PaymentSetupSessions::PaypalCheckout]
21
+ def create_payment_setup_session(customer:, external_data: {})
22
+ return_url = external_data[:return_url] || external_data['return_url'] || default_setup_return_url
23
+ cancel_url = external_data[:cancel_url] || external_data['cancel_url'] || default_setup_cancel_url
24
+
25
+ protect_from_error do
26
+ response = client.vault.create_setup_token(
27
+ SpreePaypalCheckout::SetupTokenPresenter.new(
28
+ customer: customer,
29
+ return_url: return_url,
30
+ cancel_url: cancel_url
31
+ ).to_h
32
+ )
33
+
34
+ payment_setup_session_class.create!(
35
+ customer: customer,
36
+ payment_method: self,
37
+ status: 'pending',
38
+ external_id: response.data.id,
39
+ external_data: response.data.as_json.merge(
40
+ 'return_url' => return_url,
41
+ 'cancel_url' => cancel_url
42
+ )
43
+ )
44
+ end
45
+ end
46
+
47
+ # Completes a setup session by exchanging the approved setup token for a
48
+ # permanent payment token, creates a Spree::PaymentSource representing the
49
+ # vaulted PayPal account, and transitions the session.
50
+ #
51
+ # @param setup_session [Spree::PaymentSetupSessions::PaypalCheckout]
52
+ # @param params [Hash] unused — PayPal needs no params beyond the setup_session
53
+ def complete_payment_setup_session(setup_session:, params: {})
54
+ protect_from_error do
55
+ response = client.vault.create_payment_token(
56
+ 'body' => {
57
+ 'payment_source' => {
58
+ 'token' => {
59
+ 'id' => setup_session.external_id,
60
+ 'type' => 'SETUP_TOKEN'
61
+ }
62
+ }
63
+ }
64
+ )
65
+
66
+ payment_token = response.data
67
+
68
+ setup_session.payment_source = build_payment_source_from_token(setup_session, payment_token)
69
+ setup_session.update!(
70
+ external_data: setup_session.external_data.to_h.merge('payment_token' => payment_token.as_json)
71
+ )
72
+ setup_session.complete if setup_session.can_complete?
73
+ rescue PaypalServerSdk::APIException => e
74
+ setup_session.fail if setup_session.can_fail?
75
+ raise Spree::Core::GatewayError, "PayPal Vault error: #{e.message}"
76
+ end
77
+
78
+ setup_session
79
+ end
80
+
81
+ private
82
+
83
+ def build_payment_source_from_token(setup_session, payment_token)
84
+ paypal = payment_token.as_json.dig('payment_source', 'paypal') || {}
85
+ account_id = paypal['email_address'] || payment_token.id
86
+
87
+ source = SpreePaypalCheckout::PaymentSources::Paypal.find_or_initialize_by(
88
+ payment_method: self,
89
+ gateway_payment_profile_id: payment_token.id
90
+ )
91
+ source.update!(
92
+ user: setup_session.customer,
93
+ email: paypal['email_address'],
94
+ name: [paypal.dig('name', 'given_name'), paypal.dig('name', 'surname')].compact.join(' ').strip.presence,
95
+ account_id: account_id,
96
+ account_status: paypal['account_status']
97
+ )
98
+ source
99
+ end
100
+
101
+ def default_setup_return_url
102
+ store = stores.first
103
+ return nil unless store
104
+
105
+ "#{store.storefront_url}/paypal/payment_setup_sessions/return"
106
+ end
107
+
108
+ def default_setup_cancel_url
109
+ store = stores.first
110
+ return nil unless store
111
+
112
+ "#{store.storefront_url}/paypal/payment_setup_sessions/cancel"
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,37 @@
1
+ module SpreePaypalCheckout
2
+ # Builds the body of a PayPal Vault /v3/vault/setup-tokens request for tokenizing
3
+ # a buyer's PayPal account without an immediate purchase.
4
+ class SetupTokenPresenter
5
+ def initialize(customer:, return_url:, cancel_url:)
6
+ @customer = customer
7
+ @return_url = return_url
8
+ @cancel_url = cancel_url
9
+ end
10
+
11
+ def to_h
12
+ {
13
+ 'body' => {
14
+ 'payment_source' => {
15
+ 'paypal' => {
16
+ 'usage_type' => 'MERCHANT',
17
+ 'experience_context' => {
18
+ 'return_url' => return_url,
19
+ 'cancel_url' => cancel_url
20
+ }
21
+ }
22
+ }
23
+ }.merge(customer_payload)
24
+ }
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :customer, :return_url, :cancel_url
30
+
31
+ def customer_payload
32
+ return {} unless customer&.id
33
+
34
+ { 'customer' => { 'id' => "customer_#{customer.id}" } }
35
+ end
36
+ end
37
+ end
@@ -1,6 +1,7 @@
1
1
  <div class="alert alert-info">
2
2
  <p class="mb-0">
3
3
  To find your <strong>Client ID</strong> and <strong>Client Secret</strong>, go to the
4
- <%= external_link_to 'PayPal dashboard', 'https://www.paypal.com/mep/merchantapps/setup/checkout/apicredentials', class: 'alert-link' %>
4
+ <%= external_link_to 'PayPal Developer Dashboard', 'https://developer.paypal.com/dashboard/', class: 'alert-link' %>.
5
+ For more information, see the <%= external_link_to 'PayPal REST API documentation', 'https://developer.paypal.com/api/rest/', class: 'alert-link' %>.
5
6
  </p>
6
7
  </div>
@@ -1,5 +1,5 @@
1
1
  module SpreePaypalCheckout
2
- VERSION = '0.7.0'.freeze
2
+ VERSION = '0.7.1'.freeze
3
3
 
4
4
  def gem_version
5
5
  Gem::Version.new(VERSION)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_paypal_checkout
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vendo Connect Inc.
@@ -121,15 +121,18 @@ files:
121
121
  - app/javascript/spree_paypal_checkout/application.js
122
122
  - app/javascript/spree_paypal_checkout/controllers/checkout_paypal_controller.js
123
123
  - app/models/spree/payment_sessions/paypal_checkout.rb
124
+ - app/models/spree/payment_setup_sessions/paypal_checkout.rb
124
125
  - app/models/spree_paypal_checkout/base.rb
125
126
  - app/models/spree_paypal_checkout/gateway.rb
126
127
  - app/models/spree_paypal_checkout/gateway/payment_sessions.rb
128
+ - app/models/spree_paypal_checkout/gateway/payment_setup_sessions.rb
127
129
  - app/models/spree_paypal_checkout/order.rb
128
130
  - app/models/spree_paypal_checkout/order_decorator.rb
129
131
  - app/models/spree_paypal_checkout/payment_method_decorator.rb
130
132
  - app/models/spree_paypal_checkout/payment_sources/paypal.rb
131
133
  - app/models/spree_paypal_checkout/store_decorator.rb
132
134
  - app/presenters/spree_paypal_checkout/order_presenter.rb
135
+ - app/presenters/spree_paypal_checkout/setup_token_presenter.rb
133
136
  - app/services/spree_paypal_checkout/capture_order.rb
134
137
  - app/services/spree_paypal_checkout/create_payment.rb
135
138
  - app/services/spree_paypal_checkout/create_source.rb
@@ -157,9 +160,9 @@ licenses:
157
160
  - MIT
158
161
  metadata:
159
162
  bug_tracker_uri: https://github.com/spree/spree_paypal_checkout/issues
160
- changelog_uri: https://github.com/spree/spree_paypal_checkout/releases/tag/v0.7.0
163
+ changelog_uri: https://github.com/spree/spree_paypal_checkout/releases/tag/v0.7.1
161
164
  documentation_uri: https://docs.spreecommerce.org/
162
- source_code_uri: https://github.com/spree/spree_paypal_checkout/tree/v0.7.0
165
+ source_code_uri: https://github.com/spree/spree_paypal_checkout/tree/v0.7.1
163
166
  rdoc_options: []
164
167
  require_paths:
165
168
  - lib