spree_api 5.4.0.beta4 → 5.4.0.beta6

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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +4 -4
  3. data/app/controllers/concerns/spree/api/v3/api_key_authentication.rb +1 -1
  4. data/app/controllers/concerns/spree/api/v3/error_handler.rb +12 -2
  5. data/app/controllers/concerns/spree/api/v3/http_caching.rb +10 -2
  6. data/app/controllers/concerns/spree/api/v3/idempotent.rb +82 -0
  7. data/app/controllers/concerns/spree/api/v3/jwt_authentication.rb +4 -1
  8. data/app/controllers/concerns/spree/api/v3/order_concern.rb +1 -6
  9. data/app/controllers/concerns/spree/api/v3/rate_limit_headers.rb +31 -0
  10. data/app/controllers/concerns/spree/api/v3/resource_serializer.rb +30 -2
  11. data/app/controllers/concerns/spree/api/v3/security_headers.rb +4 -0
  12. data/app/controllers/spree/api/v3/admin/base_controller.rb +28 -0
  13. data/app/controllers/spree/api/v3/admin/resource_controller.rb +28 -0
  14. data/app/controllers/spree/api/v3/base_controller.rb +20 -1
  15. data/app/controllers/spree/api/v3/resource_controller.rb +35 -3
  16. data/app/controllers/spree/api/v3/store/auth_controller.rb +4 -24
  17. data/app/controllers/spree/api/v3/store/base_controller.rb +0 -11
  18. data/app/controllers/spree/api/v3/store/cart_controller.rb +6 -2
  19. data/app/controllers/spree/api/v3/store/categories/products_controller.rb +37 -0
  20. data/app/controllers/spree/api/v3/store/{taxons_controller.rb → categories_controller.rb} +8 -6
  21. data/app/controllers/spree/api/v3/store/countries_controller.rb +6 -0
  22. data/app/controllers/spree/api/v3/store/currencies_controller.rb +4 -0
  23. data/app/controllers/spree/api/v3/store/customers_controller.rb +102 -0
  24. data/app/controllers/spree/api/v3/store/locales_controller.rb +4 -0
  25. data/app/controllers/spree/api/v3/store/markets/countries_controller.rb +6 -0
  26. data/app/controllers/spree/api/v3/store/markets_controller.rb +8 -0
  27. data/app/controllers/spree/api/v3/store/orders/payments_controller.rb +38 -0
  28. data/app/controllers/spree/api/v3/store/orders_controller.rb +3 -2
  29. data/app/controllers/spree/api/v3/store/products/filters_controller.rb +9 -8
  30. data/app/controllers/spree/api/v3/store/products_controller.rb +12 -19
  31. data/app/jobs/spree/webhook_delivery_job.rb +4 -1
  32. data/app/serializers/spree/api/v3/admin/address_serializer.rb +24 -0
  33. data/app/serializers/spree/api/v3/admin/adjustment_serializer.rb +36 -0
  34. data/app/serializers/spree/api/v3/admin/admin_user_serializer.rb +15 -0
  35. data/app/serializers/spree/api/v3/admin/asset_serializer.rb +10 -0
  36. data/app/serializers/spree/api/v3/admin/category_serializer.rb +33 -0
  37. data/app/serializers/spree/api/v3/admin/credit_card_serializer.rb +22 -0
  38. data/app/serializers/spree/api/v3/admin/customer_serializer.rb +8 -6
  39. data/app/serializers/spree/api/v3/admin/digital_link_serializer.rb +10 -0
  40. data/app/serializers/spree/api/v3/admin/image_serializer.rb +10 -0
  41. data/app/serializers/spree/api/v3/admin/line_item_serializer.rb +36 -1
  42. data/app/serializers/spree/api/v3/admin/option_type_serializer.rb +13 -0
  43. data/app/serializers/spree/api/v3/admin/option_value_serializer.rb +13 -0
  44. data/app/serializers/spree/api/v3/admin/order_promotion_serializer.rb +10 -0
  45. data/app/serializers/spree/api/v3/admin/order_serializer.rb +47 -6
  46. data/app/serializers/spree/api/v3/admin/payment_method_serializer.rb +14 -0
  47. data/app/serializers/spree/api/v3/admin/payment_serializer.rb +56 -0
  48. data/app/serializers/spree/api/v3/admin/payment_source_serializer.rb +10 -0
  49. data/app/serializers/spree/api/v3/admin/product_serializer.rb +23 -6
  50. data/app/serializers/spree/api/v3/admin/refund_serializer.rb +21 -0
  51. data/app/serializers/spree/api/v3/admin/reimbursement_serializer.rb +13 -0
  52. data/app/serializers/spree/api/v3/admin/return_authorization_serializer.rb +17 -0
  53. data/app/serializers/spree/api/v3/admin/shipment_serializer.rb +44 -0
  54. data/app/serializers/spree/api/v3/admin/shipping_category_serializer.rb +14 -0
  55. data/app/serializers/spree/api/v3/admin/shipping_method_serializer.rb +11 -0
  56. data/app/serializers/spree/api/v3/admin/shipping_rate_serializer.rb +11 -0
  57. data/app/serializers/spree/api/v3/admin/stock_item_serializer.rb +17 -0
  58. data/app/serializers/spree/api/v3/admin/stock_location_serializer.rb +15 -0
  59. data/app/serializers/spree/api/v3/admin/store_credit_serializer.rb +27 -0
  60. data/app/serializers/spree/api/v3/admin/tax_category_serializer.rb +15 -0
  61. data/app/serializers/spree/api/v3/admin/variant_serializer.rb +11 -14
  62. data/app/serializers/spree/api/v3/base_serializer.rb +21 -4
  63. data/app/serializers/spree/api/v3/category_serializer.rb +71 -0
  64. data/app/serializers/spree/api/v3/country_serializer.rb +2 -1
  65. data/app/serializers/spree/api/v3/customer_serializer.rb +2 -1
  66. data/app/serializers/spree/api/v3/gift_card_serializer.rb +5 -21
  67. data/app/serializers/spree/api/v3/order_serializer.rb +2 -2
  68. data/app/serializers/spree/api/v3/payment_serializer.rb +1 -1
  69. data/app/serializers/spree/api/v3/product_serializer.rb +11 -11
  70. data/app/serializers/spree/api/v3/shipping_category_serializer.rb +11 -0
  71. data/app/serializers/spree/api/v3/shipping_rate_serializer.rb +2 -6
  72. data/app/serializers/spree/api/v3/tax_category_serializer.rb +11 -0
  73. data/app/serializers/spree/api/v3/variant_serializer.rb +4 -4
  74. data/app/serializers/spree/api/v3/wishlist_serializer.rb +1 -1
  75. data/app/services/spree/api/v3/filters_aggregator.rb +31 -25
  76. data/app/services/spree/webhooks/deliver_webhook.rb +23 -17
  77. data/app/subscribers/spree/webhook_event_subscriber.rb +1 -1
  78. data/config/initializers/alba.rb +1 -1
  79. data/config/initializers/typelizer.rb +26 -16
  80. data/config/locales/en.yml +5 -0
  81. data/config/routes.rb +10 -10
  82. data/lib/spree/api/configuration.rb +3 -1
  83. data/lib/spree/api/dependencies.rb +31 -5
  84. data/lib/spree/api/engine.rb +15 -0
  85. data/lib/spree/api/openapi/schema_helper.rb +27 -7
  86. data/lib/spree/api/testing_support/v3/base.rb +24 -1
  87. metadata +43 -19
  88. data/app/controllers/spree/api/v3/store/customer/account_controller.rb +0 -38
  89. data/app/controllers/spree/api/v3/store/stores_controller.rb +0 -26
  90. data/app/controllers/spree/api/v3/store/taxonomies_controller.rb +0 -19
  91. data/app/controllers/spree/api/v3/store/taxons/products_controller.rb +0 -37
  92. data/app/serializers/spree/api/v3/admin/taxon_serializer.rb +0 -20
  93. data/app/serializers/spree/api/v3/admin/taxonomy_serializer.rb +0 -15
  94. data/app/serializers/spree/api/v3/store_serializer.rb +0 -38
  95. data/app/serializers/spree/api/v3/taxon_serializer.rb +0 -78
  96. data/app/serializers/spree/api/v3/taxonomy_serializer.rb +0 -33
  97. data/app/services/spree/api/v3/orders/update.rb +0 -105
