spree_api 5.4.0.beta5 → 5.4.0.beta7

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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/app/controllers/concerns/spree/api/v3/cart_resolvable.rb +45 -0
  4. data/app/controllers/concerns/spree/api/v3/error_handler.rb +20 -15
  5. data/app/controllers/concerns/spree/api/v3/order_lock.rb +4 -4
  6. data/app/controllers/spree/api/v3/store/carts/coupon_codes_controller.rb +57 -0
  7. data/app/controllers/spree/api/v3/store/{orders/line_items_controller.rb → carts/items_controller.rb} +18 -31
  8. data/app/controllers/spree/api/v3/store/carts/payment_methods_controller.rb +24 -0
  9. data/app/controllers/spree/api/v3/store/{orders → carts}/payment_sessions_controller.rb +14 -19
  10. data/app/controllers/spree/api/v3/store/carts/payments_controller.rb +72 -0
  11. data/app/controllers/spree/api/v3/store/carts/shipments_controller.rb +65 -0
  12. data/app/controllers/spree/api/v3/store/{orders → carts}/store_credits_controller.rb +9 -10
  13. data/app/controllers/spree/api/v3/store/carts_controller.rb +176 -0
  14. data/app/controllers/spree/api/v3/store/customer/orders_controller.rb +1 -1
  15. data/app/controllers/spree/api/v3/store/orders_controller.rb +21 -114
  16. data/app/controllers/spree/api/v3/webhooks/payments_controller.rb +52 -0
  17. data/app/serializers/spree/api/v3/admin/order_serializer.rb +2 -2
  18. data/app/serializers/spree/api/v3/cart_promotion_serializer.rb +18 -0
  19. data/app/serializers/spree/api/v3/cart_serializer.rb +56 -0
  20. data/app/serializers/spree/api/v3/order_serializer.rb +7 -9
  21. data/config/locales/en.yml +4 -0
  22. data/config/routes.rb +21 -24
  23. data/lib/spree/api/dependencies.rb +5 -0
  24. data/lib/spree/api/openapi/schema_helper.rb +28 -0
  25. metadata +18 -15
  26. data/app/controllers/concerns/spree/api/v3/order_concern.rb +0 -46
  27. data/app/controllers/spree/api/v3/store/cart_controller.rb +0 -97
  28. data/app/controllers/spree/api/v3/store/orders/coupon_codes_controller.rb +0 -73
  29. data/app/controllers/spree/api/v3/store/orders/payment_methods_controller.rb +0 -43
  30. data/app/controllers/spree/api/v3/store/orders/payments_controller.rb +0 -45
  31. data/app/controllers/spree/api/v3/store/orders/shipments_controller.rb +0 -56
@@ -84,6 +84,15 @@ module Spree
84
84
  user: { '$ref' => '#/components/schemas/Customer' }
85
85
  },
86
86
  required: %w[token user]
87
+ },
88
+ CheckoutRequirement: {
89
+ type: :object,
90
+ properties: {
91
+ step: { type: :string, description: 'Checkout step this requirement belongs to', example: 'payment' },
92
+ field: { type: :string, description: 'Field that needs to be satisfied', example: 'payment' },
93
+ message: { type: :string, description: 'Human-readable requirement message', example: 'Add a payment method' }
94
+ },
95
+ required: %w[step field message]
87
96
  }
88
97
  }
89
98
  end
@@ -110,10 +119,29 @@ module Spree
110
119
  s[:'x-typelizer'] = true
111
120
  strip_null_from_enums(s)
112
121
  end
122
+ patch_cart_schema(schemas)
113
123
  schemas
114
124
  end
115
125
  end
116
126
 
127
+ # Typelizer cannot represent Array<{...}> inline object types in OpenAPI,
128
+ # so we patch them to reference manually-defined component schemas.
129
+ def patch_cart_schema(schemas)
130
+ cart = schemas['Cart'] || schemas[:Cart]
131
+ return unless cart
132
+
133
+ props = cart[:properties]
134
+ return unless props
135
+
136
+ req_key = props.key?('requirements') ? 'requirements' : :requirements
137
+ if props[req_key]
138
+ props[req_key] = {
139
+ type: :array,
140
+ items: { '$ref' => '#/components/schemas/CheckoutRequirement' }
141
+ }
142
+ end
143
+ end
144
+
117
145
  # Typelizer adds nil to enum arrays for nullable fields.
