spree_stripe 1.5.1 → 1.6.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.
- checksums.yaml +4 -4
- data/Rakefile +2 -0
- data/app/jobs/spree_stripe/complete_order_from_session_job.rb +12 -0
- data/app/models/spree/payment_sessions/stripe.rb +54 -0
- data/app/models/spree_stripe/custom_domain_decorator.rb +3 -1
- data/app/models/spree_stripe/gateway/payment_intents.rb +138 -0
- data/app/models/spree_stripe/gateway/payment_sessions.rb +177 -0
- data/app/models/spree_stripe/gateway.rb +53 -126
- data/app/presenters/spree_stripe/statement_descriptor_suffix_presenter.rb +1 -1
- data/app/services/spree_stripe/create_payment_intent.rb +1 -2
- data/app/services/spree_stripe/create_source.rb +1 -1
- data/app/services/spree_stripe/register_domain.rb +1 -1
- data/app/services/spree_stripe/webhook_handlers/payment_intent_payment_failed.rb +13 -1
- data/app/services/spree_stripe/webhook_handlers/payment_intent_succeeded.rb +11 -2
- data/config/routes.rb +11 -9
- data/lib/spree_stripe/configuration.rb +2 -0
- data/lib/spree_stripe/engine.rb +13 -2
- data/lib/spree_stripe/testing_support/factories/stripe_payment_session_factory.rb +33 -0
- data/lib/spree_stripe/version.rb +1 -1
- metadata +34 -29
- /data/{app/serializers → lib/spree_api_v2}/spree/api/v2/platform/affirm_serializer.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree/api/v2/platform/after_pay_serializer.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree/api/v2/platform/alipay_serializer.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree/api/v2/platform/ideal_serializer.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree/api/v2/platform/klarna_serializer.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree/api/v2/platform/link_serializer.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree/api/v2/platform/przelewy24_serializer.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree/api/v2/platform/sepa_debit_serializer.rb +0 -0
- /data/{app/controllers → lib/spree_api_v2}/spree/api/v2/storefront/stripe/base_controller.rb +0 -0
- /data/{app/controllers → lib/spree_api_v2}/spree/api/v2/storefront/stripe/payment_intents_controller.rb +0 -0
- /data/{app/controllers → lib/spree_api_v2}/spree/api/v2/storefront/stripe/setup_intents_controller.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree/v2/storefront/affirm_serializer.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree/v2/storefront/after_pay_serializer.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree/v2/storefront/alipay_serializer.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree/v2/storefront/ideal_serializer.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree/v2/storefront/klarna_serializer.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree/v2/storefront/link_serializer.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree/v2/storefront/payment_intent_serializer.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree/v2/storefront/przelewy24_serializer.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree/v2/storefront/sepa_debit_serializer.rb +0 -0
- /data/{app/serializers → lib/spree_api_v2}/spree_stripe/v2/storefront/payment_method_serializer_decorator.rb +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1b3cc636d33d9aa8152aba43d60a15a1d3c44dca2feeafcd30c215cec823c028
|
|
4
|
+
data.tar.gz: 82cf337690f6c7f03d4770e2001e4d4dc3a273d4b84887f079e664a02788edd6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0ddd1b76f5c7991a636f78751fc43c8bc5eac6b952a91621ad928eaa66823f0b2bfa42a21ba7ba2942934c9146eb362841ad8ef7cc74f63b1422f8a2fbdf23ec
|
|
7
|
+
data.tar.gz: 9b9d768391f1e41ff1444dcf9f6faad7bab6c73c25e9712f0628171d770c3d00ef9b71d38f8be1ab471a21e979e9d5d8bd03e2b3cb8d0c68623e4d717d0f87b1
|
data/Rakefile
CHANGED
|
@@ -21,5 +21,7 @@ task :test_app do
|
|
|
21
21
|
install_storefront: true,
|
|
22
22
|
install_admin: true
|
|
23
23
|
)
|
|
24
|
+
system({ 'BUNDLE_GEMFILE' => File.expand_path('Gemfile', __dir__) }, 'rails g spree_posts:install')
|
|
24
25
|
system({ 'BUNDLE_GEMFILE' => File.expand_path('Gemfile', __dir__) }, 'rails g spree_legacy_api_v2:install')
|
|
26
|
+
system({ 'BUNDLE_GEMFILE' => File.expand_path('Gemfile', __dir__) }, 'rails g spree_legacy_product_properties:install')
|
|
25
27
|
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module SpreeStripe
|
|
2
|
+
class CompleteOrderFromSessionJob < BaseJob
|
|
3
|
+
def perform(payment_session_id)
|
|
4
|
+
payment_session = Spree::PaymentSessions::Stripe.find(payment_session_id)
|
|
5
|
+
|
|
6
|
+
# PaymentSessions::Stripe duck-types as PaymentIntent
|
|
7
|
+
SpreeStripe::CompleteOrder.new(payment_intent: payment_session).call
|
|
8
|
+
|
|
9
|
+
payment_session.complete unless payment_session.completed?
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class PaymentSessions::Stripe < PaymentSession
|
|
3
|
+
delegate :api_options, to: :payment_method
|
|
4
|
+
|
|
5
|
+
# Duck-type interface matching SpreeStripe::PaymentIntent
|
|
6
|
+
# This allows reuse of CompleteOrder and CreatePayment services
|
|
7
|
+
def stripe_id
|
|
8
|
+
external_id
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def client_secret
|
|
12
|
+
external_data&.dig('client_secret')
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def ephemeral_key_secret
|
|
16
|
+
external_data&.dig('ephemeral_key_secret')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def stripe_payment_intent
|
|
20
|
+
@stripe_payment_intent ||= payment_method.retrieve_payment_intent(external_id)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def stripe_charge
|
|
24
|
+
@stripe_charge ||= begin
|
|
25
|
+
latest_charge = stripe_payment_intent.latest_charge
|
|
26
|
+
latest_charge.present? ? payment_method.retrieve_charge(latest_charge) : nil
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def accepted?
|
|
31
|
+
payment_method.payment_intent_accepted?(stripe_payment_intent)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def successful?
|
|
35
|
+
stripe_payment_intent.status == 'succeeded'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def charge_not_required?
|
|
39
|
+
payment_method.payment_intent_charge_not_required?(stripe_payment_intent)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def find_or_create_payment!(metadata = {})
|
|
43
|
+
return unless persisted?
|
|
44
|
+
return payment if payment.present?
|
|
45
|
+
|
|
46
|
+
SpreeStripe::CreatePayment.new(
|
|
47
|
+
order: order,
|
|
48
|
+
payment_intent: self,
|
|
49
|
+
gateway: payment_method,
|
|
50
|
+
amount: amount
|
|
51
|
+
).call
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
module SpreeStripe
|
|
2
|
+
class Gateway < ::Spree::Gateway
|
|
3
|
+
module PaymentIntents
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES = %w[sepa_debit us_bank_account].freeze
|
|
7
|
+
BANK_PAYMENT_METHOD_TYPES = %w[customer_balance us_bank_account].freeze
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
has_many :payment_intents, class_name: 'SpreeStripe::PaymentIntent', foreign_key: 'payment_method_id', dependent: :delete_all
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def payment_intent_accepted?(payment_intent)
|
|
14
|
+
payment_intent.status.in?(payment_intent_accepted_statuses(payment_intent))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def payment_intent_delayed_notification?(payment_intent)
|
|
18
|
+
payment_method = payment_intent.payment_method
|
|
19
|
+
return false unless payment_method.respond_to?(:type)
|
|
20
|
+
|
|
21
|
+
payment_intent.payment_method.type.in?(DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def payment_intent_charge_not_required?(payment_intent)
|
|
25
|
+
payment_intent_bank_payment_method?(payment_intent)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def payment_intent_bank_payment_method?(payment_intent)
|
|
29
|
+
payment_method = payment_intent.payment_method
|
|
30
|
+
return false unless payment_method.respond_to?(:type)
|
|
31
|
+
|
|
32
|
+
payment_intent.payment_method.type.in?(BANK_PAYMENT_METHOD_TYPES)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Creates a Stripe payment intent for the order
|
|
36
|
+
#
|
|
37
|
+
# @param amount_in_cents [Integer] the amount in cents
|
|
38
|
+
# @param order [Spree::Order] the order to create a payment intent for
|
|
39
|
+
# @param payment_method_id [String] Stripe payment method id to use, eg. a card token
|
|
40
|
+
# @param off_session [Boolean] whether the payment intent is off session
|
|
41
|
+
# @param customer_profile_id [String] Stripe customer profile id to use, eg. cus_123
|
|
42
|
+
# @return [Spree::PaymentResponse] the response from the payment intent creation
|
|
43
|
+
def create_payment_intent(amount_in_cents, order, payment_method_id: nil, off_session: false, customer_profile_id: nil)
|
|
44
|
+
payload = SpreeStripe::PaymentIntentPresenter.new(
|
|
45
|
+
amount: amount_in_cents,
|
|
46
|
+
order: order,
|
|
47
|
+
customer: customer_profile_id || fetch_or_create_customer(order: order)&.profile_id,
|
|
48
|
+
payment_method_id: payment_method_id,
|
|
49
|
+
off_session: off_session
|
|
50
|
+
).call
|
|
51
|
+
|
|
52
|
+
protect_from_error do
|
|
53
|
+
response = send_request { |opts| Stripe::PaymentIntent.create(payload, opts) }
|
|
54
|
+
|
|
55
|
+
success(response.id, response)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Updates a Stripe payment intent for the order
|
|
60
|
+
#
|
|
61
|
+
# @param payment_intent_id [String] Stripe payment intent id
|
|
62
|
+
# @param amount_in_cents [Integer] the amount in cents
|
|
63
|
+
# @param order [Spree::Order] the order to update the payment intent for
|
|
64
|
+
# @param payment_method_id [String] Stripe payment method id to use, eg. a card token
|
|
65
|
+
# @return [Spree::PaymentResponse] the response from the payment intent update
|
|
66
|
+
def update_payment_intent(payment_intent_id, amount_in_cents, order, payment_method_id = nil)
|
|
67
|
+
protect_from_error do
|
|
68
|
+
payload = SpreeStripe::PaymentIntentPresenter.new(
|
|
69
|
+
amount: amount_in_cents,
|
|
70
|
+
order: order,
|
|
71
|
+
customer: fetch_or_create_customer(order: order)&.profile_id,
|
|
72
|
+
payment_method_id: payment_method_id
|
|
73
|
+
).call.slice(:amount, :currency, :payment_method, :shipping, :customer)
|
|
74
|
+
|
|
75
|
+
response = send_request { |opts| Stripe::PaymentIntent.update(payment_intent_id, payload, opts) }
|
|
76
|
+
|
|
77
|
+
success(response.id, response)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def retrieve_payment_intent(payment_intent_id)
|
|
82
|
+
send_request { |opts| Stripe::PaymentIntent.retrieve({ id: payment_intent_id, expand: ['payment_method'] }, opts) }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def confirm_payment_intent(payment_intent_id)
|
|
86
|
+
send_request { |opts| Stripe::PaymentIntent.confirm(payment_intent_id, {}, opts) }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def capture_payment_intent(payment_intent_id, amount_in_cents)
|
|
90
|
+
send_request { |opts| Stripe::PaymentIntent.capture(payment_intent_id, { amount_to_capture: amount_in_cents }, opts) }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Cancels a Stripe payment intent
|
|
94
|
+
#
|
|
95
|
+
# @param payment_intent_id [String] Stripe payment intent ID, eg. pi_123
|
|
96
|
+
def cancel_payment_intent(payment_intent_id)
|
|
97
|
+
send_request { |opts| Stripe::PaymentIntent.cancel(payment_intent_id, {}, opts) }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Ensures a Stripe payment intent exists for Spree payment
|
|
101
|
+
#
|
|
102
|
+
# @param payment [Spree::Payment] the payment to ensure a payment intent exists for
|
|
103
|
+
# @param amount_in_cents [Integer] the amount in cents
|
|
104
|
+
# @param payment_source [Spree::CreditCard | Spree::PaymentSource] the payment source to use
|
|
105
|
+
# @return [Spree::Payment] the payment with the payment intent
|
|
106
|
+
def ensure_payment_intent_exists_for_payment(payment, amount_in_cents = nil, payment_source = nil)
|
|
107
|
+
return payment if payment.response_code.present?
|
|
108
|
+
|
|
109
|
+
amount_in_cents ||= payment.display_amount.cents
|
|
110
|
+
payment_source ||= payment.source
|
|
111
|
+
|
|
112
|
+
response = create_payment_intent(
|
|
113
|
+
amount_in_cents,
|
|
114
|
+
payment.order,
|
|
115
|
+
payment_method_id: payment_source.gateway_payment_profile_id,
|
|
116
|
+
off_session: true,
|
|
117
|
+
customer_profile_id: payment_source.gateway_customer_profile_id
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
payment.update_columns(
|
|
121
|
+
response_code: response.authorization,
|
|
122
|
+
updated_at: Time.current
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
payment
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
def payment_intent_accepted_statuses(payment_intent)
|
|
131
|
+
statuses = %w[succeeded]
|
|
132
|
+
statuses << 'processing' if payment_intent_delayed_notification?(payment_intent)
|
|
133
|
+
statuses << 'requires_action' if payment_intent_charge_not_required?(payment_intent)
|
|
134
|
+
statuses
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
module SpreeStripe
|
|
2
|
+
class Gateway < ::Spree::Gateway
|
|
3
|
+
module PaymentSessions
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
def session_required?
|
|
7
|
+
!SpreeStripe::Config[:use_legacy_payment_intents]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def payment_session_class
|
|
11
|
+
Spree::PaymentSessions::Stripe
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create_payment_session(order:, amount: nil, external_data: {})
|
|
15
|
+
total = amount.presence || order.total_minus_store_credits
|
|
16
|
+
amount_in_cents = Spree::Money.new(total, currency: order.currency).cents
|
|
17
|
+
|
|
18
|
+
return nil if amount_in_cents.zero?
|
|
19
|
+
|
|
20
|
+
customer = fetch_or_create_customer(order: order)
|
|
21
|
+
stripe_pm_id = external_data[:stripe_payment_method_id] || external_data['stripe_payment_method_id']
|
|
22
|
+
|
|
23
|
+
response = create_payment_intent(
|
|
24
|
+
amount_in_cents, order,
|
|
25
|
+
payment_method_id: stripe_pm_id,
|
|
26
|
+
customer_profile_id: customer&.profile_id
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
ephemeral_key_response = create_ephemeral_key(customer.profile_id) if customer.present?
|
|
30
|
+
ephemeral_key_secret = ephemeral_key_response&.params&.dig('secret')
|
|
31
|
+
|
|
32
|
+
payment_session_class.create!(
|
|
33
|
+
order: order,
|
|
34
|
+
payment_method: self,
|
|
35
|
+
amount: total,
|
|
36
|
+
currency: order.currency,
|
|
37
|
+
status: 'pending',
|
|
38
|
+
external_id: response.authorization,
|
|
39
|
+
customer: order.user,
|
|
40
|
+
customer_external_id: customer&.profile_id,
|
|
41
|
+
external_data: {
|
|
42
|
+
'client_secret' => response.params['client_secret'],
|
|
43
|
+
'ephemeral_key_secret' => ephemeral_key_secret,
|
|
44
|
+
'stripe_payment_method_id' => stripe_pm_id
|
|
45
|
+
}.compact
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def update_payment_session(payment_session:, amount: nil, external_data: {})
|
|
50
|
+
attrs = {}
|
|
51
|
+
amount_in_cents = nil
|
|
52
|
+
|
|
53
|
+
if amount.present?
|
|
54
|
+
attrs[:amount] = amount
|
|
55
|
+
amount_in_cents = Spree::Money.new(amount, currency: payment_session.currency).cents
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
stripe_pm_id = external_data[:stripe_payment_method_id] || external_data['stripe_payment_method_id']
|
|
59
|
+
|
|
60
|
+
update_payment_intent(
|
|
61
|
+
payment_session.external_id,
|
|
62
|
+
amount_in_cents || payment_session.amount_in_cents,
|
|
63
|
+
payment_session.order,
|
|
64
|
+
stripe_pm_id
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if external_data.present?
|
|
68
|
+
attrs[:external_data] = (payment_session.external_data || {}).merge(external_data.stringify_keys)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
payment_session.update!(attrs) if attrs.any?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Completes a payment session by verifying with Stripe, creating the
|
|
75
|
+
# Payment record, and patching wallet address data.
|
|
76
|
+
#
|
|
77
|
+
# Does NOT complete the order — that is handled by Carts::Complete
|
|
78
|
+
# (called by the storefront or by the webhook handler).
|
|
79
|
+
def complete_payment_session(payment_session:, params: {})
|
|
80
|
+
stripe_pi = retrieve_payment_intent(payment_session.external_id)
|
|
81
|
+
|
|
82
|
+
if payment_intent_accepted?(stripe_pi)
|
|
83
|
+
payment_session.process if payment_session.can_process?
|
|
84
|
+
|
|
85
|
+
charge = payment_session.stripe_charge
|
|
86
|
+
|
|
87
|
+
# Patch wallet billing address (Apple Pay, Google Pay)
|
|
88
|
+
patch_wallet_address(payment_session.order, charge) if charge.present?
|
|
89
|
+
|
|
90
|
+
# Create the Payment record
|
|
91
|
+
payment_session.find_or_create_payment!
|
|
92
|
+
|
|
93
|
+
# Process the payment state
|
|
94
|
+
payment = payment_session.payment
|
|
95
|
+
if payment.present? && !payment.completed?
|
|
96
|
+
if payment_intent_successful?(stripe_pi)
|
|
97
|
+
payment.process!
|
|
98
|
+
else
|
|
99
|
+
payment.authorize!
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
payment_session.complete unless payment_session.completed?
|
|
104
|
+
else
|
|
105
|
+
payment_session.fail if payment_session.can_fail?
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
private
|
|
110
|
+
|
|
111
|
+
def payment_intent_successful?(stripe_pi)
|
|
112
|
+
stripe_pi.status == 'succeeded'
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Patches the order's billing address with data from the Stripe charge.
|
|
116
|
+
# Needed for quick checkout (Apple Pay/Google Pay) where the storefront
|
|
117
|
+
# doesn't have the billing address before payment confirmation.
|
|
118
|
+
def patch_wallet_address(order, charge)
|
|
119
|
+
return if charge.blank?
|
|
120
|
+
|
|
121
|
+
billing_details = charge.billing_details
|
|
122
|
+
address = billing_details.address
|
|
123
|
+
|
|
124
|
+
order.email ||= billing_details.email
|
|
125
|
+
order.save! if order.email_changed?
|
|
126
|
+
|
|
127
|
+
# Skip if billing address is already valid
|
|
128
|
+
return if order.bill_address.present? && order.bill_address.valid?
|
|
129
|
+
|
|
130
|
+
country_iso = address.country
|
|
131
|
+
country = Spree::Country.find_by(iso: country_iso) || Spree::Country.default
|
|
132
|
+
|
|
133
|
+
order.bill_address ||= Spree::Address.new(country: country, user: order.user)
|
|
134
|
+
order.bill_address.quick_checkout = true
|
|
135
|
+
|
|
136
|
+
first_name = billing_details.name&.split(' ')&.first || order.ship_address&.first_name || order.user&.first_name
|
|
137
|
+
last_name = billing_details.name&.split(' ')&.last || order.ship_address&.last_name || order.user&.last_name
|
|
138
|
+
|
|
139
|
+
order.bill_address.first_name ||= first_name
|
|
140
|
+
order.bill_address.last_name ||= last_name
|
|
141
|
+
order.bill_address.phone ||= billing_details.phone
|
|
142
|
+
order.bill_address.address1 ||= address.line1
|
|
143
|
+
order.bill_address.address2 ||= address.line2
|
|
144
|
+
order.bill_address.city ||= address.city
|
|
145
|
+
order.bill_address.zipcode ||= address.postal_code
|
|
146
|
+
|
|
147
|
+
state_name = address.state
|
|
148
|
+
if country.states_required?
|
|
149
|
+
order.bill_address.state = country.states.find_all_by_name_or_abbr(state_name)&.first
|
|
150
|
+
else
|
|
151
|
+
order.bill_address.state_name = state_name
|
|
152
|
+
end
|
|
153
|
+
order.bill_address.state_name ||= state_name
|
|
154
|
+
|
|
155
|
+
if order.bill_address.invalid?
|
|
156
|
+
return if order.ship_address.blank?
|
|
157
|
+
|
|
158
|
+
order.bill_address = order.ship_address
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
order.bill_address.save! if order.bill_address&.changed?
|
|
162
|
+
order.save!
|
|
163
|
+
|
|
164
|
+
copy_bill_info_to_user(order) if order.user.present?
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def copy_bill_info_to_user(order)
|
|
168
|
+
user = order.user
|
|
169
|
+
user.first_name ||= order.bill_address.first_name
|
|
170
|
+
user.last_name ||= order.bill_address.last_name
|
|
171
|
+
user.phone ||= order.bill_address.phone
|
|
172
|
+
user.bill_address_id ||= order.bill_address.id
|
|
173
|
+
user.save! if user.changed?
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
module SpreeStripe
|
|
2
2
|
class Gateway < ::Spree::Gateway
|
|
3
|
+
include SpreeStripe::Gateway::PaymentIntents
|
|
4
|
+
include SpreeStripe::Gateway::PaymentSessions
|
|
3
5
|
include SpreeStripe::Gateway::Tax if defined?(SpreeStripe::Gateway::Tax)
|
|
4
6
|
|
|
5
|
-
DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES = %w[sepa_debit us_bank_account].freeze
|
|
6
|
-
BANK_PAYMENT_METHOD_TYPES = %w[customer_balance us_bank_account].freeze
|
|
7
|
-
|
|
8
7
|
preference :publishable_key, :password
|
|
9
8
|
preference :secret_key, :password
|
|
10
9
|
|
|
@@ -16,36 +15,38 @@ module SpreeStripe
|
|
|
16
15
|
after_commit :create_webhook_endpoint_async, on: %i[create update]
|
|
17
16
|
after_commit :register_domain, on: :create
|
|
18
17
|
|
|
19
|
-
has_many :payment_intents, class_name: 'SpreeStripe::PaymentIntent', foreign_key: 'payment_method_id', dependent: :delete_all
|
|
20
|
-
|
|
21
18
|
def webhook_url
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def provider_class
|
|
26
|
-
self.class
|
|
27
|
-
end
|
|
19
|
+
store = stores.first
|
|
20
|
+
return nil unless store
|
|
28
21
|
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
if SpreeStripe::Config[:use_legacy_webhook_handlers]
|
|
23
|
+
StripeEvent::Engine.routes.url_helpers.root_url(host: store.url, protocol: 'https')
|
|
24
|
+
else
|
|
25
|
+
"#{store.formatted_url}/api/v3/webhooks/payments/#{prefixed_id}"
|
|
26
|
+
end
|
|
31
27
|
end
|
|
32
28
|
|
|
33
|
-
def
|
|
34
|
-
|
|
35
|
-
return false unless payment_method.respond_to?(:type)
|
|
36
|
-
|
|
37
|
-
payment_intent.payment_method.type.in?(DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES)
|
|
38
|
-
end
|
|
29
|
+
def parse_webhook_event(raw_body, headers)
|
|
30
|
+
event = verify_webhook_signature(raw_body, headers)
|
|
39
31
|
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
payment_session = Spree::PaymentSessions::Stripe.find_by(
|
|
33
|
+
payment_method: self,
|
|
34
|
+
external_id: event.data.object[:id]
|
|
35
|
+
)
|
|
36
|
+
return nil unless payment_session
|
|
37
|
+
|
|
38
|
+
case event.type
|
|
39
|
+
when 'payment_intent.succeeded'
|
|
40
|
+
{ action: :captured, payment_session: payment_session, metadata: { stripe_event: event } }
|
|
41
|
+
when 'payment_intent.payment_failed'
|
|
42
|
+
{ action: :failed, payment_session: payment_session, metadata: { stripe_event: event } }
|
|
43
|
+
else
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
42
46
|
end
|
|
43
47
|
|
|
44
|
-
def
|
|
45
|
-
|
|
46
|
-
return false unless payment_method.respond_to?(:type)
|
|
47
|
-
|
|
48
|
-
payment_intent.payment_method.type.in?(BANK_PAYMENT_METHOD_TYPES)
|
|
48
|
+
def provider_class
|
|
49
|
+
self.class
|
|
49
50
|
end
|
|
50
51
|
|
|
51
52
|
# @param amount_in_cents [Integer] the amount in cents to capture
|
|
@@ -188,99 +189,6 @@ module SpreeStripe
|
|
|
188
189
|
send_request { |opts| Stripe::Customer.update(customer.profile_id, payload, opts) }
|
|
189
190
|
end
|
|
190
191
|
|
|
191
|
-
# Creates a Stripe payment intent for the order
|
|
192
|
-
#
|
|
193
|
-
# @param amount_in_cents [Integer] the amount in cents
|
|
194
|
-
# @param order [Spree::Order] the order to create a payment intent for
|
|
195
|
-
# @param payment_method_id [String] Stripe payment method id to use, eg. a card token
|
|
196
|
-
# @param off_session [Boolean] whether the payment intent is off session
|
|
197
|
-
# @param customer_profile_id [String] Stripe customer profile id to use, eg. cus_123
|
|
198
|
-
# @return [ActiveMerchant::Billing::Response] the response from the payment intent creation
|
|
199
|
-
def create_payment_intent(amount_in_cents, order, payment_method_id: nil, off_session: false, customer_profile_id: nil)
|
|
200
|
-
payload = SpreeStripe::PaymentIntentPresenter.new(
|
|
201
|
-
amount: amount_in_cents,
|
|
202
|
-
order: order,
|
|
203
|
-
customer: customer_profile_id || fetch_or_create_customer(order: order)&.profile_id,
|
|
204
|
-
payment_method_id: payment_method_id,
|
|
205
|
-
off_session: off_session
|
|
206
|
-
).call
|
|
207
|
-
|
|
208
|
-
protect_from_error do
|
|
209
|
-
response = send_request { |opts| Stripe::PaymentIntent.create(payload, opts) }
|
|
210
|
-
|
|
211
|
-
success(response.id, response)
|
|
212
|
-
end
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
# Updates a Stripe payment intent for the order
|
|
216
|
-
#
|
|
217
|
-
# @param payment_intent_id [String] Stripe payment intent id
|
|
218
|
-
# @param amount_in_cents [Integer] the amount in cents
|
|
219
|
-
# @param order [Spree::Order] the order to update the payment intent for
|
|
220
|
-
# @param payment_method_id [String] Stripe payment method id to use, eg. a card token
|
|
221
|
-
# @return [ActiveMerchant::Billing::Response] the response from the payment intent update
|
|
222
|
-
def update_payment_intent(payment_intent_id, amount_in_cents, order, payment_method_id = nil)
|
|
223
|
-
protect_from_error do
|
|
224
|
-
payload = SpreeStripe::PaymentIntentPresenter.new(
|
|
225
|
-
amount: amount_in_cents,
|
|
226
|
-
order: order,
|
|
227
|
-
customer: fetch_or_create_customer(order: order)&.profile_id,
|
|
228
|
-
payment_method_id: payment_method_id
|
|
229
|
-
).call.slice(:amount, :currency, :payment_method, :shipping, :customer)
|
|
230
|
-
|
|
231
|
-
response = send_request { |opts| Stripe::PaymentIntent.update(payment_intent_id, payload, opts) }
|
|
232
|
-
|
|
233
|
-
success(response.id, response)
|
|
234
|
-
end
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
def retrieve_payment_intent(payment_intent_id)
|
|
238
|
-
send_request { |opts| Stripe::PaymentIntent.retrieve({ id: payment_intent_id, expand: ['payment_method'] }, opts) }
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
def confirm_payment_intent(payment_intent_id)
|
|
242
|
-
send_request { |opts| Stripe::PaymentIntent.confirm(payment_intent_id, {}, opts) }
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
def capture_payment_intent(payment_intent_id, amount_in_cents)
|
|
246
|
-
send_request { |opts| Stripe::PaymentIntent.capture(payment_intent_id, { amount_to_capture: amount_in_cents }, opts) }
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
# Cancels a Stripe payment intent
|
|
250
|
-
#
|
|
251
|
-
# @param payment_intent_id [String] Stripe payment intent ID, eg. pi_123
|
|
252
|
-
def cancel_payment_intent(payment_intent_id)
|
|
253
|
-
send_request { |opts| Stripe::PaymentIntent.cancel(payment_intent_id, {}, opts) }
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
# Ensures a Stripe payment intent exists for Spree payment
|
|
257
|
-
#
|
|
258
|
-
# @param payment [Spree::Payment] the payment to ensure a payment intent exists for
|
|
259
|
-
# @param amount_in_cents [Integer] the amount in cents
|
|
260
|
-
# @param payment_source [Spree::CreditCard | Spree::PaymentSource] the payment source to use
|
|
261
|
-
# @return [Spree::Payment] the payment with the payment intent
|
|
262
|
-
def ensure_payment_intent_exists_for_payment(payment, amount_in_cents = nil, payment_source = nil)
|
|
263
|
-
return payment if payment.response_code.present?
|
|
264
|
-
|
|
265
|
-
amount_in_cents ||= payment.display_amount.cents
|
|
266
|
-
payment_source ||= payment.source
|
|
267
|
-
|
|
268
|
-
response = create_payment_intent(
|
|
269
|
-
amount_in_cents,
|
|
270
|
-
payment.order,
|
|
271
|
-
payment_method_id: payment_source.gateway_payment_profile_id,
|
|
272
|
-
off_session: true,
|
|
273
|
-
customer_profile_id: payment_source.gateway_customer_profile_id
|
|
274
|
-
)
|
|
275
|
-
|
|
276
|
-
payment.update_columns(
|
|
277
|
-
response_code: response.authorization,
|
|
278
|
-
updated_at: Time.current
|
|
279
|
-
)
|
|
280
|
-
|
|
281
|
-
payment
|
|
282
|
-
end
|
|
283
|
-
|
|
284
192
|
def retrieve_charge(charge_id)
|
|
285
193
|
send_request { |opts| Stripe::Charge.retrieve(charge_id, opts) }
|
|
286
194
|
end
|
|
@@ -407,11 +315,11 @@ module SpreeStripe
|
|
|
407
315
|
end
|
|
408
316
|
|
|
409
317
|
def success(authorization, full_response)
|
|
410
|
-
|
|
318
|
+
Spree::PaymentResponse.new(true, nil, full_response.as_json, authorization: authorization)
|
|
411
319
|
end
|
|
412
320
|
|
|
413
321
|
def failure(error = nil)
|
|
414
|
-
|
|
322
|
+
Spree::PaymentResponse.new(false, error)
|
|
415
323
|
end
|
|
416
324
|
|
|
417
325
|
def protect_from_error
|
|
@@ -430,6 +338,8 @@ module SpreeStripe
|
|
|
430
338
|
stores.each do |store|
|
|
431
339
|
RegisterDomainJob.perform_later(store.id, 'store')
|
|
432
340
|
|
|
341
|
+
next unless defined?(Spree::CustomDomain)
|
|
342
|
+
|
|
433
343
|
store.custom_domains.each do |custom_domain|
|
|
434
344
|
RegisterDomainJob.perform_later(custom_domain.id, 'custom_domain')
|
|
435
345
|
end
|
|
@@ -445,11 +355,28 @@ module SpreeStripe
|
|
|
445
355
|
SpreeStripe::CustomerPresenter.new(name: name, email: email, address: address).call
|
|
446
356
|
end
|
|
447
357
|
|
|
448
|
-
def
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
358
|
+
def verify_webhook_signature(raw_body, headers)
|
|
359
|
+
signature = headers['HTTP_STRIPE_SIGNATURE']
|
|
360
|
+
signing_secrets = webhook_signing_secrets
|
|
361
|
+
|
|
362
|
+
signing_secrets.each do |secret|
|
|
363
|
+
return Stripe::Webhook.construct_event(raw_body, signature, secret)
|
|
364
|
+
rescue Stripe::SignatureVerificationError
|
|
365
|
+
next
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
raise Spree::PaymentMethod::WebhookSignatureError, 'Invalid webhook signature'
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def webhook_signing_secrets
|
|
372
|
+
secrets = SpreeStripe::WebhookKey
|
|
373
|
+
.joins(:payment_methods_webhook_keys)
|
|
374
|
+
.where(payment_methods_webhook_keys: { payment_method_id: id })
|
|
375
|
+
.pluck(:signing_secret)
|
|
376
|
+
.compact
|
|
377
|
+
|
|
378
|
+
secrets << ENV['STRIPE_SIGNING_SECRET'] if ENV['STRIPE_SIGNING_SECRET'].present?
|
|
379
|
+
secrets
|
|
453
380
|
end
|
|
454
381
|
end
|
|
455
382
|
end
|
|
@@ -24,7 +24,7 @@ module SpreeStripe
|
|
|
24
24
|
|
|
25
25
|
def stripped_order_description
|
|
26
26
|
@stripped_order_description ||= begin
|
|
27
|
-
|
|
27
|
+
I18n.transliterate(order_description)
|
|
28
28
|
.gsub(STATEMENT_DESCRIPTOR_NOT_ALLOWED_CHARS, '')
|
|
29
29
|
.strip
|
|
30
30
|
.upcase[0...STATEMENT_DESCRIPTOR_MAX_CHARS]
|
|
@@ -11,8 +11,7 @@ module SpreeStripe
|
|
|
11
11
|
|
|
12
12
|
return payment_intent if payment_intent.present?
|
|
13
13
|
|
|
14
|
-
# response is
|
|
15
|
-
# https://github.com/activemerchant/active_merchant/blob/master/lib/active_merchant/billing/response.rb
|
|
14
|
+
# response is a Spree::PaymentResponse object
|
|
16
15
|
response = gateway.create_payment_intent(
|
|
17
16
|
amount,
|
|
18
17
|
order,
|
|
@@ -37,7 +37,7 @@ module SpreeStripe
|
|
|
37
37
|
when 'customer_balance', 'us_bank_account'
|
|
38
38
|
SpreeStripe::PaymentSources::BankTransfer.create!(source_params)
|
|
39
39
|
else
|
|
40
|
-
|
|
40
|
+
Spree::PaymentSource.create!(source_params)
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
|
|
@@ -8,7 +8,7 @@ module SpreeStripe
|
|
|
8
8
|
attributes_to_update = { stripe_apple_pay_domain_id: payment_method_domain.id }
|
|
9
9
|
|
|
10
10
|
tld_length = model.url.split('.').length
|
|
11
|
-
if tld_length > 2 && model.is_a?(Spree::CustomDomain)
|
|
11
|
+
if tld_length > 2 && defined?(Spree::CustomDomain) && model.is_a?(Spree::CustomDomain)
|
|
12
12
|
top_level_domain_name = model.url.split('.').last(tld_length - 1).join('.')
|
|
13
13
|
top_level_domain = gateway.send_request { |opts| Stripe::PaymentMethodDomain.create({ domain_name: top_level_domain_name }, opts) }
|
|
14
14
|
attributes_to_update[:stripe_top_level_domain_id] = top_level_domain.id
|
|
@@ -2,7 +2,19 @@ module SpreeStripe
|
|
|
2
2
|
module WebhookHandlers
|
|
3
3
|
class PaymentIntentPaymentFailed
|
|
4
4
|
def call(event)
|
|
5
|
-
|
|
5
|
+
stripe_id = event.data.object.id
|
|
6
|
+
|
|
7
|
+
# New system: PaymentSession
|
|
8
|
+
payment_session = Spree::PaymentSessions::Stripe.find_by(external_id: stripe_id)
|
|
9
|
+
if payment_session.present?
|
|
10
|
+
payment_session.fail if payment_session.can_fail?
|
|
11
|
+
order = payment_session.order
|
|
12
|
+
order.cancel! if !order.canceled? && order.can_cancel?
|
|
13
|
+
return
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Legacy system: PaymentIntent
|
|
17
|
+
payment_intent = SpreeStripe::PaymentIntent.find_by(stripe_id: stripe_id)
|
|
6
18
|
return if payment_intent.nil?
|
|
7
19
|
|
|
8
20
|
order = payment_intent.order
|
|
@@ -2,8 +2,17 @@ module SpreeStripe
|
|
|
2
2
|
module WebhookHandlers
|
|
3
3
|
class PaymentIntentSucceeded
|
|
4
4
|
def call(event)
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
stripe_id = event.data.object[:id]
|
|
6
|
+
|
|
7
|
+
# New system: PaymentSession
|
|
8
|
+
payment_session = Spree::PaymentSessions::Stripe.find_by(external_id: stripe_id)
|
|
9
|
+
if payment_session.present?
|
|
10
|
+
SpreeStripe::CompleteOrderFromSessionJob.set(wait: 10.seconds).perform_later(payment_session.id)
|
|
11
|
+
return
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Legacy system: PaymentIntent
|
|
15
|
+
payment_intent = SpreeStripe::PaymentIntent.find_by(stripe_id: stripe_id)
|
|
7
16
|
return if payment_intent.nil?
|
|
8
17
|
|
|
9
18
|
SpreeStripe::CompleteOrderJob.set(wait: 10.seconds).perform_later(payment_intent.id)
|
data/config/routes.rb
CHANGED
|
@@ -10,15 +10,17 @@ Spree::Core::Engine.add_routes do
|
|
|
10
10
|
# Stripe webhooks
|
|
11
11
|
mount StripeEvent::Engine, at: '/stripe'
|
|
12
12
|
|
|
13
|
-
# Storefront API
|
|
14
|
-
|
|
15
|
-
namespace :
|
|
16
|
-
namespace :
|
|
17
|
-
namespace :
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
# Storefront API (only when spree_legacy_api_v2 gem is available)
|
|
14
|
+
if defined?(SpreeLegacyApiV2::Engine)
|
|
15
|
+
namespace :api, defaults: { format: 'json' } do
|
|
16
|
+
namespace :v2 do
|
|
17
|
+
namespace :storefront do
|
|
18
|
+
namespace :stripe do
|
|
19
|
+
resources :setup_intents, only: %i[create]
|
|
20
|
+
resources :payment_intents, only: %i[show create update] do
|
|
21
|
+
member do
|
|
22
|
+
patch :confirm
|
|
23
|
+
end
|
|
22
24
|
end
|
|
23
25
|
end
|
|
24
26
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module SpreeStripe
|
|
2
2
|
class Configuration < Spree::Preferences::Configuration
|
|
3
3
|
preference :supported_webhook_events, :array, default: %w[payment_intent.payment_failed payment_intent.succeeded setup_intent.succeeded]
|
|
4
|
+
preference :use_legacy_payment_intents, :boolean, default: false
|
|
5
|
+
preference :use_legacy_webhook_handlers, :boolean, default: false
|
|
4
6
|
end
|
|
5
7
|
end
|
data/lib/spree_stripe/engine.rb
CHANGED
|
@@ -7,6 +7,12 @@ module SpreeStripe
|
|
|
7
7
|
# Add app/subscribers to autoload paths
|
|
8
8
|
config.paths.add 'app/subscribers', eager_load: true
|
|
9
9
|
|
|
10
|
+
# Only load API controllers and serializers when spree_legacy_api_v2 gem is available
|
|
11
|
+
if defined?(SpreeLegacyApiV2::Engine)
|
|
12
|
+
config.autoload_paths << root.join('lib', 'spree_api_v2')
|
|
13
|
+
config.eager_load_paths << root.join('lib', 'spree_api_v2')
|
|
14
|
+
end
|
|
15
|
+
|
|
10
16
|
# use rspec for tests
|
|
11
17
|
config.generators do |g|
|
|
12
18
|
g.test_framework :rspec
|
|
@@ -33,8 +39,13 @@ module SpreeStripe
|
|
|
33
39
|
end
|
|
34
40
|
|
|
35
41
|
def self.activate
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
glob_paths = [File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')]
|
|
43
|
+
glob_paths << File.join(File.dirname(__FILE__), '../../lib/spree_api_v2/**/*_decorator*.rb') if defined?(SpreeLegacyApiV2::Engine)
|
|
44
|
+
|
|
45
|
+
glob_paths.each do |glob_path|
|
|
46
|
+
Dir.glob(glob_path) do |c|
|
|
47
|
+
Rails.configuration.cache_classes ? require(c) : load(c)
|
|
48
|
+
end
|
|
38
49
|
end
|
|
39
50
|
end
|
|
40
51
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
FactoryBot.define do
|
|
2
|
+
factory :stripe_payment_session, class: 'Spree::PaymentSessions::Stripe' do
|
|
3
|
+
order { create(:order_with_line_items) }
|
|
4
|
+
payment_method { create(:stripe_gateway, stores: [order.store]) }
|
|
5
|
+
amount { order.total }
|
|
6
|
+
currency { order.currency }
|
|
7
|
+
status { 'pending' }
|
|
8
|
+
type { 'Spree::PaymentSessions::Stripe' }
|
|
9
|
+
external_id { "pi_test_#{SecureRandom.hex(12)}" }
|
|
10
|
+
external_data { { 'client_secret' => "pi_secret_#{SecureRandom.hex(8)}" } }
|
|
11
|
+
|
|
12
|
+
trait :with_customer do
|
|
13
|
+
customer { order.user || create(:user) }
|
|
14
|
+
customer_external_id { "cus_#{SecureRandom.hex(8)}" }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
trait :with_ephemeral_key do
|
|
18
|
+
external_data { { 'client_secret' => "pi_secret_#{SecureRandom.hex(8)}", 'ephemeral_key_secret' => "ek_test_#{SecureRandom.hex(8)}" } }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
trait :processing do
|
|
22
|
+
status { 'processing' }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
trait :completed do
|
|
26
|
+
status { 'completed' }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
trait :failed do
|
|
30
|
+
status { 'failed' }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/spree_stripe/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spree_stripe
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vendo Connect Inc., Vendo Sp. z o.o.
|
|
@@ -15,28 +15,28 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 5.
|
|
18
|
+
version: 5.4.0.beta
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 5.
|
|
25
|
+
version: 5.4.0.beta
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: spree_admin
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 5.
|
|
32
|
+
version: 5.4.0.beta
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 5.
|
|
39
|
+
version: 5.4.0.beta
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: stripe
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -166,9 +166,6 @@ files:
|
|
|
166
166
|
- app/assets/images/payment_icons/banktransfer.svg
|
|
167
167
|
- app/assets/images/payment_icons/link.svg
|
|
168
168
|
- app/assets/images/payment_icons/sepadebit.svg
|
|
169
|
-
- app/controllers/spree/api/v2/storefront/stripe/base_controller.rb
|
|
170
|
-
- app/controllers/spree/api/v2/storefront/stripe/payment_intents_controller.rb
|
|
171
|
-
- app/controllers/spree/api/v2/storefront/stripe/setup_intents_controller.rb
|
|
172
169
|
- app/controllers/spree_stripe/apple_pay_domain_verification_controller.rb
|
|
173
170
|
- app/controllers/spree_stripe/payment_intents_controller.rb
|
|
174
171
|
- app/controllers/spree_stripe/store_controller_decorator.rb
|
|
@@ -180,16 +177,20 @@ files:
|
|
|
180
177
|
- app/javascript/spree_stripe/controllers/stripe_controller.js
|
|
181
178
|
- app/jobs/spree_stripe/attach_customer_to_credit_card_job.rb
|
|
182
179
|
- app/jobs/spree_stripe/base_job.rb
|
|
180
|
+
- app/jobs/spree_stripe/complete_order_from_session_job.rb
|
|
183
181
|
- app/jobs/spree_stripe/complete_order_job.rb
|
|
184
182
|
- app/jobs/spree_stripe/create_tax_transaction_job.rb
|
|
185
183
|
- app/jobs/spree_stripe/create_webhook_endpoint_job.rb
|
|
186
184
|
- app/jobs/spree_stripe/register_domain_job.rb
|
|
187
185
|
- app/jobs/spree_stripe/update_customer_job.rb
|
|
186
|
+
- app/models/spree/payment_sessions/stripe.rb
|
|
188
187
|
- app/models/spree_stripe/base.rb
|
|
189
188
|
- app/models/spree_stripe/calculators/stripe_tax.rb
|
|
190
189
|
- app/models/spree_stripe/credit_card_decorator.rb
|
|
191
190
|
- app/models/spree_stripe/custom_domain_decorator.rb
|
|
192
191
|
- app/models/spree_stripe/gateway.rb
|
|
192
|
+
- app/models/spree_stripe/gateway/payment_intents.rb
|
|
193
|
+
- app/models/spree_stripe/gateway/payment_sessions.rb
|
|
193
194
|
- app/models/spree_stripe/gateway_customer_decorator.rb
|
|
194
195
|
- app/models/spree_stripe/order_decorator.rb
|
|
195
196
|
- app/models/spree_stripe/payment_decorator.rb
|
|
@@ -214,24 +215,6 @@ files:
|
|
|
214
215
|
- app/presenters/spree_stripe/payment_intent_presenter.rb
|
|
215
216
|
- app/presenters/spree_stripe/statement_descriptor_suffix_presenter.rb
|
|
216
217
|
- app/presenters/spree_stripe/tax_presenter.rb
|
|
217
|
-
- app/serializers/spree/api/v2/platform/affirm_serializer.rb
|
|
218
|
-
- app/serializers/spree/api/v2/platform/after_pay_serializer.rb
|
|
219
|
-
- app/serializers/spree/api/v2/platform/alipay_serializer.rb
|
|
220
|
-
- app/serializers/spree/api/v2/platform/ideal_serializer.rb
|
|
221
|
-
- app/serializers/spree/api/v2/platform/klarna_serializer.rb
|
|
222
|
-
- app/serializers/spree/api/v2/platform/link_serializer.rb
|
|
223
|
-
- app/serializers/spree/api/v2/platform/przelewy24_serializer.rb
|
|
224
|
-
- app/serializers/spree/api/v2/platform/sepa_debit_serializer.rb
|
|
225
|
-
- app/serializers/spree/v2/storefront/affirm_serializer.rb
|
|
226
|
-
- app/serializers/spree/v2/storefront/after_pay_serializer.rb
|
|
227
|
-
- app/serializers/spree/v2/storefront/alipay_serializer.rb
|
|
228
|
-
- app/serializers/spree/v2/storefront/ideal_serializer.rb
|
|
229
|
-
- app/serializers/spree/v2/storefront/klarna_serializer.rb
|
|
230
|
-
- app/serializers/spree/v2/storefront/link_serializer.rb
|
|
231
|
-
- app/serializers/spree/v2/storefront/payment_intent_serializer.rb
|
|
232
|
-
- app/serializers/spree/v2/storefront/przelewy24_serializer.rb
|
|
233
|
-
- app/serializers/spree/v2/storefront/sepa_debit_serializer.rb
|
|
234
|
-
- app/serializers/spree_stripe/v2/storefront/payment_method_serializer_decorator.rb
|
|
235
218
|
- app/services/spree_stripe/complete_order.rb
|
|
236
219
|
- app/services/spree_stripe/create_gateway_webhooks.rb
|
|
237
220
|
- app/services/spree_stripe/create_payment.rb
|
|
@@ -258,6 +241,27 @@ files:
|
|
|
258
241
|
- config/routes.rb
|
|
259
242
|
- db/migrate/20250310152812_setup_spree_stripe_models.rb
|
|
260
243
|
- lib/generators/spree_stripe/install/install_generator.rb
|
|
244
|
+
- lib/spree_api_v2/spree/api/v2/platform/affirm_serializer.rb
|
|
245
|
+
- lib/spree_api_v2/spree/api/v2/platform/after_pay_serializer.rb
|
|
246
|
+
- lib/spree_api_v2/spree/api/v2/platform/alipay_serializer.rb
|
|
247
|
+
- lib/spree_api_v2/spree/api/v2/platform/ideal_serializer.rb
|
|
248
|
+
- lib/spree_api_v2/spree/api/v2/platform/klarna_serializer.rb
|
|
249
|
+
- lib/spree_api_v2/spree/api/v2/platform/link_serializer.rb
|
|
250
|
+
- lib/spree_api_v2/spree/api/v2/platform/przelewy24_serializer.rb
|
|
251
|
+
- lib/spree_api_v2/spree/api/v2/platform/sepa_debit_serializer.rb
|
|
252
|
+
- lib/spree_api_v2/spree/api/v2/storefront/stripe/base_controller.rb
|
|
253
|
+
- lib/spree_api_v2/spree/api/v2/storefront/stripe/payment_intents_controller.rb
|
|
254
|
+
- lib/spree_api_v2/spree/api/v2/storefront/stripe/setup_intents_controller.rb
|
|
255
|
+
- lib/spree_api_v2/spree/v2/storefront/affirm_serializer.rb
|
|
256
|
+
- lib/spree_api_v2/spree/v2/storefront/after_pay_serializer.rb
|
|
257
|
+
- lib/spree_api_v2/spree/v2/storefront/alipay_serializer.rb
|
|
258
|
+
- lib/spree_api_v2/spree/v2/storefront/ideal_serializer.rb
|
|
259
|
+
- lib/spree_api_v2/spree/v2/storefront/klarna_serializer.rb
|
|
260
|
+
- lib/spree_api_v2/spree/v2/storefront/link_serializer.rb
|
|
261
|
+
- lib/spree_api_v2/spree/v2/storefront/payment_intent_serializer.rb
|
|
262
|
+
- lib/spree_api_v2/spree/v2/storefront/przelewy24_serializer.rb
|
|
263
|
+
- lib/spree_api_v2/spree/v2/storefront/sepa_debit_serializer.rb
|
|
264
|
+
- lib/spree_api_v2/spree_stripe/v2/storefront/payment_method_serializer_decorator.rb
|
|
261
265
|
- lib/spree_stripe.rb
|
|
262
266
|
- lib/spree_stripe/configuration.rb
|
|
263
267
|
- lib/spree_stripe/engine.rb
|
|
@@ -271,6 +275,7 @@ files:
|
|
|
271
275
|
- lib/spree_stripe/testing_support/factories/payment_intent_factory.rb
|
|
272
276
|
- lib/spree_stripe/testing_support/factories/przelewy24_payment_source_factory.rb
|
|
273
277
|
- lib/spree_stripe/testing_support/factories/sepa_debit_payment_source_factory.rb
|
|
278
|
+
- lib/spree_stripe/testing_support/factories/stripe_payment_session_factory.rb
|
|
274
279
|
- lib/spree_stripe/testing_support/factories/webhook_key_factory.rb
|
|
275
280
|
- lib/spree_stripe/version.rb
|
|
276
281
|
- vendor/javascript/@stripe--stripe-js--dist--pure.esm.js.js
|
|
@@ -279,9 +284,9 @@ licenses:
|
|
|
279
284
|
- MIT
|
|
280
285
|
metadata:
|
|
281
286
|
bug_tracker_uri: https://github.com/spree/spree_stripe/issues
|
|
282
|
-
changelog_uri: https://github.com/spree/spree_stripe/releases/tag/v1.
|
|
287
|
+
changelog_uri: https://github.com/spree/spree_stripe/releases/tag/v1.6.0
|
|
283
288
|
documentation_uri: https://docs.spreecommerce.org/
|
|
284
|
-
source_code_uri: https://github.com/spree/spree_stripe/tree/v1.
|
|
289
|
+
source_code_uri: https://github.com/spree/spree_stripe/tree/v1.6.0
|
|
285
290
|
rdoc_options: []
|
|
286
291
|
require_paths:
|
|
287
292
|
- lib
|
|
@@ -289,7 +294,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
289
294
|
requirements:
|
|
290
295
|
- - ">="
|
|
291
296
|
- !ruby/object:Gem::Version
|
|
292
|
-
version: '3.
|
|
297
|
+
version: '3.2'
|
|
293
298
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
294
299
|
requirements:
|
|
295
300
|
- - ">="
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/data/{app/controllers → lib/spree_api_v2}/spree/api/v2/storefront/stripe/base_controller.rb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|