@@ -4,7 +4,7 @@ module Spree
4
4
  # Store API Order Serializer
5
5
  # Customer-facing order data
6
6
  class OrderSerializer < BaseSerializer
7
- typelize number: :string, state: :string, token: :string, email: [:string, nullable: true],
7
+ typelize number: :string, state: :string, checkout_steps: 'string[]', token: :string, email: [:string, nullable: true],
8
8
  special_instructions: [:string, nullable: true], currency: :string, locale: [:string, nullable: true], item_count: :number,
9
9
  state_lock_version: :number, shipment_state: [:string, nullable: true], payment_state: [:string, nullable: true],
10
10
  item_total: :string, display_item_total: :string,
@@ -17,7 +17,7 @@ module Spree
17
17
  total: :string, display_total: :string, completed_at: [:string, nullable: true],
18
18
  bill_address: { nullable: true }, ship_address: { nullable: true }
19
19
 
20
- attributes :number, :state, :token, :email, :special_instructions,
20
+ attributes :number, :state, :checkout_steps, :token, :email, :special_instructions,
21
21
  :currency, :locale, :item_count, :state_lock_version, :shipment_state, :payment_state,
22
22
  :item_total, :display_item_total, :ship_total, :display_ship_total,
23
23
  :adjustment_total, :display_adjustment_total, :promo_total, :display_promo_total,
