spree_api 5.6.0.rc3 → 5.6.0.rc4
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/app/controllers/concerns/spree/api/v3/channel_resolution.rb +54 -8
- data/app/controllers/concerns/spree/api/v3/error_handler.rb +4 -0
- data/app/controllers/spree/api/v3/admin/api_keys_controller.rb +5 -5
- data/app/controllers/spree/api/v3/store/base_controller.rb +7 -2
- data/app/controllers/spree/api/v3/store/carts_controller.rb +6 -1
- data/app/controllers/spree/api/v3/store/channel_controller.rb +31 -0
- data/app/controllers/spree/api/v3/store/resource_controller.rb +12 -0
- data/app/jobs/spree/webhook_delivery_job.rb +6 -1
- data/app/serializers/spree/api/v3/admin/api_key_serializer.rb +6 -1
- data/app/serializers/spree/api/v3/admin/customer_group_serializer.rb +3 -4
- data/app/serializers/spree/api/v3/admin/webhook_delivery_serializer.rb +6 -1
- data/app/serializers/spree/api/v3/cart_serializer.rb +8 -1
- data/app/serializers/spree/api/v3/channel_serializer.rb +9 -1
- data/app/serializers/spree/api/v3/customer_group_serializer.rb +15 -0
- data/app/serializers/spree/api/v3/customer_serializer.rb +8 -0
- data/app/services/spree/webhooks/deliver_webhook.rb +6 -2
- data/app/subscribers/spree/webhook_event_subscriber.rb +5 -2
- data/config/locales/en.yml +2 -0
- data/config/routes.rb +3 -0
- data/lib/spree/api/dependencies.rb +1 -0
- metadata +8 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ca50a0fac8c0d346157c3d42a2969414d5f4e8a392aae87176cb02069c828e21
|
|
4
|
+
data.tar.gz: dc56eff71e891ef304710a9a2f2bfc7c72be17a50d0a24ba1335b137ea817057
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e1097d160a66eac8cee1e1b9169f06781c4105ddb01fce2bf560bcf0c94aeb7e0f947bd36675e17e7b6532af02c47999d242f23fb8359abaa866d3257775afd0
|
|
7
|
+
data.tar.gz: a48b3317819fd03b05d71b016a7856b5c1a8da5d238080964dd9980b0eef328f2a712aed8a903efffde9a86ee1c902903495aee339d01e4a5e216fae0886ffb7
|
|
@@ -6,41 +6,87 @@ module Spree
|
|
|
6
6
|
# read channel context without threading it through method args.
|
|
7
7
|
#
|
|
8
8
|
# Resolution order:
|
|
9
|
-
# 1.
|
|
9
|
+
# 1. The authenticated publishable key's channel binding
|
|
10
|
+
# (+Spree::ApiKey#channel+) — server-assigned, cannot be overridden;
|
|
11
|
+
# a +X-Spree-Channel+ header naming a different channel is a 422
|
|
12
|
+
# 2. +X-Spree-Channel+ header value matched against +channels.code+ —
|
|
10
13
|
# or, if it looks like a prefixed ID (+ch_…+), against +channels.id+
|
|
11
14
|
# — scoped to the current store
|
|
12
|
-
#
|
|
15
|
+
# 3. +current_store.default_channel+
|
|
13
16
|
#
|
|
14
17
|
# The concern is a no-op if no channel matches — callers fall back to
|
|
15
18
|
# +Spree::Current.channel+'s store-default behavior.
|
|
19
|
+
#
|
|
20
|
+
# IMPORTANT: key-bound resolution requires +authenticate_api_key!+ to run
|
|
21
|
+
# BEFORE +set_current_channel+ — both Store API branches order their
|
|
22
|
+
# callbacks accordingly (prepended in Store::BaseController, inherited
|
|
23
|
+
# ahead of the include in Store::ResourceController).
|
|
16
24
|
module ChannelResolution
|
|
17
25
|
extend ActiveSupport::Concern
|
|
18
26
|
|
|
19
27
|
CHANNEL_HEADER = 'X-Spree-Channel'.freeze
|
|
20
28
|
|
|
21
29
|
included do
|
|
30
|
+
# Prepended: LocaleAndCurrency's set_locale/set_currency callbacks
|
|
31
|
+
# (registered up in V3::BaseController) consult Spree::Current's
|
|
32
|
+
# fallback chain, which must derive from the REQUEST's store — not
|
|
33
|
+
# the Store.default fallback — before they run.
|
|
34
|
+
prepend_before_action :set_current_store_context
|
|
22
35
|
before_action :set_current_channel
|
|
23
36
|
end
|
|
24
37
|
|
|
25
38
|
protected
|
|
26
39
|
|
|
27
40
|
def current_channel
|
|
28
|
-
@current_channel ||= channel_from_header || Spree::Current.channel
|
|
41
|
+
@current_channel ||= channel_from_api_key || channel_from_header || Spree::Current.channel
|
|
29
42
|
end
|
|
30
43
|
|
|
31
44
|
private
|
|
32
45
|
|
|
33
|
-
# Only write to Spree::Current when the header resolves a
|
|
34
|
-
# channel. The store-default fallback is handled lazily by
|
|
46
|
+
# Only write to Spree::Current when the key or header resolves a
|
|
47
|
+
# specific channel. The store-default fallback is handled lazily by
|
|
35
48
|
# +Spree::Current.channel+ itself, which avoids one query per
|
|
36
|
-
# header-less API request.
|
|
49
|
+
# unbound, header-less API request.
|
|
50
|
+
def set_current_store_context
|
|
51
|
+
Spree::Current.store = current_store
|
|
52
|
+
end
|
|
53
|
+
|
|
37
54
|
def set_current_channel
|
|
55
|
+
bound = channel_from_api_key
|
|
56
|
+
|
|
57
|
+
if bound
|
|
58
|
+
header = channel_from_header
|
|
59
|
+
if header && header.id != bound.id
|
|
60
|
+
render_error(
|
|
61
|
+
code: ErrorHandler::ERROR_CODES[:channel_mismatch],
|
|
62
|
+
message: Spree.t('api.errors.channel_mismatch', default: 'The requested channel does not match the channel this API key is bound to'),
|
|
63
|
+
status: :unprocessable_entity
|
|
64
|
+
)
|
|
65
|
+
return
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
unless bound.active?
|
|
69
|
+
render_error(
|
|
70
|
+
code: ErrorHandler::ERROR_CODES[:channel_inactive],
|
|
71
|
+
message: Spree.t('api.errors.channel_inactive', default: 'The channel this API key is bound to is not active'),
|
|
72
|
+
status: :forbidden
|
|
73
|
+
)
|
|
74
|
+
return
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
38
78
|
# Memoize so a later +current_channel+ call (e.g. the storefront gate,
|
|
39
|
-
# serializer params) reuses this row instead of re-querying
|
|
40
|
-
@current_channel = channel_from_header
|
|
79
|
+
# serializer params) reuses this row instead of re-querying.
|
|
80
|
+
@current_channel = bound || channel_from_header
|
|
41
81
|
Spree::Current.channel = @current_channel if @current_channel
|
|
42
82
|
end
|
|
43
83
|
|
|
84
|
+
def channel_from_api_key
|
|
85
|
+
return nil unless respond_to?(:current_api_key, true)
|
|
86
|
+
|
|
87
|
+
current_api_key&.channel
|
|
88
|
+
end
|
|
89
|
+
|
|
44
90
|
def channel_from_header
|
|
45
91
|
value = request.headers[CHANNEL_HEADER].presence
|
|
46
92
|
return nil if value.blank?
|
|
@@ -21,6 +21,10 @@ module Spree
|
|
|
21
21
|
record_not_found: 'record_not_found',
|
|
22
22
|
resource_invalid: 'resource_invalid',
|
|
23
23
|
|
|
24
|
+
# Channel errors
|
|
25
|
+
channel_mismatch: 'channel_mismatch',
|
|
26
|
+
channel_inactive: 'channel_inactive',
|
|
27
|
+
|
|
24
28
|
# Cart errors
|
|
25
29
|
cart_not_found: 'cart_not_found',
|
|
26
30
|
cart_cannot_transition: 'cart_cannot_transition',
|
|
@@ -87,14 +87,14 @@ module Spree
|
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
# `key_type` and `
|
|
91
|
-
# Spree::ApiKey); update is limited to the human-facing `name`.
|
|
92
|
-
# them here keeps them out of mass assignment so a rename
|
|
93
|
-
# rather than 422.
|
|
90
|
+
# `key_type`, `scopes`, and `channel_id` are create-only (immutability
|
|
91
|
+
# lives on Spree::ApiKey); update is limited to the human-facing `name`.
|
|
92
|
+
# Stripping them here keeps them out of mass assignment so a rename
|
|
93
|
+
# returns 200 rather than 422.
|
|
94
94
|
def permitted_params
|
|
95
95
|
return params.permit(:name) if action_name == 'update'
|
|
96
96
|
|
|
97
|
-
params.permit(:name, :key_type, scopes: [])
|
|
97
|
+
params.permit(:name, :key_type, :channel_id, scopes: [])
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
private
|
|
@@ -14,8 +14,13 @@ module Spree
|
|
|
14
14
|
# +allow_guest_storefront_access!+.
|
|
15
15
|
include Spree::Api::V3::StorefrontGating
|
|
16
16
|
|
|
17
|
-
# Require publishable API key for all Store API requests
|
|
18
|
-
|
|
17
|
+
# Require publishable API key for all Store API requests. Prepended so
|
|
18
|
+
# the key is authenticated BEFORE ChannelResolution's
|
|
19
|
+
# +set_current_channel+ and the StorefrontGating 401 guard run —
|
|
20
|
+
# a channel-bound key must resolve its channel ahead of the gate, and
|
|
21
|
+
# this matches Store::ResourceController, where +authenticate_request!+
|
|
22
|
+
# is inherited ahead of both concerns.
|
|
23
|
+
prepend_before_action :authenticate_api_key!
|
|
19
24
|
end
|
|
20
25
|
end
|
|
21
26
|
end
|
|
@@ -141,8 +141,13 @@ module Spree
|
|
|
141
141
|
Spree.api.cart_serializer
|
|
142
142
|
end
|
|
143
143
|
|
|
144
|
+
# Channel-scoped: a storefront surface lists only its own channel's
|
|
145
|
+
# carts — without this, a blended DTC + wholesale storefront leaks
|
|
146
|
+
# the user's wholesale cart into the DTC surface (and vice versa).
|
|
147
|
+
# Explicit by-ID lookups (show/update) stay cross-channel so a shared
|
|
148
|
+
# checkout can load any cart the caller is authorized for.
|
|
144
149
|
def scope
|
|
145
|
-
current_store.carts.where(user: current_user).order(updated_at: :desc)
|
|
150
|
+
current_store.carts.where(user: current_user, channel: current_channel).order(updated_at: :desc)
|
|
146
151
|
end
|
|
147
152
|
|
|
148
153
|
private
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
module Store
|
|
5
|
+
# Exposes the channel context the request resolved to (bound key →
|
|
6
|
+
# X-Spree-Channel header → store default) so a storefront can read its
|
|
7
|
+
# own identity and access posture. Deliberately a singular resource:
|
|
8
|
+
# the Store API never enumerates a store's channels — that would leak
|
|
9
|
+
# the existence of gated surfaces (wholesale, POS) to anyone holding
|
|
10
|
+
# the publishable key.
|
|
11
|
+
class ChannelController < Store::BaseController
|
|
12
|
+
# A gated storefront must be able to read "this channel requires
|
|
13
|
+
# login" before authentication to render a sign-in wall instead of
|
|
14
|
+
# an error page.
|
|
15
|
+
allow_guest_storefront_access!
|
|
16
|
+
|
|
17
|
+
# GET /api/v3/store/channel
|
|
18
|
+
def show
|
|
19
|
+
render json: serialize_resource(current_channel)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
protected
|
|
23
|
+
|
|
24
|
+
def serializer_class
|
|
25
|
+
Spree.api.channel_serializer
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -9,6 +9,18 @@ module Spree
|
|
|
9
9
|
include Spree::Api::V3::ChannelResolution
|
|
10
10
|
include Spree::Api::V3::StorefrontGating
|
|
11
11
|
|
|
12
|
+
# The inherited +set_parent+/+set_resource+ callbacks were registered
|
|
13
|
+
# before the two concerns above, so they'd run resource lookups ahead
|
|
14
|
+
# of channel resolution (wrong channel scoping for X-Spree-Channel /
|
|
15
|
+
# key-bound requests) and ahead of the login gate (guests could probe
|
|
16
|
+
# resource existence on a gated channel via 404s). Re-register them so
|
|
17
|
+
# lookups always run inside the resolved channel context, behind the
|
|
18
|
+
# gate.
|
|
19
|
+
skip_before_action :set_parent
|
|
20
|
+
skip_before_action :set_resource
|
|
21
|
+
before_action :set_parent
|
|
22
|
+
before_action :set_resource, only: [:show, :update, :destroy]
|
|
23
|
+
|
|
12
24
|
protected
|
|
13
25
|
|
|
14
26
|
def authenticate_request!
|
|
@@ -13,10 +13,15 @@ module Spree
|
|
|
13
13
|
|
|
14
14
|
# Accept optional second argument for backward compatibility with jobs
|
|
15
15
|
# enqueued before this change was deployed.
|
|
16
|
-
|
|
16
|
+
#
|
|
17
|
+
# `payload_secrets` carries credentials withheld from the persisted payload
|
|
18
|
+
# so the outgoing request body stays complete. See
|
|
19
|
+
# {Spree::WebhookPayloadRedaction}.
|
|
20
|
+
def perform(delivery_id, _deprecated_secret_key = nil, payload_secrets: nil)
|
|
17
21
|
delivery = Spree::WebhookDelivery.find_by(id: delivery_id)
|
|
18
22
|
return if delivery.nil?
|
|
19
23
|
|
|
24
|
+
delivery.payload_secrets = payload_secrets
|
|
20
25
|
secret_key = delivery.webhook_endpoint.secret_key
|
|
21
26
|
Spree::Webhooks::DeliverWebhook.call(delivery: delivery, secret_key: secret_key)
|
|
22
27
|
end
|
|
@@ -18,12 +18,17 @@ module Spree
|
|
|
18
18
|
scopes: [:string, multi: true],
|
|
19
19
|
revoked_at: [:string, nullable: true],
|
|
20
20
|
last_used_at: [:string, nullable: true],
|
|
21
|
-
created_by_email: [:string, nullable: true]
|
|
21
|
+
created_by_email: [:string, nullable: true],
|
|
22
|
+
channel_id: [:string, nullable: true]
|
|
22
23
|
|
|
23
24
|
attributes :name, :key_type, :token_prefix, :scopes,
|
|
24
25
|
created_at: :iso8601, updated_at: :iso8601,
|
|
25
26
|
revoked_at: :iso8601, last_used_at: :iso8601
|
|
26
27
|
|
|
28
|
+
attribute :channel_id do |key|
|
|
29
|
+
key.channel&.prefixed_id
|
|
30
|
+
end
|
|
31
|
+
|
|
27
32
|
# Returned only on the create response — `plaintext_token` is held in
|
|
28
33
|
# memory on the model after `generate_token` and is never persisted
|
|
29
34
|
# for secret keys, so we serialize it whenever it's available rather
|
|
@@ -5,12 +5,11 @@ module Spree
|
|
|
5
5
|
# Serializes Spree::CustomerGroup for the admin pickers
|
|
6
6
|
# (e.g. promotion rule, customer-group filters). Surfaces only
|
|
7
7
|
# what the admin UI needs to display + select rows.
|
|
8
|
-
class CustomerGroupSerializer < V3::
|
|
9
|
-
typelize
|
|
10
|
-
description: 'string | null',
|
|
8
|
+
class CustomerGroupSerializer < V3::CustomerGroupSerializer
|
|
9
|
+
typelize description: 'string | null',
|
|
11
10
|
customers_count: :number
|
|
12
11
|
|
|
13
|
-
attributes :
|
|
12
|
+
attributes :description, :customers_count,
|
|
14
13
|
created_at: :iso8601, updated_at: :iso8601
|
|
15
14
|
|
|
16
15
|
# Members are paginated separately via `/customers?customer_group_id_in=…`
|
|
@@ -25,10 +25,15 @@ module Spree
|
|
|
25
25
|
|
|
26
26
|
attributes :event_name, :event_id, :response_code, :execution_time,
|
|
27
27
|
:error_type, :request_errors, :response_body, :success,
|
|
28
|
-
:payload,
|
|
29
28
|
created_at: :iso8601, updated_at: :iso8601,
|
|
30
29
|
delivered_at: :iso8601
|
|
31
30
|
|
|
31
|
+
# Redacted again on read: deliveries written before payload redaction
|
|
32
|
+
# shipped still hold live credentials in the column.
|
|
33
|
+
attribute :payload do |delivery|
|
|
34
|
+
Spree::WebhookPayloadRedaction.split(delivery.payload).first
|
|
35
|
+
end
|
|
36
|
+
|
|
32
37
|
attribute :webhook_endpoint_id do |delivery|
|
|
33
38
|
delivery.webhook_endpoint&.prefixed_id
|
|
34
39
|
end
|
|
@@ -5,7 +5,7 @@ module Spree
|
|
|
5
5
|
# Pre-purchase cart data with checkout progression info
|
|
6
6
|
class CartSerializer < BaseSerializer
|
|
7
7
|
typelize number: :string, current_step: :string, completed_steps: 'string[]', token: :string, email: [:string, nullable: true],
|
|
8
|
-
customer_note: [:string, nullable: true], market_id: [:string, nullable: true],
|
|
8
|
+
customer_note: [:string, nullable: true], market_id: [:string, nullable: true], channel_id: [:string, nullable: true],
|
|
9
9
|
currency: :string, locale: [:string, nullable: true], total_quantity: :number,
|
|
10
10
|
requirements: 'Array<{step: string, field: string, message: string}>',
|
|
11
11
|
item_total: [:string, nullable: true], display_item_total: [:string, nullable: true],
|
|
@@ -34,6 +34,13 @@ module Spree
|
|
|
34
34
|
order.market&.prefixed_id
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
# Which surface this cart belongs to — lets a multi-channel storefront
|
|
38
|
+
# verify a cookie-persisted cart against its own channel before
|
|
39
|
+
# adopting it (see the wholesale reference storefront's cart guard).
|
|
40
|
+
attribute :channel_id do |order|
|
|
41
|
+
order.channel&.prefixed_id
|
|
42
|
+
end
|
|
43
|
+
|
|
37
44
|
attributes :number, :token, :email, :customer_note,
|
|
38
45
|
:currency, :locale, :total_quantity, :warnings
|
|
39
46
|
|
|
@@ -5,9 +5,17 @@ module Spree
|
|
|
5
5
|
typelize name: :string,
|
|
6
6
|
code: :string,
|
|
7
7
|
active: :boolean,
|
|
8
|
-
default: :boolean
|
|
8
|
+
default: :boolean,
|
|
9
|
+
storefront_access: :string,
|
|
10
|
+
guest_checkout: :boolean
|
|
9
11
|
|
|
10
12
|
attributes :name, :code, :active, :default
|
|
13
|
+
|
|
14
|
+
# Resolved (channel → store fallback) values — what a client should act
|
|
15
|
+
# on, as opposed to the raw nullable preferences on the admin serializer.
|
|
16
|
+
attribute :storefront_access, &:resolved_storefront_access
|
|
17
|
+
|
|
18
|
+
attribute :guest_checkout, &:resolved_guest_checkout
|
|
11
19
|
end
|
|
12
20
|
end
|
|
13
21
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
# Minimal customer-group shape for the Store API. Exposed on
|
|
5
|
+
# `customers/me` so a storefront can branch on membership (e.g. an
|
|
6
|
+
# approved wholesale buyer) — pricing itself always resolves
|
|
7
|
+
# server-side.
|
|
8
|
+
class CustomerGroupSerializer < BaseSerializer
|
|
9
|
+
typelize name: :string
|
|
10
|
+
|
|
11
|
+
attributes :name
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -37,6 +37,14 @@ module Spree
|
|
|
37
37
|
store = params&.dig(:store) || Spree::Current.store
|
|
38
38
|
user.newsletter_subscriber(store)
|
|
39
39
|
end
|
|
40
|
+
|
|
41
|
+
# Membership signal for storefront branching (e.g. wholesale approval);
|
|
42
|
+
# scoped to the request store (params first — Spree::Current.store falls
|
|
43
|
+
# back to the DEFAULT store, wrong on sibling stores' domains) so other
|
|
44
|
+
# stores' memberships never leak.
|
|
45
|
+
many :customer_groups,
|
|
46
|
+
proc { |groups, params| groups.for_store(params&.dig(:store) || Spree::Current.store) },
|
|
47
|
+
resource: proc { Spree.api.customer_group_serializer }
|
|
40
48
|
end
|
|
41
49
|
end
|
|
42
50
|
end
|
|
@@ -56,7 +56,7 @@ module Spree
|
|
|
56
56
|
'X-Spree-Webhook-Timestamp' => webhook_timestamp.to_s,
|
|
57
57
|
'X-Spree-Webhook-Event' => @delivery.event_name
|
|
58
58
|
}
|
|
59
|
-
body =
|
|
59
|
+
body = payload_json
|
|
60
60
|
http_options = { open_timeout: TIMEOUT, read_timeout: TIMEOUT, verify_mode: ssl_verify_mode }
|
|
61
61
|
|
|
62
62
|
# SSRF protection is disabled in development so webhooks can reach
|
|
@@ -77,10 +77,14 @@ module Spree
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def generate_signature
|
|
80
|
-
payload_json = @delivery.payload.to_json
|
|
81
80
|
OpenSSL::HMAC.hexdigest('SHA256', @secret_key, "#{webhook_timestamp}.#{payload_json}")
|
|
82
81
|
end
|
|
83
82
|
|
|
83
|
+
# Memoized so the signature is computed over exactly the bytes sent.
|
|
84
|
+
def payload_json
|
|
85
|
+
@payload_json ||= @delivery.deliverable_payload.to_json
|
|
86
|
+
end
|
|
87
|
+
|
|
84
88
|
def webhook_timestamp
|
|
85
89
|
@webhook_timestamp ||= Time.current.to_i
|
|
86
90
|
end
|
|
@@ -56,13 +56,16 @@ module Spree
|
|
|
56
56
|
)
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
# Live credentials go over the wire but never into the delivery log.
|
|
60
|
+
persisted_payload, secrets = Spree::WebhookPayloadRedaction.split(payload)
|
|
61
|
+
|
|
59
62
|
delivery = endpoint.webhook_deliveries.create!(
|
|
60
63
|
event_name: event.name,
|
|
61
64
|
event_id: event.id,
|
|
62
|
-
payload:
|
|
65
|
+
payload: persisted_payload
|
|
63
66
|
)
|
|
64
67
|
|
|
65
|
-
Spree::WebhookDeliveryJob.perform_later(delivery.id)
|
|
68
|
+
Spree::WebhookDeliveryJob.perform_later(delivery.id, payload_secrets: secrets)
|
|
66
69
|
rescue ActiveRecord::RecordNotUnique
|
|
67
70
|
# Race condition: another thread already created this delivery — safe to ignore
|
|
68
71
|
rescue StandardError => e
|
data/config/locales/en.yml
CHANGED
|
@@ -10,6 +10,8 @@ en:
|
|
|
10
10
|
invalid_taxonomy_id: Invalid taxonomy id.
|
|
11
11
|
must_specify_api_key: You must specify an API key.
|
|
12
12
|
errors:
|
|
13
|
+
channel_inactive: The channel this API key is bound to is not active.
|
|
14
|
+
channel_mismatch: The requested channel does not match the channel this API key is bound to.
|
|
13
15
|
guest_checkout_not_allowed: You must be signed in to complete checkout.
|
|
14
16
|
storefront_login_required: Authentication required to access this store.
|
|
15
17
|
negative_quantity: quantity is negative
|
data/config/routes.rb
CHANGED
|
@@ -20,6 +20,9 @@ Spree::Core::Engine.add_routes do
|
|
|
20
20
|
resources :currencies, only: [:index]
|
|
21
21
|
resources :locales, only: [:index]
|
|
22
22
|
|
|
23
|
+
# Current channel context (never a list — see Store::ChannelController)
|
|
24
|
+
resource :channel, only: [:show], controller: 'channel'
|
|
25
|
+
|
|
23
26
|
# Catalog
|
|
24
27
|
resources :products, only: [:index, :show] do
|
|
25
28
|
collection do
|
|
@@ -112,6 +112,7 @@ module Spree
|
|
|
112
112
|
fulfillment_serializer: 'Spree::Api::V3::FulfillmentSerializer',
|
|
113
113
|
address_serializer: 'Spree::Api::V3::AddressSerializer',
|
|
114
114
|
customer_serializer: 'Spree::Api::V3::CustomerSerializer',
|
|
115
|
+
customer_group_serializer: 'Spree::Api::V3::CustomerGroupSerializer',
|
|
115
116
|
country_serializer: 'Spree::Api::V3::CountrySerializer',
|
|
116
117
|
channel_serializer: 'Spree::Api::V3::ChannelSerializer',
|
|
117
118
|
product_publication_serializer: 'Spree::Api::V3::ProductPublicationSerializer',
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spree_api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.6.0.
|
|
4
|
+
version: 5.6.0.rc4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vendo Connect Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rswag-specs
|
|
@@ -72,14 +72,14 @@ dependencies:
|
|
|
72
72
|
requirements:
|
|
73
73
|
- - '='
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: 5.6.0.
|
|
75
|
+
version: 5.6.0.rc4
|
|
76
76
|
type: :runtime
|
|
77
77
|
prerelease: false
|
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
79
|
requirements:
|
|
80
80
|
- - '='
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: 5.6.0.
|
|
82
|
+
version: 5.6.0.rc4
|
|
83
83
|
description: Spree's API
|
|
84
84
|
email:
|
|
85
85
|
- hello@spreecommerce.org
|
|
@@ -189,6 +189,7 @@ files:
|
|
|
189
189
|
- app/controllers/spree/api/v3/store/carts/store_credits_controller.rb
|
|
190
190
|
- app/controllers/spree/api/v3/store/carts_controller.rb
|
|
191
191
|
- app/controllers/spree/api/v3/store/categories_controller.rb
|
|
192
|
+
- app/controllers/spree/api/v3/store/channel_controller.rb
|
|
192
193
|
- app/controllers/spree/api/v3/store/countries_controller.rb
|
|
193
194
|
- app/controllers/spree/api/v3/store/currencies_controller.rb
|
|
194
195
|
- app/controllers/spree/api/v3/store/customer/addresses_controller.rb
|
|
@@ -291,6 +292,7 @@ files:
|
|
|
291
292
|
- app/serializers/spree/api/v3/credit_card_serializer.rb
|
|
292
293
|
- app/serializers/spree/api/v3/currency_serializer.rb
|
|
293
294
|
- app/serializers/spree/api/v3/custom_field_serializer.rb
|
|
295
|
+
- app/serializers/spree/api/v3/customer_group_serializer.rb
|
|
294
296
|
- app/serializers/spree/api/v3/customer_return_serializer.rb
|
|
295
297
|
- app/serializers/spree/api/v3/customer_serializer.rb
|
|
296
298
|
- app/serializers/spree/api/v3/delivery_method_serializer.rb
|
|
@@ -380,9 +382,9 @@ licenses:
|
|
|
380
382
|
- BSD-3-Clause
|
|
381
383
|
metadata:
|
|
382
384
|
bug_tracker_uri: https://github.com/spree/spree/issues
|
|
383
|
-
changelog_uri: https://github.com/spree/spree/releases/tag/v5.6.0.
|
|
385
|
+
changelog_uri: https://github.com/spree/spree/releases/tag/v5.6.0.rc4
|
|
384
386
|
documentation_uri: https://docs.spreecommerce.org/
|
|
385
|
-
source_code_uri: https://github.com/spree/spree/tree/v5.6.0.
|
|
387
|
+
source_code_uri: https://github.com/spree/spree/tree/v5.6.0.rc4
|
|
386
388
|
post_install_message:
|
|
387
389
|
rdoc_options: []
|
|
388
390
|
require_paths:
|