118
146
  # OpenAPI 3.0 handles nullability via `nullable: true`, so the nil entry is redundant
119
147
  # and causes issues with code generators.
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.4.0.beta5
4
+ version: 5.4.0.beta7
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-03-10 00:00:00.000000000 Z
11
+ date: 2026-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rswag-specs
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - '='
88
88
  - !ruby/object:Gem::Version
89
- version: 5.4.0.beta5
89
+ version: 5.4.0.beta7
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - '='
95
95
  - !ruby/object:Gem::Version
96
- version: 5.4.0.beta5
96
+ version: 5.4.0.beta7
97
97
  description: Spree's API
98
98
  email:
99
99
  - hello@spreecommerce.org
@@ -104,12 +104,12 @@ files:
104
104
  - README.md
105
105
  - Rakefile
106
106
  - app/controllers/concerns/spree/api/v3/api_key_authentication.rb
107
+ - app/controllers/concerns/spree/api/v3/cart_resolvable.rb
107
108
  - app/controllers/concerns/spree/api/v3/error_handler.rb
108
109
  - app/controllers/concerns/spree/api/v3/http_caching.rb
109
110
  - app/controllers/concerns/spree/api/v3/idempotent.rb
110
111
  - app/controllers/concerns/spree/api/v3/jwt_authentication.rb
111
112
  - app/controllers/concerns/spree/api/v3/locale_and_currency.rb
112
- - app/controllers/concerns/spree/api/v3/order_concern.rb
113
113
  - app/controllers/concerns/spree/api/v3/order_lock.rb
114
114
  - app/controllers/concerns/spree/api/v3/rate_limit_headers.rb
115
115
  - app/controllers/concerns/spree/api/v3/resource_serializer.rb
@@ -120,7 +120,14 @@ files:
120
120
  - app/controllers/spree/api/v3/resource_controller.rb
121
121
  - app/controllers/spree/api/v3/store/auth_controller.rb
122
122
  - app/controllers/spree/api/v3/store/base_controller.rb
123
- - app/controllers/spree/api/v3/store/cart_controller.rb
123
+ - app/controllers/spree/api/v3/store/carts/coupon_codes_controller.rb
124
+ - app/controllers/spree/api/v3/store/carts/items_controller.rb
125
+ - app/controllers/spree/api/v3/store/carts/payment_methods_controller.rb
126
+ - app/controllers/spree/api/v3/store/carts/payment_sessions_controller.rb
127
+ - app/controllers/spree/api/v3/store/carts/payments_controller.rb
128
+ - app/controllers/spree/api/v3/store/carts/shipments_controller.rb
129
+ - app/controllers/spree/api/v3/store/carts/store_credits_controller.rb
130
+ - app/controllers/spree/api/v3/store/carts_controller.rb
124
131
  - app/controllers/spree/api/v3/store/categories/products_controller.rb
125
132
  - app/controllers/spree/api/v3/store/categories_controller.rb
126
133
  - app/controllers/spree/api/v3/store/countries_controller.rb
@@ -135,19 +142,13 @@ files:
135
142
  - app/controllers/spree/api/v3/store/locales_controller.rb
136
143
  - app/controllers/spree/api/v3/store/markets/countries_controller.rb
137
144
  - app/controllers/spree/api/v3/store/markets_controller.rb
138
- - app/controllers/spree/api/v3/store/orders/coupon_codes_controller.rb
139
- - app/controllers/spree/api/v3/store/orders/line_items_controller.rb
140
- - app/controllers/spree/api/v3/store/orders/payment_methods_controller.rb
141
- - app/controllers/spree/api/v3/store/orders/payment_sessions_controller.rb
142
- - app/controllers/spree/api/v3/store/orders/payments_controller.rb
143
- - app/controllers/spree/api/v3/store/orders/shipments_controller.rb
144
- - app/controllers/spree/api/v3/store/orders/store_credits_controller.rb
145
145
  - app/controllers/spree/api/v3/store/orders_controller.rb