@@ -6,7 +6,7 @@ module Spree
6
6
  number: :string, amount: :string, display_amount: :string,
7
7
  source_type: [:string, nullable: true, enum: %w[credit_card store_credit payment_source]],
8
8
  source_id: [:string, nullable: true],
9
- source: 'StoreCreditCard | StoreStoreCredit | StorePaymentSource | null'
9
+ source: 'CreditCard | StoreCredit | PaymentSource | null'
10
10
 
11
11
  attribute :payment_method_id do |payment|
12
12
  payment.payment_method&.prefixed_id
@@ -11,8 +11,8 @@ module Spree
11
11
  thumbnail_url: [:string, nullable: true],
12
12
  available_on: [:string, nullable: true],
13
13
  purchasable: :boolean, in_stock: :boolean, backorderable: :boolean, available: :boolean,
14
- price: 'StorePrice',
15
- original_price: ['StorePrice', nullable: true],
14
+ price: 'Price',
15
+ original_price: ['Price', nullable: true],
16
16
  tags: [:string, multi: true]
17
17
 
18
18
  attributes :name, :description, :slug,
@@ -71,37 +71,37 @@ module Spree
71
71
  many :variant_images,
72
72
  key: :images,
73
73
  resource: Spree.api.image_serializer,
74
- if: proc { params[:expand]&.include?('images') }
74
+ if: proc { expand?('images') }
75
75
 
76
76
  many :variants,
77
77
  resource: Spree.api.variant_serializer,
78
- if: proc { params[:expand]&.include?('variants') }
78
+ if: proc { expand?('variants') }
79
79
 
80
80
  one :default_variant,
81
81
  resource: Spree.api.variant_serializer,
82
- if: proc { params[:expand]&.include?('default_variant') }
82
+ if: proc { expand?('default_variant') }
83
83
 
84
84
  one :master,
85
85
  key: :master_variant,
86
86
  resource: Spree.api.variant_serializer,
87
- if: proc { params[:expand]&.include?('master_variant') }
87
+ if: proc { expand?('master_variant') }
88
88
 
89
89
  many :option_types,
90
90
  resource: Spree.api.option_type_serializer,
91
- if: proc { params[:expand]&.include?('option_types') }
91
+ if: proc { expand?('option_types') }
92
92
 
93
93
  many :taxons,
94
94
  proc { |taxons, params|
95
95
  taxons.select { |t| t.taxonomy.store_id == params[:store].id }
96
96
  },
97
- resource: Spree.api.taxon_serializer,
98
- if: proc { params[:expand]&.include?('taxons') },
99
- params: { expand: [] }
97
+ key: :categories,
98
+ resource: Spree.api.category_serializer,
99
+ if: proc { expand?('categories') }
100
100
 
101
101
  many :public_metafields,
102
102
  key: :metafields,
103
103
  resource: Spree.api.metafield_serializer,
104
- if: proc { params[:expand]&.include?('metafields') }
104
+ if: proc { expand?('metafields') }
105
105
  end
106
106
  end
107
107
  end
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ class ShippingCategorySerializer < BaseSerializer
5
+ typelize name: :string
6
+
7
+ attributes :id, :name
8
+ end
9
+ end
10
+ end
11
+ end
@@ -3,17 +3,13 @@ module Spree
3
3
  module V3
4
4
  class ShippingRateSerializer < BaseSerializer
5
5
  typelize name: :string, selected: :boolean, shipping_method_id: :string,
6
- cost: :number, display_cost: :string
6
+ cost: :string, display_cost: :string
7
7
 
8
8
  attribute :shipping_method_id do |shipping_rate|
9
9
  shipping_rate.shipping_method&.prefixed_id
10
10
  end
11
11
 
12
- attributes :name, :selected
13
-
14
- attribute :cost do |shipping_rate|
15
- shipping_rate.cost.to_f
16
- end
12
+ attributes :name, :selected, :cost
17
13
 
18
14
  attribute :display_cost do |shipping_rate|
19
15
  shipping_rate.display_cost.to_s
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ class TaxCategorySerializer < BaseSerializer
5
+ typelize name: :string
6
+
7
+ attributes :id, :name
8
+ end
9
+ end
10
+ end
11
+ end
@@ -9,8 +9,8 @@ module Spree
9
9
  thumbnail: [:string, nullable: true],
10
10
  purchasable: :boolean, in_stock: :boolean, backorderable: :boolean,
11
11
  weight: [:number, nullable: true], height: [:number, nullable: true], width: [:number, nullable: true], depth: [:number, nullable: true],
