@spree/docs 0.1.8 → 0.1.10
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.
- package/dist/api-reference/store-api/querying.md +9 -4
- package/dist/api-reference/store.yaml +513 -667
- package/dist/developer/core-concepts/search-filtering.md +13 -4
- package/dist/developer/core-concepts/webhooks.md +47 -12
- package/dist/developer/storefront/nextjs/spree-next-package.md +45 -0
- package/package.json +2 -2
|
@@ -73,9 +73,14 @@ const { data: products } = await client.categories.products.list('clothing/shirt
|
|
|
73
73
|
limit: 12,
|
|
74
74
|
})
|
|
75
75
|
|
|
76
|
-
// Or filter by category ID
|
|
76
|
+
// Or filter by category ID (includes descendants)
|
|
77
77
|
const { data: products } = await client.products.list({
|
|
78
|
-
|
|
78
|
+
in_category: 'ctg_xxx',
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// Multiple categories (OR logic, checkbox filters)
|
|
82
|
+
const { data: products } = await client.products.list({
|
|
83
|
+
in_categories: ['ctg_shirts', 'ctg_pants'],
|
|
79
84
|
})
|
|
80
85
|
```
|
|
81
86
|
|
|
@@ -84,8 +89,12 @@ const { data: products } = await client.products.list({
|
|
|
84
89
|
curl 'https://api.mystore.com/api/v3/store/categories/clothing/shirts/products?limit=12' \
|
|
85
90
|
-H 'Authorization: Bearer spree_pk_xxx'
|
|
86
91
|
|
|
87
|
-
# Filter by category ID
|
|
88
|
-
curl 'https://api.mystore.com/api/v3/store/products?q[
|
|
92
|
+
# Filter by category ID (includes descendants)
|
|
93
|
+
curl 'https://api.mystore.com/api/v3/store/products?q[in_category]=ctg_xxx' \
|
|
94
|
+
-H 'Authorization: Bearer spree_pk_xxx'
|
|
95
|
+
|
|
96
|
+
# Multiple categories (OR logic)
|
|
97
|
+
curl 'https://api.mystore.com/api/v3/store/products?q[in_categories][]=ctg_shirts&q[in_categories][]=ctg_pants' \
|
|
89
98
|
-H 'Authorization: Bearer spree_pk_xxx'
|
|
90
99
|
```
|
|
91
100
|
|
|
@@ -163,22 +163,56 @@ Each webhook request includes these headers:
|
|
|
163
163
|
|
|
164
164
|
### Verifying Webhook Signatures
|
|
165
165
|
|
|
166
|
-
To ensure webhooks are genuinely from your Spree store, verify the signature
|
|
166
|
+
To ensure webhooks are genuinely from your Spree store, verify the signature.
|
|
167
|
+
|
|
168
|
+
#### Next.js (recommended)
|
|
169
|
+
|
|
170
|
+
Use `@spree/next/webhooks` for a ready-made Route Handler:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
// src/app/api/webhooks/spree/route.ts
|
|
174
|
+
import { createWebhookHandler } from '@spree/next/webhooks'
|
|
175
|
+
|
|
176
|
+
export const POST = createWebhookHandler({
|
|
177
|
+
secret: process.env.SPREE_WEBHOOK_SECRET!,
|
|
178
|
+
handlers: {
|
|
179
|
+
'order.completed': async (event) => {
|
|
180
|
+
// event.data is the order payload (same shape as Store API)
|
|
181
|
+
await sendOrderConfirmationEmail(event.data)
|
|
182
|
+
},
|
|
183
|
+
'order.canceled': async (event) => {
|
|
184
|
+
await sendCancellationEmail(event.data)
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
})
|
|
188
|
+
```
|
|
167
189
|
|
|
168
|
-
|
|
169
|
-
# In your webhook receiver
|
|
170
|
-
def verify_webhook(request)
|
|
171
|
-
payload = request.body.read
|
|
172
|
-
signature = request.headers['X-Spree-Webhook-Signature']
|
|
173
|
-
secret_key = ENV['SPREE_WEBHOOK_SECRET'] # Your endpoint's secret_key
|
|
190
|
+
#### Any JavaScript/TypeScript framework
|
|
174
191
|
|
|
175
|
-
|
|
192
|
+
Use `@spree/sdk/webhooks` for framework-agnostic verification:
|
|
176
193
|
|
|
177
|
-
|
|
178
|
-
|
|
194
|
+
```typescript
|
|
195
|
+
import { verifyWebhookSignature } from '@spree/sdk/webhooks'
|
|
196
|
+
import type { WebhookEvent } from '@spree/sdk/webhooks'
|
|
197
|
+
import type { Order } from '@spree/sdk'
|
|
198
|
+
|
|
199
|
+
// Hono, Cloudflare Workers, or any Web Fetch API-based framework
|
|
200
|
+
app.post('/webhooks/spree', async (req, res) => {
|
|
201
|
+
const body = await req.text()
|
|
202
|
+
const signature = req.headers['x-spree-webhook-signature']
|
|
203
|
+
const timestamp = req.headers['x-spree-webhook-timestamp']
|
|
204
|
+
|
|
205
|
+
if (!verifyWebhookSignature(body, signature, timestamp, process.env.SPREE_WEBHOOK_SECRET!)) {
|
|
206
|
+
return res.status(401).json({ error: 'Invalid signature' })
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const event: WebhookEvent<Order> = JSON.parse(body)
|
|
210
|
+
// handle event...
|
|
211
|
+
res.json({ received: true })
|
|
212
|
+
})
|
|
179
213
|
```
|
|
180
214
|
|
|
181
|
-
|
|
215
|
+
#### Ruby
|
|
182
216
|
|
|
183
217
|
```ruby
|
|
184
218
|
class WebhooksController < ApplicationController
|
|
@@ -209,7 +243,8 @@ class WebhooksController < ApplicationController
|
|
|
209
243
|
request.body.rewind
|
|
210
244
|
|
|
211
245
|
signature = request.headers['X-Spree-Webhook-Signature']
|
|
212
|
-
|
|
246
|
+
timestamp = request.headers['X-Spree-Webhook-Timestamp']
|
|
247
|
+
expected = OpenSSL::HMAC.hexdigest('SHA256', ENV['SPREE_WEBHOOK_SECRET'], "#{timestamp}.#{payload}")
|
|
213
248
|
|
|
214
249
|
ActiveSupport::SecurityUtils.secure_compare(signature.to_s, expected)
|
|
215
250
|
end
|
|
@@ -20,6 +20,9 @@ Set environment variables and the client initializes automatically:
|
|
|
20
20
|
```env
|
|
21
21
|
SPREE_API_URL=https://api.mystore.com
|
|
22
22
|
SPREE_PUBLISHABLE_KEY=spree_pk_xxx
|
|
23
|
+
|
|
24
|
+
# Required for webhook-based emails (see Webhooks section below)
|
|
25
|
+
SPREE_WEBHOOK_SECRET=your_webhook_endpoint_secret_key
|
|
23
26
|
```
|
|
24
27
|
|
|
25
28
|
### Explicit initialization
|
|
@@ -284,6 +287,48 @@ const products = await listProducts({ limit: 10 }, { locale: 'fr', country: 'FR'
|
|
|
284
287
|
const category = await getCategory('clothing/shirts', {}, { locale: 'de', country: 'DE' });
|
|
285
288
|
```
|
|
286
289
|
|
|
290
|
+
## Webhooks
|
|
291
|
+
|
|
292
|
+
Handle Spree webhook events in your Next.js app with `@spree/next/webhooks`:
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
// src/app/api/webhooks/spree/route.ts
|
|
296
|
+
import { createWebhookHandler } from '@spree/next/webhooks'
|
|
297
|
+
|
|
298
|
+
export const POST = createWebhookHandler({
|
|
299
|
+
secret: process.env.SPREE_WEBHOOK_SECRET!,
|
|
300
|
+
handlers: {
|
|
301
|
+
'order.completed': async (event) => {
|
|
302
|
+
await sendOrderConfirmationEmail(event.data)
|
|
303
|
+
},
|
|
304
|
+
'order.canceled': async (event) => {
|
|
305
|
+
await sendCancellationEmail(event.data)
|
|
306
|
+
},
|
|
307
|
+
'order.shipped': async (event) => {
|
|
308
|
+
await sendShippingNotification(event.data)
|
|
309
|
+
},
|
|
310
|
+
'customer.password_reset_requested': async (event) => {
|
|
311
|
+
await sendPasswordResetEmail(event.data)
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
})
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
The handler verifies HMAC-SHA256 signatures, rejects replayed requests, routes events to your handlers, and returns 200 immediately (handlers run async).
|
|
318
|
+
|
|
319
|
+
### Local development
|
|
320
|
+
|
|
321
|
+
Webhooks require a publicly accessible URL. Use [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/) for local development:
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
brew install cloudflared
|
|
325
|
+
cloudflared tunnel --url http://localhost:3001
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Then create a webhook endpoint in **Spree Admin → Settings → Developers → Webhooks** pointing to your tunnel URL + `/api/webhooks/spree`.
|
|
329
|
+
|
|
330
|
+
For more details, see [Webhooks](../../core-concepts/webhooks.md).
|
|
331
|
+
|
|
287
332
|
## TypeScript
|
|
288
333
|
|
|
289
334
|
All types are re-exported from `@spree/sdk`:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spree/docs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Spree Commerce developer documentation for AI agents and local reference",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "CC-BY-4.0",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"clean": "rm -rf dist"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"vitest": "^4.
|
|
27
|
+
"vitest": "^4.1.1"
|
|
28
28
|
},
|
|
29
29
|
"engines": {
|
|
30
30
|
"node": ">=18.0.0"
|