146
146
  - app/controllers/spree/api/v3/store/products/filters_controller.rb
147
147
  - app/controllers/spree/api/v3/store/products_controller.rb
148
148
  - app/controllers/spree/api/v3/store/resource_controller.rb
149
149
  - app/controllers/spree/api/v3/store/wishlist_items_controller.rb
150
150
  - app/controllers/spree/api/v3/store/wishlists_controller.rb
151
+ - app/controllers/spree/api/v3/webhooks/payments_controller.rb
151
152
  - app/jobs/spree/api_keys/mark_as_used.rb
152
153
  - app/jobs/spree/webhook_delivery_job.rb
153
154
  - app/serializers/spree/api/v3/address_serializer.rb
@@ -185,6 +186,8 @@ files:
185
186
  - app/serializers/spree/api/v3/admin/variant_serializer.rb
186
187
  - app/serializers/spree/api/v3/asset_serializer.rb
187
188
  - app/serializers/spree/api/v3/base_serializer.rb
189
+ - app/serializers/spree/api/v3/cart_promotion_serializer.rb
190
+ - app/serializers/spree/api/v3/cart_serializer.rb
188
191
  - app/serializers/spree/api/v3/category_serializer.rb
189
192
  - app/serializers/spree/api/v3/country_serializer.rb
190
193
  - app/serializers/spree/api/v3/credit_card_serializer.rb
@@ -263,9 +266,9 @@ licenses:
263
266
  - BSD-3-Clause
264
267
  metadata:
265
268
  bug_tracker_uri: https://github.com/spree/spree/issues
266
- changelog_uri: https://github.com/spree/spree/releases/tag/v5.4.0.beta5
269
+ changelog_uri: https://github.com/spree/spree/releases/tag/v5.4.0.beta7
267
270
  documentation_uri: https://docs.spreecommerce.org/
268
- source_code_uri: https://github.com/spree/spree/tree/v5.4.0.beta5
271
+ source_code_uri: https://github.com/spree/spree/tree/v5.4.0.beta7
269
272
  post_install_message:
270
273
  rdoc_options: []
271
274
  require_paths:
@@ -1,46 +0,0 @@
1
- module Spree
2
- module Api
3
- module V3
4
- module OrderConcern
5
- extend ActiveSupport::Concern
6
-
7
- # Allow access to order via order token for guests or authenticated users
8
- # Expects @parent to be set to the order
9
- def authorize_order_access!
10
- authorize!(:update, @parent, order_token)
11
- end
12
-
13
- def set_parent
14
- return if params[:order_id].blank?
15
-
16
- @parent = order_scope.find_by_prefix_id!(params[:order_id])
17
- end
18
-
19
- def order_scope
20
- base = current_store.orders
21
- base = if current_user
22
- base.where(user: current_user)
23
- elsif order_token.present?
24
- base.where(token: order_token)
25
- else
26
- base.none
27
- end
28
- base.preload_associations_lazily
29
- end
30
-
31
- protected
32
-
33
- # Render the parent order as JSON using the order serializer.
34
- # Use this when sub-resource mutations should return the updated order
35
- # (e.g., line items, coupon codes, store credits).
36
- def render_order(status: :ok)
37
- render json: Spree.api.order_serializer.new(@parent.reload, params: serializer_params).to_h, status: status
38
- end
39
-
40
- def order_token
41
- request.headers['x-spree-order-token']
42
- end
43
- end
44
- end
45
- end
46
- end
@@ -1,97 +0,0 @@
1
- module Spree
2
- module Api
3
- module V3
4
- module Store
5
- class CartController < Store::BaseController
6
- include Spree::Api::V3::OrderConcern
7
-
8
- before_action :require_authentication!, only: [:associate]
9
-
10
- # POST /api/v3/store/cart
11
- # Creates a new shopping cart (order)
12
- # Can be created by guests or authenticated customers
13
- def create
14
- result = Spree.cart_create_service.call(
15
- user: current_user,
16
- store: current_store,
17
- currency: current_currency,
18
- locale: current_locale,
19
- metadata: cart_params[:metadata] || {},
20
- line_items: cart_params[:line_items] || []
21
- )
22
-
23
- if result.success?
24
- @cart = result.value
25
- render json: serialize_resource(@cart), status: :created
26
- else
27
- render_service_error(result.error.to_s)
28
- end
29
- end
30
-
31
- # GET /api/v3/store/cart
32
- # Returns current incomplete order (cart)
33
- # Returns 404 if no cart exists - use POST /cart to create one
34
- # Authorize via order_token param or JWT Bearer token
35
- def show
36
- @cart = find_cart
37
-
38
- render json: serialize_resource(@cart)
39
- end
40
-
41
- # PATCH /api/v3/store/cart/associate
42
- # Associates a guest cart with the currently authenticated user
43
- # Requires: JWT authentication + order token (header or param)
44
- def associate
45
- @cart = find_cart_by_token
46
-
47
- result = Spree.cart_associate_service.call(guest_order: @cart, user: current_user, guest_only: true)
48
-
49
- if result.success?
50
- render json: serialize_resource(@cart)
51
- else
52
- render_service_error(result.error.to_s)
53
- end
54
- end
55
-
56
- protected
57
-
58
- def serializer_class
59
- Spree.api.order_serializer
60
- end
61
-
62
- private
63
-
64
- def cart_params
65
- params.permit(
66
- metadata: {},
67
- line_items: [:variant_id, :quantity, { metadata: {}, options: {} }]
68
- )
69
- end
70
-
71
- # Find incomplete cart by order token for associate action
72
- # Only finds guest carts (no user) or carts already owned by current user (idempotent)
73
- def find_cart_by_token
74
- current_store.orders.incomplete.where(user: [ nil, current_user ]).find_by!(token: order_token)
75
- end
76
-
77
- def find_cart
78
- scope = current_store.orders.incomplete
79
-
80
- # Try order_token first (guest checkout)
81
- if order_token.present?
82
- return scope.find_by!(token: order_token)
83
- end
84
-
85
- # Then try JWT authenticated user
86
- if current_user.present?
87
- cart = scope.where(user: current_user).order(created_at: :desc).first
88
- return cart if cart
89
- end
90
-
91
- raise ActiveRecord::RecordNotFound.new(nil, 'Spree::Order')
92
- end
93
- end
94
- end
95
- end
96
- end
97
- end
@@ -1,73 +0,0 @@
1
- module Spree
2
- module Api
3
- module V3
4
- module Store
5
- module Orders
6
- class CouponCodesController < ResourceController
7
- include Spree::Api::V3::OrderConcern
8
- include Spree::Api::V3::OrderLock
9
-
10
- before_action :authorize_order_access!
11
- skip_before_action :set_resource
12
-
13
- # POST /api/v3/store/orders/:order_id/coupon_codes
14
- # Apply a coupon code to the order
15
- def create
16
- with_order_lock do
17
- @parent.coupon_code = permitted_params[:code]
18
-
19
- coupon_handler.apply
20
-
21
- if coupon_handler.successful?
22
- render_order(status: :created)
23
- else
24
- render_errors(coupon_handler.error)
25
- end
26
- end
27
- end
28
-
29
- # DELETE /api/v3/store/orders/:order_id/coupon_codes/:id
30
- # Remove a coupon code from the order
31
- # :id is the promotion prefix_id (e.g., promo_xxx)
32
- def destroy
33
- with_order_lock do
34
- @resource = scope.find_by_prefix_id!(params[:id])
35
- coupon_code = @resource.code.presence || @resource.name
36
-
37
- coupon_handler.remove(coupon_code)
38
-
39
- if coupon_handler.successful?
40
- render_order
41
- else
42
- render_errors(coupon_handler.error)
43
- end
44
- end
45
- end
46
-
47
- protected
48
-
49
- def parent_association
50
- :order_promotions
51
- end
52
-
53
- def coupon_handler
54
- @coupon_handler ||= Spree.coupon_handler.new(@parent)
55
- end
56
-
57
- def permitted_params
58
- params.permit(:code)
59
- end
60
-
61
- def model_class
62
- Spree::OrderPromotion
63
- end
64
-
65
- def serializer_class
66
- Spree.api.order_promotion_serializer
67
- end
68
- end
69
- end
70
- end
71
- end
72
- end
73
- end
@@ -1,43 +0,0 @@
1
- module Spree
2
- module Api
3
- module V3
4
- module Store
5
- module Orders
6
- class PaymentMethodsController < Store::BaseController
7
- include Spree::Api::V3::OrderConcern
8
-
9
- before_action :set_parent
10
-
11
- # GET /api/v3/store/orders/:order_id/payment_methods
12
- # Returns available payment methods for the current order
13
- def index
14
- payment_methods = @parent.collect_frontend_payment_methods
15
- render json: {
16
- data: serialize_collection(payment_methods),
17
- meta: { count: payment_methods.size }
18
- }
19
- end
20
-
21
- protected
22
-
23
- def serializer_class
24
- Spree.api.payment_method_serializer
25
- end
26
-
27
- def serialize_collection(collection)
28
- collection.map { |item| serializer_class.new(item, params: serializer_params).to_h }
29
- end
30
-
31
- def serializer_params
32
- {
33
- currency: current_currency,
34
- store: current_store,
35
- user: current_user
36
- }
37
- end
38
- end
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,45 +0,0 @@
1
- module Spree
2
- module Api
3
- module V3
4
- module Store
5
- module Orders
6
- class PaymentsController < Store::BaseController
7
- include Spree::Api::V3::OrderConcern
8
- include Spree::Api::V3::ResourceSerializer
9
-
10
- before_action :set_parent
11
- before_action :set_payment, only: [:show]
12
-
13
- # GET /api/v3/store/orders/:order_id/payments
14
- def index
15
- payments = @parent.payments.includes(:payment_method)
16
- render json: {
17
- data: serialize_payments(payments),
18
- meta: {}
19
- }
20
- end
21
-
22
- # GET /api/v3/store/orders/:order_id/payments/:id
23
- def show
24
- render json: serialize_resource(@payment)
25
- end
26
-
27
- private
28
-
29
- def set_payment
30
- @payment = @parent.payments.find_by_prefix_id!(params[:id])
31
- end
32
-
33
- def serializer_class
34
- Spree.api.payment_serializer
35
- end
36
-
37
- def serialize_payments(payments)
38
- payments.map { |p| serializer_class.new(p, params: serializer_params).to_h }
39
- end
40
- end
41
- end
42
- end
43
- end
44
- end
45
- end
@@ -1,56 +0,0 @@
1
- module Spree
2
- module Api
3
- module V3
4
- module Store
5
- module Orders
6
- class ShipmentsController < ResourceController
7
- include Spree::Api::V3::OrderConcern
8
- include Spree::Api::V3::OrderLock
9
-
10
- before_action :authorize_order_access!
11
- skip_before_action :set_resource
12
- before_action :set_shipment, only: [:show, :update]
13
-
14
- # PATCH /api/v3/store/orders/:order_id/shipments/:id
15
- def update
16
- with_order_lock do
17
- if permitted_params[:selected_shipping_rate_id].present?
18
- shipping_rate = @resource.shipping_rates.find_by_prefix_id!(permitted_params[:selected_shipping_rate_id])
19
- @resource.selected_shipping_rate_id = shipping_rate.id
20
- @resource.save!
21
- end
22
-
23
- render_order
24
- end
25
- end
26
-
27
- protected
28
-
29
- def parent_association
30
- :shipments
31
- end
32
-
33
- def model_class
34
- Spree::Shipment
35
- end
36
-
37
- def serializer_class
38
- Spree.api.shipment_serializer
39
- end
40
-
41
- def permitted_params
42
- params.permit(:selected_shipping_rate_id)
43
- end
44
-
45
- private
46
-
47
- # Find shipment without additional authorization - order access already verified
48
- def set_shipment
49
- @resource = scope.find_by_prefix_id!(params[:id])
50
- end
51
- end
52
- end
53
- end
54
- end
55
- end
56
- end