12
- price: 'StorePrice',
13
- original_price: ['StorePrice', nullable: true]
12
+ price: 'Price',
13
+ original_price: ['Price', nullable: true]
14
14
 
15
15
  attribute :product_id do |variant|
16
16
  variant.product&.prefixed_id
@@ -72,14 +72,14 @@ module Spree
72
72
  # Conditional associations
73
73
  many :images,
74
74
  resource: Spree.api.image_serializer,
75
- if: proc { params[:expand]&.include?('images') }
75
+ if: proc { expand?('images') }
76
76
 
77
77
  many :option_values, resource: Spree.api.option_value_serializer
78
78
 
79
79
  many :public_metafields,
80
80
  key: :metafields,
81
81
  resource: Spree.api.metafield_serializer,
82
- if: proc { params[:expand]&.include?('metafields') }
82
+ if: proc { expand?('metafields') }
83
83
  end
84
84
  end
85
85
  end
@@ -18,7 +18,7 @@ module Spree
18
18
  many :wished_items,
19
19
  key: :items,
20
20
  resource: Spree.api.wished_item_serializer,
21
- if: proc { params[:expand]&.include?('items') }
21
+ if: proc { expand?('items') }
22
22
  end
23
23
  end
24
24
  end
@@ -2,22 +2,20 @@ module Spree
2
2
  module Api
3
3
  module V3
4
4
  class FiltersAggregator
5
- SORT_OPTION_IDS = Spree::Taxon::SORT_ORDERS
6
-
7
- # @param scope [ActiveRecord::Relation] Base product scope (already filtered by store, availability, taxon, etc.)
5
+ # @param scope [ActiveRecord::Relation] Base product scope (already filtered by store, availability, category, etc.)
8
6
  # @param currency [String] Currency for price range
9
- # @param taxon [Spree::Taxon, nil] Optional taxon for default_sort and taxon filtering context
10
- def initialize(scope:, currency:, taxon: nil)
7
+ # @param category [Spree::Category, nil] Optional category for default_sort and category filtering context
8
+ def initialize(scope:, currency:, category: nil)
11
9
  @scope = scope
12
10
  @currency = currency
13
- @taxon = taxon
11
+ @category = category
14
12
  end
15
13
 
16
14
  def call
17
15
  {
18
16
  filters: build_filters,
19
17
  sort_options: sort_options,
20
- default_sort: @taxon&.sort_order || 'manual',
18
+ default_sort: to_api_sort(@category&.sort_order || 'manual'),
21
19
  total_count: @scope.distinct.count
22
20
  }
23
21
  end
@@ -29,12 +27,20 @@ module Spree
29
27
  price_filter,
30
28
  availability_filter,
31
29
  *option_type_filters,
32
- taxon_filter
30
+ category_filter
33
31
  ].compact
34
32
  end
35
33
 
36
34
  def sort_options
37
- SORT_OPTION_IDS.map { |id| { id: id } }
35
+ Spree::Taxon::SORT_ORDERS.map { |id| { id: to_api_sort(id) } }
36
+ end
37
+
38
+ # Converts internal sort format ('price asc') to API format ('price', '-price')
39
+ def to_api_sort(sort_value)
40
+ return sort_value unless sort_value.include?(' ')
41
+
42
+ field, direction = sort_value.split(' ', 2)
43
+ direction == 'desc' ? "-#{field}" : field
38
44
  end
39
45
 
40
46
 
@@ -87,7 +93,7 @@ module Spree
87
93
 
88
94
  def option_value_data(option_type, option_value)
89
95
  # Count products in scope that have this option value
90
- # We use a subquery approach to avoid GROUP BY conflicts when scope includes joins (like in_taxon)
96
+ # We use a subquery approach to avoid GROUP BY conflicts when scope includes joins (like in_category)
91
97
  # Join directly to option_value_variants for efficiency (skips joining through option_values table)
92
98
  count = Spree::Product
93
99
  .where(id: base_scope_product_ids)
@@ -109,31 +115,31 @@ module Spree
109
115
  @base_scope_product_ids ||= @scope.reorder('').distinct.pluck(:id)
110
116
  end
111
117
 
112
- def taxon_filter
113
- return nil if @taxon.nil?
118
+ def category_filter
119
+ return nil if @category.nil?
114
120
 
115
- # Get child taxons at the next depth level
116
- child_taxons = @taxon.children.order(:lft).select do |child|
117
- # Only include taxons that have products in the current scope
118
- @scope.in_taxon(child).exists?
121
+ # Get child categories at the next depth level
122
+ child_categories = @category.children.order(:lft).select do |child|
123
+ # Only include categories that have products in the current scope
124
+ @scope.in_category(child).exists?
119
125
  end
120
126
 
121
- return nil if child_taxons.empty?
127
+ return nil if child_categories.empty?
122
128
 
