spree_stripe 1.0.2
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 +7 -0
- data/LICENSE.md +14 -0
- data/README.md +132 -0
- data/Rakefile +21 -0
- data/app/assets/config/spree_stripe_manifest.js +3 -0
- data/app/controllers/spree/api/v2/storefront/stripe/base_controller.rb +21 -0
- data/app/controllers/spree/api/v2/storefront/stripe/payment_intents_controller.rb +61 -0
- data/app/controllers/spree/api/v2/storefront/stripe/setup_intents_controller.rb +19 -0
- data/app/controllers/spree_stripe/apple_pay_domain_verification_controller.rb +11 -0
- data/app/controllers/spree_stripe/payment_intents_controller.rb +61 -0
- data/app/controllers/spree_stripe/store_controller_decorator.rb +9 -0
- data/app/controllers/stripe_event/webhook_controller_decorator.rb +17 -0
- data/app/helpers/spree_stripe/base_helper.rb +13 -0
- data/app/javascript/spree_stripe/application.js +18 -0
- data/app/javascript/spree_stripe/controllers/stripe_button_controller.js +452 -0
- data/app/javascript/spree_stripe/controllers/stripe_controller.js +311 -0
- data/app/jobs/spree_stripe/base_job.rb +5 -0
- data/app/jobs/spree_stripe/complete_order_job.rb +8 -0
- data/app/jobs/spree_stripe/create_webhook_endpoint_job.rb +7 -0
- data/app/jobs/spree_stripe/register_domain_job.rb +20 -0
- data/app/models/spree_stripe/base.rb +6 -0
- data/app/models/spree_stripe/credit_card_decorator.rb +13 -0
- data/app/models/spree_stripe/custom_domain_decorator.rb +18 -0
- data/app/models/spree_stripe/gateway.rb +366 -0
- data/app/models/spree_stripe/order_decorator.rb +19 -0
- data/app/models/spree_stripe/payment_decorator.rb +46 -0
- data/app/models/spree_stripe/payment_intent.rb +66 -0
- data/app/models/spree_stripe/payment_method_decorator.rb +18 -0
- data/app/models/spree_stripe/payment_methods_webhook_key.rb +8 -0
- data/app/models/spree_stripe/payment_source_decorator.rb +9 -0
- data/app/models/spree_stripe/payment_sources/affirm.rb +9 -0
- data/app/models/spree_stripe/payment_sources/after_pay.rb +13 -0
- data/app/models/spree_stripe/payment_sources/alipay.rb +9 -0
- data/app/models/spree_stripe/payment_sources/ideal.rb +15 -0
- data/app/models/spree_stripe/payment_sources/klarna.rb +9 -0
- data/app/models/spree_stripe/payment_sources/link.rb +13 -0
- data/app/models/spree_stripe/payment_sources/przelewy24.rb +11 -0
- data/app/models/spree_stripe/payment_sources/sepa_debit.rb +13 -0
- data/app/models/spree_stripe/store_decorator.rb +24 -0
- data/app/models/spree_stripe/webhook_key.rb +14 -0
- data/app/presenters/spree_stripe/customer_presenter.rb +35 -0
- data/app/presenters/spree_stripe/payment_intent_presenter.rb +103 -0
- data/app/serializers/spree/api/v2/platform/affirm_serializer.rb +9 -0
- data/app/serializers/spree/api/v2/platform/after_pay_serializer.rb +9 -0
- data/app/serializers/spree/api/v2/platform/alipay_serializer.rb +9 -0
- data/app/serializers/spree/api/v2/platform/ideal_serializer.rb +9 -0
- data/app/serializers/spree/api/v2/platform/klarna_serializer.rb +9 -0
- data/app/serializers/spree/api/v2/platform/link_serializer.rb +9 -0
- data/app/serializers/spree/api/v2/platform/przelewy24_serializer.rb +9 -0
- data/app/serializers/spree/api/v2/platform/sepa_debit_serializer.rb +9 -0
- data/app/serializers/spree/v2/storefront/affirm_serializer.rb +7 -0
- data/app/serializers/spree/v2/storefront/after_pay_serializer.rb +7 -0
- data/app/serializers/spree/v2/storefront/alipay_serializer.rb +7 -0
- data/app/serializers/spree/v2/storefront/ideal_serializer.rb +7 -0
- data/app/serializers/spree/v2/storefront/klarna_serializer.rb +7 -0
- data/app/serializers/spree/v2/storefront/link_serializer.rb +7 -0
- data/app/serializers/spree/v2/storefront/payment_intent_serializer.rb +11 -0
- data/app/serializers/spree/v2/storefront/przelewy24_serializer.rb +7 -0
- data/app/serializers/spree/v2/storefront/sepa_debit_serializer.rb +7 -0
- data/app/services/spree_stripe/complete_order.rb +99 -0
- data/app/services/spree_stripe/create_gateway_webhooks.rb +55 -0
- data/app/services/spree_stripe/create_payment.rb +43 -0
- data/app/services/spree_stripe/create_payment_intent.rb +40 -0
- data/app/services/spree_stripe/create_setup_intent.rb +20 -0
- data/app/services/spree_stripe/create_source.rb +87 -0
- data/app/services/spree_stripe/register_domain.rb +22 -0
- data/app/services/spree_stripe/webhook_handlers/payment_intent_payment_failed.rb +18 -0
- data/app/services/spree_stripe/webhook_handlers/payment_intent_succeeded.rb +13 -0
- data/app/services/spree_stripe/webhook_handlers/setup_intent_succeeded.rb +34 -0
- data/app/views/spree/admin/payment_methods/configuration_guides/_spree_stripe.html.erb +0 -0
- data/app/views/spree/admin/payment_methods/custom_form_fields/_spree_stripe.html.erb +47 -0
- data/app/views/spree/admin/payment_methods/descriptions/_spree_stripe.html.erb +15 -0
- data/app/views/spree/checkout/payment/_spree_stripe.html.erb +63 -0
- data/app/views/spree_stripe/_head.html.erb +2 -0
- data/app/views/spree_stripe/_quick_checkout.html.erb +34 -0
- data/config/importmap.rb +8 -0
- data/config/initializers/spree.rb +8 -0
- data/config/initializers/stripe.rb +14 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +24 -0
- data/db/migrate/20250310152812_setup_spree_stripe_models.rb +41 -0
- data/lib/generators/spree_stripe/install/install_generator.rb +20 -0
- data/lib/spree_stripe/configuration.rb +5 -0
- data/lib/spree_stripe/engine.rb +36 -0
- data/lib/spree_stripe/factories.rb +3 -0
- data/lib/spree_stripe/testing_support/factories/after_pay_payment_source_factory.rb +6 -0
- data/lib/spree_stripe/testing_support/factories/alipay_payment_source_factory.rb +6 -0
- data/lib/spree_stripe/testing_support/factories/gateway_factory.rb +23 -0
- data/lib/spree_stripe/testing_support/factories/ideal_payment_source_factory.rb +6 -0
- data/lib/spree_stripe/testing_support/factories/klarna_payment_source_factory.rb +6 -0
- data/lib/spree_stripe/testing_support/factories/link_payment_source_factory.rb +6 -0
- data/lib/spree_stripe/testing_support/factories/payment_intent_factory.rb +24 -0
- data/lib/spree_stripe/testing_support/factories/payment_source_factory.rb +8 -0
- data/lib/spree_stripe/testing_support/factories/przelewy24_payment_source_factory.rb +6 -0
- data/lib/spree_stripe/testing_support/factories/sepa_debit_payment_source_factory.rb +6 -0
- data/lib/spree_stripe/testing_support/factories/webhook_key_factory.rb +23 -0
- data/lib/spree_stripe/version.rb +7 -0
- data/lib/spree_stripe.rb +14 -0
- data/vendor/javascript/@stripe--stripe-js--dist--pure.esm.js.js +4 -0
- metadata +295 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module SpreeStripe
|
2
|
+
module WebhookHandlers
|
3
|
+
class PaymentIntentPaymentFailed
|
4
|
+
def call(event)
|
5
|
+
return unless ['affirm', 'afterpay_clearpay'].include?(event.data.object&.last_payment_error&.payment_method&.type)
|
6
|
+
|
7
|
+
payment_intent = SpreeStripe::PaymentIntent.find_by(stripe_id: event.data.object.id)
|
8
|
+
return if payment_intent.nil?
|
9
|
+
|
10
|
+
order = payment_intent.order
|
11
|
+
|
12
|
+
return if order.canceled?
|
13
|
+
|
14
|
+
order.cancel!
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module SpreeStripe
|
2
|
+
module WebhookHandlers
|
3
|
+
class PaymentIntentSucceeded
|
4
|
+
def call(event)
|
5
|
+
payment_intent_data = event.data.object
|
6
|
+
payment_intent = SpreeStripe::PaymentIntent.find_by(stripe_id: payment_intent_data[:id])
|
7
|
+
return if payment_intent.nil?
|
8
|
+
|
9
|
+
SpreeStripe::CompleteOrderJob.set(wait: 10.seconds).perform_later(payment_intent.id)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module SpreeStripe
|
2
|
+
module WebhookHandlers
|
3
|
+
class SetupIntentSucceeded
|
4
|
+
def call(event)
|
5
|
+
setup_intent_data = event.data.object
|
6
|
+
gateway_customer = Spree::GatewayCustomer.find_by(profile_id: setup_intent_data.customer)
|
7
|
+
return if gateway_customer.nil?
|
8
|
+
|
9
|
+
spree_payment_method = gateway_customer.payment_method
|
10
|
+
|
11
|
+
user = gateway_customer.user
|
12
|
+
return if user.nil?
|
13
|
+
|
14
|
+
begin
|
15
|
+
stripe_payment_method = Stripe::PaymentMethod.retrieve(
|
16
|
+
setup_intent_data.payment_method,
|
17
|
+
{ api_key: spree_payment_method.preferred_secret_key }
|
18
|
+
)
|
19
|
+
rescue Stripe::StripeError => e
|
20
|
+
Rails.error.report(e, handled: false, context: { event: event, user_id: user.id }, source: 'spree_stripe')
|
21
|
+
return
|
22
|
+
end
|
23
|
+
|
24
|
+
SpreeStripe::CreateSource.new(
|
25
|
+
stripe_payment_method_details: stripe_payment_method,
|
26
|
+
stripe_payment_method_id: setup_intent_data.payment_method,
|
27
|
+
stripe_billing_details: stripe_payment_method.billing_details,
|
28
|
+
gateway: spree_payment_method,
|
29
|
+
user: user
|
30
|
+
).call
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
File without changes
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<% if f.object.persisted? %>
|
2
|
+
<div class="form-group mb-0">
|
3
|
+
<%= f.label :apple_developer_merchantid_domain_association, 'Apple Pay', class: 'font-weight-bold' %>
|
4
|
+
|
5
|
+
<% if f.object.apple_developer_merchantid_domain_association.attached? %>
|
6
|
+
<div class="form-control d-flex align-items-center justify-content-between pr-1 mb-3 bg-light">
|
7
|
+
Verification file uploaded!
|
8
|
+
<%= link_to f.object.apple_developer_merchantid_domain_association.url, class: 'btn btn-light btn-sm' do %>
|
9
|
+
<%= icon 'download' %>
|
10
|
+
Download
|
11
|
+
<% end %>
|
12
|
+
</div>
|
13
|
+
<% else %>
|
14
|
+
<p class="form-text">
|
15
|
+
To enable Apple Pay, you need to:
|
16
|
+
</p>
|
17
|
+
|
18
|
+
<ol class="mb-4">
|
19
|
+
<li>
|
20
|
+
go to
|
21
|
+
<%= link_to 'payment method domains', 'https://dashboard.stripe.com/settings/payment_method_domains', target: '_blank', class: 'alert-link' %>
|
22
|
+
in your Stripe account
|
23
|
+
</li>
|
24
|
+
<li>
|
25
|
+
Click <strong>Add new domain</strong> and input:
|
26
|
+
<div class="flex bg-light my-2 p-2 w-50 rounded" data-controller="clipboard" data-clipboard-success-content-value="Copied!">
|
27
|
+
<%= text_field_tag :domain, current_store.url_or_custom_domain, readonly: true, class: 'form-control-plaintext py-0', data: { 'clipboard-target': 'source' } %>
|
28
|
+
<button data-action="clipboard#copy" class="btn btn-sm btn-light py-1 with-tip" title="Copy to clipboboard" type="button" data-clipboard-target="button">
|
29
|
+
<%= icon 'clipboard', width: 16, height: 16, class: 'mr-0 text-muted' %>
|
30
|
+
</button>
|
31
|
+
</div>
|
32
|
+
</li>
|
33
|
+
<li>
|
34
|
+
Click <strong>Download file</strong> and attach it below.
|
35
|
+
</li>
|
36
|
+
<li>
|
37
|
+
Click <strong>Update</strong> on this page.
|
38
|
+
</li>
|
39
|
+
<li>
|
40
|
+
Click <strong>Verify</strong> to verify if file was uploaded correctly.
|
41
|
+
</li>
|
42
|
+
</ol>
|
43
|
+
<% end %>
|
44
|
+
|
45
|
+
<%= f.file_field :apple_developer_merchantid_domain_association, class: "form-control-file" %>
|
46
|
+
</div>
|
47
|
+
<% end %>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<p class="mb-1">
|
2
|
+
Get paid instantly when someone makes a purchase on your store.
|
3
|
+
Supports all major credit cards, wallets and more.
|
4
|
+
</p>
|
5
|
+
|
6
|
+
<div class="d-flex align-items-center">
|
7
|
+
<%= payment_method_icon_tag 'visa', class: 'm-1' %>
|
8
|
+
<%= payment_method_icon_tag 'master', class: 'm-1' %>
|
9
|
+
<%= payment_method_icon_tag 'american_express', class: 'm-1' %>
|
10
|
+
<%= payment_method_icon_tag 'apple_pay', class: 'm-1' %>
|
11
|
+
<%= payment_method_icon_tag 'google_pay', class: 'm-1' %>
|
12
|
+
<%= payment_method_icon_tag 'klarna', class: 'm-1' %>
|
13
|
+
<%= payment_method_icon_tag 'afterpay', class: 'm-1' %>
|
14
|
+
<%= payment_method_icon_tag 'affirm', class: 'm-1' %>
|
15
|
+
</div>
|
@@ -0,0 +1,63 @@
|
|
1
|
+
<div
|
2
|
+
data-controller="checkout-stripe"
|
3
|
+
data-checkout-stripe-payment-intent-value="<%= current_stripe_payment_intent.to_json %>"
|
4
|
+
data-checkout-stripe-return-url-value="<%= spree.stripe_payment_intent_url(current_stripe_payment_intent) %>"
|
5
|
+
data-checkout-stripe-api-key-value="<%= current_stripe_gateway.try(:preferred_publishable_key) %>"
|
6
|
+
data-checkout-stripe-order-token-value="<%= @order.token %>"
|
7
|
+
data-checkout-stripe-order-email-value="<%= @order.email %>"
|
8
|
+
data-checkout-stripe-color-primary-value="<%= hex_color_to_rgb(theme_setting(:primary_color)) %>"
|
9
|
+
data-checkout-stripe-color-text-value="<%= hex_color_to_rgb(theme_setting(:text_color)) %>"
|
10
|
+
>
|
11
|
+
<% payment_sources = checkout_payment_sources(payment_method) %>
|
12
|
+
|
13
|
+
<% if payment_sources.any? %>
|
14
|
+
<div id="existing_cards">
|
15
|
+
<div class="form-group">
|
16
|
+
<ul class="list-group mb-5 border rounded-md" data-controller="reveal">
|
17
|
+
<% payment_sources.each_with_index do |card, index| %>
|
18
|
+
<li class="p-0 m-0 border-b" id="<%= spree_dom_id(card) %>">
|
19
|
+
<div class="custom-control custom-radio px-5 py-4 flex items-center">
|
20
|
+
<label class="flex flex-row items-center cursor-pointer">
|
21
|
+
<%= radio_button_tag "order[existing_card]", card.gateway_payment_profile_id, index == 0, { data: {action: "checkout-stripe#initializeStripe reveal#hide", checkout_stripe_target: index == 0 ? "defaultCard" : nil}, class: "mr-5" } %>
|
22
|
+
<div class="flex space-x-4 items-center">
|
23
|
+
<%= payment_method_icon_tag(card.cc_type) %>
|
24
|
+
<span>****<%= card.last_digits %></span>
|
25
|
+
<span><%= card.month.to_s.rjust(2, '0') %>/<%= card.year %></span>
|
26
|
+
</div>
|
27
|
+
</label>
|
28
|
+
</div>
|
29
|
+
</li>
|
30
|
+
<% end %>
|
31
|
+
<li>
|
32
|
+
<div class="custom-control custom-radio px-5 py-4 flex items-center w-full">
|
33
|
+
<label class="flex flex-row items-center w-full pr-8">
|
34
|
+
<%= radio_button_tag "order[existing_card]", nil, false, { data: {action: "checkout-stripe#initializeStripe reveal#show"}, class: "mr-5" } %>
|
35
|
+
<div class="w-full flex flex-col items-start space-y-4 justify-center">
|
36
|
+
<span class="cursor-pointer">
|
37
|
+
<%= Spree.t(:add_new_credit_card) %>
|
38
|
+
</span>
|
39
|
+
<!-- Display a payment form -->
|
40
|
+
<div data-checkout-stripe-target="paymentElement" data-reveal-target="item" class="w-full hidden">
|
41
|
+
<!--Stripe.js injects the Payment Element-->
|
42
|
+
</div>
|
43
|
+
</div>
|
44
|
+
</label>
|
45
|
+
</div>
|
46
|
+
</li>
|
47
|
+
</ul>
|
48
|
+
</div>
|
49
|
+
</div>
|
50
|
+
<% else %>
|
51
|
+
<div data-checkout-stripe-target="paymentElement" class="w-full"></div>
|
52
|
+
<% end %>
|
53
|
+
<div data-checkout-stripe-target="messageContainer" class="hidden text-danger"></div>
|
54
|
+
<div data-checkout-stripe-target="loading" class="p-6">
|
55
|
+
<div class="mx-auto flex justify-center lg:w-1/4">
|
56
|
+
<svg class="animate-spin h-5 w-5 mr-4 text-primary" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
57
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
58
|
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
59
|
+
</svg>
|
60
|
+
<%= Spree.t(:loading) %>...
|
61
|
+
</div>
|
62
|
+
</div>
|
63
|
+
</div>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<% if quick_checkout_enabled?(@order) && current_stripe_gateway.present? && current_stripe_payment_intent.present? %>
|
2
|
+
<div id="quick-checkout" class="<%= defined?(container_class) ? container_class : nil %>">
|
3
|
+
<%= tag.div class: 'w-full', data: {
|
4
|
+
controller: 'checkout-stripe-button',
|
5
|
+
checkout_stripe_button_target: 'container',
|
6
|
+
checkout_stripe_button_api_key_value: current_stripe_gateway.try(:preferred_publishable_key),
|
7
|
+
checkout_stripe_button_order_token_value: @order.token,
|
8
|
+
checkout_stripe_button_client_secret_value: current_stripe_payment_intent.client_secret,
|
9
|
+
checkout_stripe_button_currency_value: @order.currency.downcase,
|
10
|
+
checkout_stripe_button_amount_value: @order.display_total_minus_store_credits.cents,
|
11
|
+
checkout_stripe_button_available_countries_value: available_countries_iso.as_json,
|
12
|
+
checkout_stripe_button_gift_card_code_value: @order.respond_to?(:gift_card) ? @order.gift_card&.code : nil,
|
13
|
+
checkout_stripe_button_gift_card_amount_value: @order.respond_to?(:gift_card_total) ? @order.gift_card_total : nil,
|
14
|
+
checkout_stripe_button_border_radius_value: theme_setting(:button_border_radius),
|
15
|
+
checkout_stripe_button_height_value: defined?(height) ? height : nil,
|
16
|
+
checkout_stripe_button_theme_value: defined?(theme) ? theme : nil,
|
17
|
+
checkout_stripe_button_max_rows_value: defined?(max_rows) ? max_rows : nil,
|
18
|
+
checkout_stripe_button_max_columns_value: defined?(max_columns) ? max_columns : nil,
|
19
|
+
checkout_stripe_button_button_width_value: defined?(button_width) ? button_width : nil,
|
20
|
+
checkout_stripe_button_store_url_value: current_store.formatted_url_or_custom_domain,
|
21
|
+
checkout_stripe_button_return_url_value: spree.stripe_payment_intent_url(current_stripe_payment_intent)
|
22
|
+
} do %>
|
23
|
+
<div id="payment-request-button"></div>
|
24
|
+
|
25
|
+
<div class="relative group-[.no-separator]:hidden flex py-5 items-center ">
|
26
|
+
<div class="flex-grow border-t border-default"></div>
|
27
|
+
<span class="flex-shrink mx-4 text-gray-500 text-sm">
|
28
|
+
<%= Spree.t(:or_continue_below) %>
|
29
|
+
</span>
|
30
|
+
<div class="flex-grow border-t border-default"></div>
|
31
|
+
</div>
|
32
|
+
<% end %>
|
33
|
+
</div>
|
34
|
+
<% end%>
|
data/config/importmap.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
pin 'application-spree-stripe', to: 'spree_stripe/application.js', preload: false
|
2
|
+
|
3
|
+
pin '@stripe/stripe-js/pure', to: '@stripe--stripe-js--dist--pure.esm.js.js' # @1.46.0
|
4
|
+
|
5
|
+
pin_all_from SpreeStripe::Engine.root.join('app/javascript/spree_stripe/controllers'),
|
6
|
+
under: 'spree_stripe/controllers',
|
7
|
+
to: 'spree_stripe/controllers',
|
8
|
+
preload: 'application-spree-stripe'
|
@@ -0,0 +1,8 @@
|
|
1
|
+
Rails.application.config.after_initialize do
|
2
|
+
Rails.application.config.spree.payment_methods << SpreeStripe::Gateway
|
3
|
+
|
4
|
+
if Rails.application.config.respond_to?(:spree_storefront)
|
5
|
+
Rails.application.config.spree_storefront.head_partials << 'spree_stripe/head'
|
6
|
+
Rails.application.config.spree_storefront.quick_checkout_partials << 'spree_stripe/quick_checkout'
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# for local setup you need to use Stripe-CLI and change the key manually
|
2
|
+
StripeEvent.signing_secret = ENV['STRIPE_SIGNING_SECRET'] if ENV['STRIPE_SIGNING_SECRET'].present?
|
3
|
+
|
4
|
+
Stripe.log_level = ENV.fetch('STRIPE_LOG_LEVEL', 'debug')
|
5
|
+
Stripe.api_version = '2023-10-16'
|
6
|
+
Stripe.set_app_info('Spree Stripe', version: Spree.version, url: 'https://spreecommerce.org')
|
7
|
+
|
8
|
+
Rails.application.config.after_initialize do
|
9
|
+
StripeEvent.configure do |events|
|
10
|
+
events.subscribe 'payment_intent.succeeded', SpreeStripe::WebhookHandlers::PaymentIntentSucceeded.new
|
11
|
+
events.subscribe 'payment_intent.payment_failed', SpreeStripe::WebhookHandlers::PaymentIntentPaymentFailed.new
|
12
|
+
events.subscribe 'setup_intent.succeeded', SpreeStripe::WebhookHandlers::SetupIntentSucceeded.new
|
13
|
+
end
|
14
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Spree::Core::Engine.add_routes do
|
2
|
+
# Stripe payment intents
|
3
|
+
get '/stripe/payment_intents/:id', to: '/spree_stripe/payment_intents#show',
|
4
|
+
as: :stripe_payment_intent,
|
5
|
+
controller: '/spree_stripe/payment_intents'
|
6
|
+
|
7
|
+
# Apple Pay domain verification certificate for Apple Pay
|
8
|
+
get '/.well-known/apple-developer-merchantid-domain-association' => '/spree_stripe/apple_pay_domain_verification#show'
|
9
|
+
|
10
|
+
# Stripe webhooks
|
11
|
+
mount StripeEvent::Engine, at: '/stripe'
|
12
|
+
|
13
|
+
# Storefront API
|
14
|
+
namespace :api, defaults: { format: 'json' } do
|
15
|
+
namespace :v2 do
|
16
|
+
namespace :storefront do
|
17
|
+
namespace :stripe do
|
18
|
+
resources :setup_intents, only: %i[create]
|
19
|
+
resources :payment_intents, only: %i[show create update]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class SetupSpreeStripeModels < ActiveRecord::Migration[7.2]
|
2
|
+
def change
|
3
|
+
create_table :spree_stripe_payment_intents do |t|
|
4
|
+
t.decimal :amount, precision: 10, scale: 2, default: '0.0', null: false
|
5
|
+
t.bigint :order_id, null: false
|
6
|
+
t.bigint :payment_method_id, null: false
|
7
|
+
t.string :stripe_id, null: false
|
8
|
+
t.string :client_secret, null: false
|
9
|
+
t.string :customer_id
|
10
|
+
t.string :ephemeral_key_secret
|
11
|
+
t.string :stripe_payment_method_id
|
12
|
+
|
13
|
+
t.timestamps
|
14
|
+
|
15
|
+
t.index ['order_id', 'stripe_id'], name: 'index_spree_stripe_payment_intents_on_order_id_and_stripe_id', unique: true
|
16
|
+
t.index ['order_id'], name: 'index_spree_stripe_payment_intents_on_order_id'
|
17
|
+
t.index ['payment_method_id'], name: 'index_spree_stripe_payment_intents_on_payment_method_id'
|
18
|
+
end
|
19
|
+
|
20
|
+
create_table :spree_stripe_webhook_keys do |t|
|
21
|
+
t.string :stripe_id, null: false
|
22
|
+
t.string :signing_secret, null: false
|
23
|
+
|
24
|
+
t.timestamps
|
25
|
+
|
26
|
+
t.index ['signing_secret'], name: 'index_spree_stripe_webhook_keys_on_signing_secret', unique: true
|
27
|
+
t.index ['stripe_id'], name: 'index_spree_stripe_webhook_keys_on_stripe_id', unique: true
|
28
|
+
end
|
29
|
+
|
30
|
+
create_table :spree_stripe_payment_methods_webhook_keys do |t|
|
31
|
+
t.bigint :payment_method_id, null: false
|
32
|
+
t.bigint :webhook_key_id, null: false
|
33
|
+
|
34
|
+
t.timestamps
|
35
|
+
|
36
|
+
t.index ['payment_method_id', 'webhook_key_id'], name: 'index_payment_method_id_webhook_key_id_uniqueness', unique: true
|
37
|
+
t.index ['payment_method_id'], name: 'index_payment_methods_webhook_keys_on_payment_method_id'
|
38
|
+
t.index ['webhook_key_id'], name: 'index_payment_methods_webhook_keys_on_webhook_key_id'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SpreeStripe
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
class_option :migrate, type: :boolean, default: true
|
5
|
+
|
6
|
+
def add_migrations
|
7
|
+
run 'bundle exec rake railties:install:migrations FROM=spree_stripe'
|
8
|
+
end
|
9
|
+
|
10
|
+
def run_migrations
|
11
|
+
run_migrations = options[:migrate] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]'))
|
12
|
+
if run_migrations
|
13
|
+
run 'bin/rails db:migrate'
|
14
|
+
else
|
15
|
+
puts 'Skipping rails db:migrate, don\'t forget to run it!'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module SpreeStripe
|
2
|
+
class Engine < Rails::Engine
|
3
|
+
require 'spree/core'
|
4
|
+
isolate_namespace Spree
|
5
|
+
engine_name 'spree_stripe'
|
6
|
+
|
7
|
+
# use rspec for tests
|
8
|
+
config.generators do |g|
|
9
|
+
g.test_framework :rspec
|
10
|
+
end
|
11
|
+
|
12
|
+
initializer 'spree_stripe.environment', before: :load_config_initializers do |_app|
|
13
|
+
SpreeStripe::Config = SpreeStripe::Configuration.new
|
14
|
+
end
|
15
|
+
|
16
|
+
initializer 'spree_stripe.assets' do |app|
|
17
|
+
app.config.assets.paths << root.join('app/javascript')
|
18
|
+
app.config.assets.paths << root.join('vendor/javascript')
|
19
|
+
app.config.assets.precompile += %w[spree_stripe_manifest]
|
20
|
+
end
|
21
|
+
|
22
|
+
initializer 'spree_stripe.importmap', before: 'importmap' do |app|
|
23
|
+
app.config.importmap.paths << root.join('config/importmap.rb')
|
24
|
+
# https://github.com/rails/importmap-rails?tab=readme-ov-file#sweeping-the-cache-in-development-and-test
|
25
|
+
app.config.importmap.cache_sweepers << root.join('app/javascript')
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.activate
|
29
|
+
Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
|
30
|
+
Rails.configuration.cache_classes ? require(c) : load(c)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
config.to_prepare(&method(:activate).to_proc)
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :stripe_gateway, parent: :payment_method, class: SpreeStripe::Gateway do
|
3
|
+
name { 'Stripe' }
|
4
|
+
type { 'SpreeStripe::Gateway' }
|
5
|
+
|
6
|
+
preferences do
|
7
|
+
{
|
8
|
+
publishable_key: ENV.fetch('STRIPE_PUBLISHABLE_KEY', 'pk_test_1234567890'),
|
9
|
+
secret_key: ENV.fetch('STRIPE_SECRET_KEY', 'sk_test_1234567890')
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
trait :with_apple_domain_association_file do
|
14
|
+
transient do
|
15
|
+
apple_domain_association_file_path do
|
16
|
+
File.join(SpreeStripe::Engine.root, 'spec', 'fixtures', 'files', 'apple-domain-association-file.txt')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
apple_developer_merchantid_domain_association { Rack::Test::UploadedFile.new(apple_domain_association_file_path) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# == Schema Information
|
2
|
+
#
|
3
|
+
# Table name: spree_stripe_payment_intents
|
4
|
+
#
|
5
|
+
# id :bigint not null, primary key
|
6
|
+
# amount :decimal(10, 2) default(0.0), not null
|
7
|
+
# client_secret :string not null
|
8
|
+
# ephemeral_key_secret :string
|
9
|
+
# created_at :datetime not null
|
10
|
+
# updated_at :datetime not null
|
11
|
+
# customer_id :string
|
12
|
+
# order_id :bigint not null
|
13
|
+
# payment_method_id :bigint not null
|
14
|
+
# stripe_id :string not null
|
15
|
+
# stripe_payment_method_id :string
|
16
|
+
#
|
17
|
+
FactoryBot.define do
|
18
|
+
factory :payment_intent, class: SpreeStripe::PaymentIntent do
|
19
|
+
stripe_id { 'pi_123' }
|
20
|
+
client_secret { 'cs_123' }
|
21
|
+
order { create(:order_with_line_items) }
|
22
|
+
payment_method { create(:stripe_gateway, stores: [order.store]) }
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# == Schema Information
|
2
|
+
#
|
3
|
+
# Table name: spree_stripe_webhook_keys
|
4
|
+
#
|
5
|
+
# id :bigint not null, primary key
|
6
|
+
# kind :integer default("direct")
|
7
|
+
# signing_secret :string not null
|
8
|
+
# created_at :datetime not null
|
9
|
+
# updated_at :datetime not null
|
10
|
+
# stripe_id :string not null
|
11
|
+
#
|
12
|
+
FactoryBot.define do
|
13
|
+
factory :stripe_webhook_key, class: SpreeStripe::WebhookKey do
|
14
|
+
stripe_id { generate(:random_string) }
|
15
|
+
signing_secret { generate(:random_string) }
|
16
|
+
|
17
|
+
before(:create) do |wk|
|
18
|
+
if wk.payment_methods.empty?
|
19
|
+
wk.payment_methods << create(:stripe_gateway)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/spree_stripe.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spree_core'
|
2
|
+
require 'spree_extension'
|
3
|
+
require 'spree_stripe/engine'
|
4
|
+
require 'spree_stripe/version'
|
5
|
+
require 'spree_stripe/configuration'
|
6
|
+
|
7
|
+
require 'stripe'
|
8
|
+
require 'stripe_event'
|
9
|
+
|
10
|
+
module SpreeStripe
|
11
|
+
def self.queue
|
12
|
+
'spree_stripe'
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,4 @@
|
|
1
|
+
// @stripe/stripe-js/dist/pure.esm.js@1.46.0 downloaded from https://ga.jspm.io/npm:@stripe/stripe-js@1.46.0/dist/pure.esm.js
|
2
|
+
|
3
|
+
function _typeof(r){_typeof="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(r){return typeof r}:function(r){return r&&"function"===typeof Symbol&&r.constructor===Symbol&&r!==Symbol.prototype?"symbol":typeof r};return _typeof(r)}var r="https://js.stripe.com/v3";var e=/^https:\/\/js\.stripe\.com\/v3\/?(\?.*)?$/;var t="loadStripe.setLoadParameters was called but an existing Stripe.js script already exists in the document; existing script parameters will be used";var n=function findScript(){var t=document.querySelectorAll('script[src^="'.concat(r,'"]'));for(var n=0;n<t.length;n++){var o=t[n];if(e.test(o.src))return o}return null};var o=function injectScript(e){var t=e&&!e.advancedFraudSignals?"?advancedFraudSignals=false":"";var n=document.createElement("script");n.src="".concat(r).concat(t);var o=document.head||document.body;if(!o)throw new Error("Expected document.body not to be null. Stripe.js requires a <body> element.");o.appendChild(n);return n};var a=function registerWrapper(r,e){r&&r._registerWrapper&&r._registerWrapper({name:"stripe-js",version:"1.46.0",startTime:e})};var i=null;var c=function loadScript(r){if(null!==i)return i;i=new Promise((function(e,a){if("undefined"!==typeof window){window.Stripe&&r&&console.warn(t);if(window.Stripe)e(window.Stripe);else try{var i=n();i&&r?console.warn(t):i||(i=o(r));i.addEventListener("load",(function(){window.Stripe?e(window.Stripe):a(new Error("Stripe.js not available"))}));i.addEventListener("error",(function(){a(new Error("Failed to load Stripe.js"))}))}catch(r){a(r);return}}else e(null)}));return i};var l=function initStripe(r,e,t){if(null===r)return null;var n=r.apply(void 0,e);a(n,t);return n};var s=function validateLoadParams(r){var e="invalid load parameters; expected object of shape\n\n {advancedFraudSignals: boolean}\n\nbut received\n\n ".concat(JSON.stringify(r),"\n");if(null===r||"object"!==_typeof(r))throw new Error(e);if(1===Object.keys(r).length&&"boolean"===typeof r.advancedFraudSignals)return r;throw new Error(e)};var d;var u=false;var p=function loadStripe(){for(var r=arguments.length,e=new Array(r),t=0;t<r;t++)e[t]=arguments[t];u=true;var n=Date.now();return c(d).then((function(r){return l(r,e,n)}))};p.setLoadParameters=function(r){if(u)throw new Error("You cannot change load parameters after calling loadStripe");d=s(r)};export{p as loadStripe};
|
4
|
+
|