123
129
  {
124
- id: 'taxons',
125
- type: 'taxon',
126
- options: child_taxons.map { |t| taxon_option_data(t) }
130
+ id: 'categories',
131
+ type: 'category',
132
+ options: child_categories.map { |c| category_option_data(c) }
127
133
  }
128
134
  end
129
135
 
130
- def taxon_option_data(taxon)
131
- count = @scope.in_taxon(taxon).distinct.count
136
+ def category_option_data(category)
137
+ count = @scope.in_category(category).distinct.count
132
138
 
133
139
  {
134
- id: taxon.prefixed_id,
135
- name: taxon.name,
136
- permalink: taxon.permalink,
140
+ id: category.prefixed_id,
141
+ name: category.name,
142
+ permalink: category.permalink,
137
143
  count: count
138
144
  }
139
145
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'net/http'
3
+ require 'ssrf_filter'
4
4
  require 'openssl'
5
5
 
6
6
  module Spree
@@ -49,32 +49,38 @@ module Spree
49
49
  private
50
50
 
51
51
  def make_request
52
- uri = URI.parse(@delivery.url)
53
- http = Net::HTTP.new(uri.host, uri.port)
54
- http.use_ssl = uri.scheme == 'https'
55
- http.verify_mode = ssl_verify_mode
56
- http.open_timeout = TIMEOUT
57
- http.read_timeout = TIMEOUT
58
-
59
- request = Net::HTTP::Post.new(uri.request_uri)
60
- request['Content-Type'] = 'application/json'
61
- request['User-Agent'] = 'Spree-Webhooks/1.0'
62
- request['X-Spree-Webhook-Signature'] = generate_signature
63
- request['X-Spree-Webhook-Event'] = @delivery.event_name
64
- request.body = @delivery.payload.to_json
65
-
66
- http.request(request)
52
+ SsrfFilter.post(
53
+ @delivery.url,
54
+ headers: {
55
+ 'Content-Type' => 'application/json',
56
+ 'User-Agent' => 'Spree-Webhooks/1.0',
57
+ 'X-Spree-Webhook-Signature' => generate_signature,
58
+ 'X-Spree-Webhook-Timestamp' => webhook_timestamp.to_s,
59
+ 'X-Spree-Webhook-Event' => @delivery.event_name
60
+ },
61
+ body: @delivery.payload.to_json,
62
+ http_options: {
63
+ open_timeout: TIMEOUT,
64
+ read_timeout: TIMEOUT,
65
+ verify_mode: ssl_verify_mode
66
+ }
67
+ )
67
68
  end
68
69
 
69
70
  def generate_signature
70
71
  payload_json = @delivery.payload.to_json
71
- OpenSSL::HMAC.hexdigest('SHA256', @secret_key, payload_json)
72
+ OpenSSL::HMAC.hexdigest('SHA256', @secret_key, "#{webhook_timestamp}.#{payload_json}")
73
+ end
74
+
75
+ def webhook_timestamp
76
+ @webhook_timestamp ||= Time.current.to_i
72
77
  end
73
78
 
74
79
  def ssl_verify_mode
75
80
  if Spree::Api::Config.webhooks_verify_ssl
76
81
  OpenSSL::SSL::VERIFY_PEER
77
82
  else
83
+ Rails.logger.warn('[Spree] Webhook SSL verification is disabled. This is not recommended for production environments.')
78
84
  OpenSSL::SSL::VERIFY_NONE
79
85
  end
80
86
  end
@@ -48,7 +48,7 @@ module Spree
48
48
  )
49
49
 
50
50
  # Queue the delivery job
51
- Spree::WebhookDeliveryJob.perform_later(delivery.id, endpoint.secret_key)
51
+ Spree::WebhookDeliveryJob.perform_later(delivery.id)
52
52
  rescue StandardError => e
53
53
  Rails.logger.error "[Spree Webhooks] Error queuing delivery for endpoint #{endpoint.id}: #{e.message}"
54
54
  Rails.error.report(e)
@@ -1,4 +1,4 @@
1
- Alba.backend = :oj
1
+ Alba.backend = :oj_rails
2
2
  Alba.inflector = :active_support
3
3
 
4
4
  # Custom types
@@ -2,24 +2,34 @@
2
2
  # via `bundle exec rake typelizer:generate`. Set ENABLE_TYPELIZER=1 to enable.
3
3
  ENV["DISABLE_TYPELIZER"] ||= "true" unless ENV["ENABLE_TYPELIZER"]
4
4
 
5
- Typelizer.configure do |config|
5
+ Rails.application.config.after_initialize do
6
6
  api_root = Spree::Api::Engine.root
7
7
 
8
- config.dirs = [
9
- api_root.join('app/serializers/spree/api/v3'),
10
- api_root.join('app/serializers/spree/api/v3/admin')
11
- ]
12
- config.output_dir = api_root.join('../../packages/sdk/src/types/generated')
13
- config.comments = true
8
+ Typelizer.configure do |config|
9
+ config.dirs = [api_root.join('app/serializers/spree/api/v3')]
10
+ config.comments = true
11
+ config.listen = false
14
12
 
15
- # Type names: StoreProduct, AdminProduct, etc.
16
- config.serializer_name_mapper = ->(serializer) {
17
- name = serializer.name.to_s
18
- .sub(/\ASpree::Api::V3::Admin::/, 'Admin')
19
- .sub(/\ASpree::Api::V3::/, 'Store')
20
- .sub(/Serializer\z/, '')
21
- name
22
- }
13
+ # Store SDK no prefix, package provides namespace
14
+ config.writer(:store) do |c|
15
+ c.output_dir = api_root.join('../../packages/sdk/src/types/generated')
16
+ c.reject_class = ->(serializer:) { serializer.name.to_s.include?('::Admin::') }
17
+ c.serializer_name_mapper = ->(serializer) {
18
+ serializer.name.to_s
19
+ .sub(/\ASpree::Api::V3::/, '')
20
+ .sub(/Serializer\z/, '')
21
+ }
22
+ end
23
23
 
24
- config.listen = false # https://github.com/skryukov/typelizer?tab=readme-ov-file#automatic-generation-in-development
24
+ # Admin SDK no prefix, package provides namespace
25
+ config.writer(:admin) do |c|
26
+ c.output_dir = api_root.join('../../packages/admin-sdk/src/types/generated')
27
+ c.reject_class = ->(serializer:) { !serializer.name.to_s.include?('::Admin::') }
28
+ c.serializer_name_mapper = ->(serializer) {
29
+ serializer.name.to_s
30
+ .sub(/\ASpree::Api::V3::Admin::/, '')
31
+ .sub(/Serializer\z/, '')
32
+ }
33
+ end
34
+ end
25
35
  end
@@ -6,6 +6,7 @@ en:
6
6
  invalid_api_key: Invalid API key (%{key}) specified.
7
7
  invalid_resource: Invalid resource. Please fix errors and try again.
8
8
  invalid_taxonomy_id: Invalid taxonomy id.
9
+ current_password_invalid: Current password is invalid or missing
9
10
  must_specify_api_key: You must specify an API key.
10
11
  negative_quantity: quantity is negative
11
12
  order:
@@ -23,6 +24,10 @@ en:
23
24
  shipment_transfer_success: Variants successfully transferred
24
25
  stock_location_required: A stock_location_id parameter must be provided in order to retrieve stock movements.
25
26
  unauthorized: You are not authorized to perform that action.
27
+ v3:
28
+ payments:
29
+ session_required: This payment method requires a payment session. Use the payment sessions endpoint instead.
30
+ method_unavailable: This payment method is not available for this order.
26
31
  v2:
27
32
  cart:
28
33
  no_coupon_code: No coupon code provided and the Order doesn't have any coupon code promotions applied
data/config/routes.rb CHANGED
@@ -4,12 +4,11 @@ Spree::Core::Engine.add_routes do
4
4
  namespace :store do
5
5
  # Authentication
6
6
  post 'auth/login', to: 'auth#create'
7
- post 'auth/register', to: 'auth#register'
8
7
  post 'auth/refresh', to: 'auth#refresh'
9
8
  post 'auth/oauth/callback', to: 'auth#oauth_callback'
10
9
 
11
- # Store
12
- resource :store, only: [:show]
10
+ # Customer registration
11
+ resources :customers, only: [:create]
13
12
 
14
13
  # Markets
15
14
  resources :markets, only: [:index, :show] do
@@ -30,9 +29,8 @@ Spree::Core::Engine.add_routes do
30
29
  get :filters, to: 'products/filters#index'
31
30
  end
32
31
  end
33
- resources :taxonomies, only: [:index, :show]
34
- resources :taxons, only: [:index, :show], id: /.+/ do
35
- resources :products, only: [:index], controller: 'taxons/products'
32
+ resources :categories, only: [:index, :show], id: /.+/ do
33
+ resources :products, only: [:index], controller: 'categories/products'
36
34
  end
37
35
 
38
36
  # Cart
@@ -53,7 +51,7 @@ Spree::Core::Engine.add_routes do
53
51
  resource :store_credits, only: [:create, :destroy], controller: 'orders/store_credits'
54
52
  resources :line_items, only: [:create, :update, :destroy], controller: 'orders/line_items'
55
53
  resources :coupon_codes, only: [:create, :destroy], controller: 'orders/coupon_codes'
56
- resources :payments, only: [:index, :show], controller: 'orders/payments'
54
+ resources :payments, only: [:index, :show, :create], controller: 'orders/payments'
57
55
  resources :payment_methods, only: [:index], controller: 'orders/payment_methods'
58
56
  resources :payment_sessions, only: [:create, :show, :update], controller: 'orders/payment_sessions' do
59
57
  member do
@@ -63,10 +61,12 @@ Spree::Core::Engine.add_routes do
63
61
  resources :shipments, only: [:index, :show, :update], controller: 'orders/shipments'
64
62
  end
65
63
 
66
- # Customer
64
+ # Customer (current user profile)
65
+ get 'customer', to: 'customers#show'
66
+ patch 'customer', to: 'customers#update'
67
+
68
+ # Customer nested resources
67
69
  namespace :customer, path: 'customer' do
68
- get '/', to: 'account#show'
69
- patch '/', to: 'account#update'
70
70
  resources :addresses, only: [:index, :show, :create, :update, :destroy] do
71
71
  member do
72
72
  patch :mark_as_default
@@ -10,9 +10,11 @@ module Spree
10
10
  preference :api_v2_per_page_limit, :integer, default: 500
11
11
 
12
12
  preference :jwt_expiration, :integer, default: 3600 # 1 hour in seconds
13
+ preference :jwt_secret_key, :string, default: nil
13
14
 
14
- # Rate limiting (requests per minute)
15
+ # Rate limiting
15
16
  preference :rate_limit_per_key, :integer, default: 300 # per publishable API key
17
+ preference :rate_limit_window, :integer, default: 60 # window in seconds
16
18
  preference :rate_limit_login, :integer, default: 5 # per IP
17
19
  preference :rate_limit_register, :integer, default: 3 # per IP
18
20
  preference :rate_limit_refresh, :integer, default: 10 # per IP
@@ -4,6 +4,7 @@ module Spree
4
4
  module Api
5
5
  class ApiDependencies
6
6
  INJECTION_POINTS_WITH_DEFAULTS = {
7
+ # Legacy API v2 dependencies - will be removed in Spree 6
7
8
  # cart services
8
9
  storefront_cart_create_service: -> { Spree::Dependencies.cart_create_service },
9
10
  storefront_cart_add_item_service: -> { Spree::Dependencies.cart_add_item_service },
@@ -66,6 +67,8 @@ module Spree
66
67
  storefront_estimated_shipment_serializer: 'Spree::V2::Storefront::EstimatedShippingRateSerializer',
67
68
  storefront_store_serializer: 'Spree::V2::Storefront::StoreSerializer',
68
69
  storefront_policy_serializer: 'Spree::V2::Storefront::PolicySerializer',
70
+ storefront_post_category_serializer: 'Spree::V2::Storefront::PostCategorySerializer',
71
+ storefront_post_serializer: 'Spree::V2::Storefront::PostSerializer',
69
72
  storefront_order_serializer: 'Spree::V2::Storefront::OrderSerializer',
70
73
  storefront_variant_serializer: 'Spree::V2::Storefront::VariantSerializer',
71
74
  storefront_image_serializer: 'Spree::V2::Storefront::ImageSerializer',
@@ -109,21 +112,21 @@ module Spree
109
112
  country_serializer: 'Spree::Api::V3::CountrySerializer',
110
113
  market_serializer: 'Spree::Api::V3::MarketSerializer',
111
114
  state_serializer: 'Spree::Api::V3::StateSerializer',
112
- store_serializer: 'Spree::Api::V3::StoreSerializer',
113
115
  wishlist_serializer: 'Spree::Api::V3::WishlistSerializer',
114
116
  wished_item_serializer: 'Spree::Api::V3::WishedItemSerializer',
115
117
  payment_method_serializer: 'Spree::Api::V3::PaymentMethodSerializer',
116
118
  shipping_method_serializer: 'Spree::Api::V3::ShippingMethodSerializer',
117
119
  shipping_rate_serializer: 'Spree::Api::V3::ShippingRateSerializer',
118
120
  stock_location_serializer: 'Spree::Api::V3::StockLocationSerializer',
119
- taxonomy_serializer: 'Spree::Api::V3::TaxonomySerializer',
120
- taxon_serializer: 'Spree::Api::V3::TaxonSerializer',
121
+ category_serializer: 'Spree::Api::V3::CategorySerializer',
121
122
  order_promotion_serializer: 'Spree::Api::V3::OrderPromotionSerializer',
122
123
  digital_link_serializer: 'Spree::Api::V3::DigitalLinkSerializer',
123
124
  gift_card_serializer: 'Spree::Api::V3::GiftCardSerializer',
124
125
  currency_serializer: 'Spree::Api::V3::CurrencySerializer',
125
126
  locale_serializer: 'Spree::Api::V3::LocaleSerializer',
126
127
  metafield_serializer: 'Spree::Api::V3::MetafieldSerializer',
128
+ shipping_category_serializer: 'Spree::Api::V3::ShippingCategorySerializer',
129
+ tax_category_serializer: 'Spree::Api::V3::TaxCategorySerializer',
127
130
 
128
131
  # v3 event serializers (for models without Store API endpoints yet)
129
132
  asset_serializer: 'Spree::Api::V3::AssetSerializer',
@@ -152,9 +155,32 @@ module Spree
152
155
  admin_variant_serializer: 'Spree::Api::V3::Admin::VariantSerializer',
153
156
  admin_price_serializer: 'Spree::Api::V3::Admin::PriceSerializer',
154
157
  admin_metafield_serializer: 'Spree::Api::V3::Admin::MetafieldSerializer',
155
- admin_taxon_serializer: 'Spree::Api::V3::Admin::TaxonSerializer',
158
+ admin_category_serializer: 'Spree::Api::V3::Admin::CategorySerializer',
156
159
  admin_line_item_serializer: 'Spree::Api::V3::Admin::LineItemSerializer',
157
- admin_taxonomy_serializer: 'Spree::Api::V3::Admin::TaxonomySerializer',
160
+ admin_option_type_serializer: 'Spree::Api::V3::Admin::OptionTypeSerializer',
161
+ admin_option_value_serializer: 'Spree::Api::V3::Admin::OptionValueSerializer',
162
+ admin_image_serializer: 'Spree::Api::V3::Admin::ImageSerializer',
163
+ admin_asset_serializer: 'Spree::Api::V3::Admin::AssetSerializer',
164
+ admin_stock_item_serializer: 'Spree::Api::V3::Admin::StockItemSerializer',
165
+ admin_shipment_serializer: 'Spree::Api::V3::Admin::ShipmentSerializer',
166
+ admin_payment_serializer: 'Spree::Api::V3::Admin::PaymentSerializer',
167
+ admin_refund_serializer: 'Spree::Api::V3::Admin::RefundSerializer',
168
+ admin_adjustment_serializer: 'Spree::Api::V3::Admin::AdjustmentSerializer',
169
+ admin_shipping_category_serializer: 'Spree::Api::V3::Admin::ShippingCategorySerializer',
170
+ admin_tax_category_serializer: 'Spree::Api::V3::Admin::TaxCategorySerializer',
171
+ admin_return_authorization_serializer: 'Spree::Api::V3::Admin::ReturnAuthorizationSerializer',
172
+ admin_reimbursement_serializer: 'Spree::Api::V3::Admin::ReimbursementSerializer',
173
+ admin_admin_user_serializer: 'Spree::Api::V3::Admin::AdminUserSerializer',
174
+ admin_address_serializer: 'Spree::Api::V3::Admin::AddressSerializer',
175
+ admin_shipping_method_serializer: 'Spree::Api::V3::Admin::ShippingMethodSerializer',
176
+ admin_stock_location_serializer: 'Spree::Api::V3::Admin::StockLocationSerializer',
177
+ admin_shipping_rate_serializer: 'Spree::Api::V3::Admin::ShippingRateSerializer',
178
+ admin_order_promotion_serializer: 'Spree::Api::V3::Admin::OrderPromotionSerializer',
179
+ admin_payment_method_serializer: 'Spree::Api::V3::Admin::PaymentMethodSerializer',
180
+ admin_credit_card_serializer: 'Spree::Api::V3::Admin::CreditCardSerializer',
181
+ admin_store_credit_serializer: 'Spree::Api::V3::Admin::StoreCreditSerializer',
182
+ admin_payment_source_serializer: 'Spree::Api::V3::Admin::PaymentSourceSerializer',
183
+ admin_digital_link_serializer: 'Spree::Api::V3::Admin::DigitalLinkSerializer',
158
184
 
159
185
  # platform serializers
160
186
  platform_metafield_serializer: 'Spree::Api::V2::Platform::MetafieldSerializer',
@@ -24,6 +24,21 @@ module Spree
24
24
  Spree.subscribers << Spree::WebhookEventSubscriber
25
25
  end
26
26
 
27
+ # Warn in production if no dedicated JWT secret key is configured
28
+ config.after_initialize do
29
+ next unless Rails.env.production?
30
+
31
+ if Spree::Api::Config[:jwt_secret_key].blank? &&
32
+ Rails.application.credentials.jwt_secret_key.blank? &&
33
+ ENV['JWT_SECRET_KEY'].blank?
34
+ Rails.logger.warn(
35
+ '[Spree] No dedicated JWT secret key configured. Falling back to Rails.application.secret_key_base. ' \
36
+ 'Set Spree::Api::Config[:jwt_secret_key], Rails credentials jwt_secret_key, or ENV["JWT_SECRET_KEY"] ' \
37
+ 'for improved security.'
38
+ )
39
+ end
40
+ end
41
+
27
42
  def self.root
28
43
  @root ||= Pathname.new(File.expand_path('../../..', __dir__))
29
44
  end