@develemit/commerce-server 0.6.0 → 0.7.0
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/CHANGELOG.md +112 -0
- package/README.md +33 -2
- package/dist/index.d.ts +14 -4
- package/dist/index.js +223 -79
- package/dist/index.js.map +1 -1
- package/docs/admin.md +38 -11
- package/docs/payments.md +13 -0
- package/migrations/0008_draft_products_incomplete.sql +3 -0
- package/migrations/meta/0008_snapshot.json +1029 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +11 -3
package/docs/admin.md
CHANGED
|
@@ -5,6 +5,9 @@ Part of [`@develemit/commerce-server`](../README.md).
|
|
|
5
5
|
Pass `admin: { authorize, storage }` to `commercePlugin` to register product
|
|
6
6
|
management under `/admin`. Without it, no admin routes exist (404).
|
|
7
7
|
|
|
8
|
+
- `requirePhotoToPublish` (default `true`) makes `PATCH` to `status: 'active'`
|
|
9
|
+
refuse a draft with no image. Set `false` for shops that go live without
|
|
10
|
+
photos.
|
|
8
11
|
- `authorize(req)` returns `true` to allow the request; `false` gives a 401.
|
|
9
12
|
The kit owns no identity: check a session, token or header yourself.
|
|
10
13
|
- `storage` implements `FileStorage` (`put(key, stream, contentType)` and
|
|
@@ -15,15 +18,16 @@ management under `/admin`. Without it, no admin routes exist (404).
|
|
|
15
18
|
files with `openStoredFile(dir, key)`, which returns `{ stream, contentType,
|
|
16
19
|
size }` or `null` (answer 404).
|
|
17
20
|
|
|
18
|
-
| Route | Purpose
|
|
19
|
-
| ---------------------------------------- |
|
|
20
|
-
| `GET/POST /admin/products` | List all statuses / create (`adminProductInputSchema`)
|
|
21
|
-
| `GET
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
24
|
-
| `PATCH /admin/products/:id/images
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
21
|
+
| Route | Purpose |
|
|
22
|
+
| ---------------------------------------- | -------------------------------------------------------- |
|
|
23
|
+
| `GET/POST /admin/products` | List all statuses / create (`adminProductInputSchema`) |
|
|
24
|
+
| `GET /admin/products/counts` | `{ all, live, draft, sold, archived, incompleteDrafts }` |
|
|
25
|
+
| `GET/PATCH /admin/products/:id` | Read / edit; `{ sold: true }` sets quantity to 0 |
|
|
26
|
+
| `POST /admin/products/:id/images` | Multipart `file` (+ optional `alt`), 8MB max |
|
|
27
|
+
| `PATCH /admin/products/:id/images/order` | `{ imageIds }`, every image exactly once |
|
|
28
|
+
| `PATCH /admin/products/:id/images/:id` | `{ alt }` (trimmed, max 500); 404 for unknown image |
|
|
29
|
+
| `DELETE /admin/products/:id/images/:id` | Removes the row and the stored file |
|
|
30
|
+
| `GET/PUT /admin/shipping-rates` | `PUT` replaces the full set of rates |
|
|
27
31
|
|
|
28
32
|
Stored image keys are `products/<productId>/<uuid>.<ext>` and are never
|
|
29
33
|
overwritten, so their URLs are safe to cache forever. Serve `FileStorage` URLs
|
|
@@ -31,8 +35,31 @@ through a CDN with `Cache-Control: public, max-age=31536000, immutable`, and
|
|
|
31
35
|
have it (or the storefront's image adapter) resize.
|
|
32
36
|
|
|
33
37
|
Uploads are checked by magic bytes (jpeg, png, webp, heic), not by filename or
|
|
34
|
-
the client's content type.
|
|
35
|
-
|
|
38
|
+
the client's content type. Edits never touch inventory holds.
|
|
39
|
+
|
|
40
|
+
## Drafts and publishing
|
|
41
|
+
|
|
42
|
+
A draft may have no name, price or category (a photo-only draft). It never
|
|
43
|
+
appears in the public catalog. Publishing needs all three, plus a photo unless
|
|
44
|
+
`requirePhotoToPublish` is `false`:
|
|
45
|
+
|
|
46
|
+
- `PATCH` to `status: 'active'` with something missing answers `422`
|
|
47
|
+
`{ error: 'PRODUCT_INCOMPLETE', missing: ['name' | 'priceCents' | 'category' | 'images'] }`.
|
|
48
|
+
`AdminClient` throws `ProductIncompleteError` with `missing`.
|
|
49
|
+
- `POST` with `status: 'active'` and no name, price or category answers `400
|
|
50
|
+
INVALID_INPUT` with one issue per missing field. A photo is not checked on
|
|
51
|
+
create, since a new product has no images yet.
|
|
52
|
+
- The public catalog also skips any active row missing a price or category.
|
|
53
|
+
|
|
54
|
+
Slugs: a nameless draft gets `draft-<first 8 chars of id>`. The first time it
|
|
55
|
+
is given a name the slug becomes `slugify(name)` (made unique with `-2`, `-3`).
|
|
56
|
+
After that it never changes, so renaming a live product keeps shop links
|
|
57
|
+
stable. A product created with a name keeps that slug on rename.
|
|
58
|
+
|
|
59
|
+
Counts: each number equals the total of the matching list filter. `live` is
|
|
60
|
+
`status=active`, `draft` is `status=draft`, `archived` is `status=archived`,
|
|
61
|
+
`sold` is `stock=sold` (any status), and `all` is every non-archived product.
|
|
62
|
+
`incompleteDrafts` counts drafts missing a name, price or category.
|
|
36
63
|
|
|
37
64
|
## Admin orders API
|
|
38
65
|
|
package/docs/payments.md
CHANGED
|
@@ -5,11 +5,16 @@ Part of [`@develemit/commerce-server`](../README.md).
|
|
|
5
5
|
`payment` takes any `PaymentGateway` (`createCheckoutSession`,
|
|
6
6
|
`verifyWebhook`, `refund`). `refund({ sessionId, amountCents, idempotencyKey })`
|
|
7
7
|
must return the same `gatewayRefundId` when called again with the same key.
|
|
8
|
+
A gateway may also define `preflight(): Promise<void>`; `commercePlugin` awaits
|
|
9
|
+
it during registration, so a gateway that can't work fails startup.
|
|
8
10
|
Two ship with the package:
|
|
9
11
|
|
|
10
12
|
- `createStripeGateway({ secretKey, webhookSecret })` loads `stripe` lazily, so
|
|
11
13
|
it's an optional peer. Refunds look up the session's PaymentIntent and call
|
|
12
14
|
`refunds.create` with the idempotency key.
|
|
15
|
+
Registration awaits the optional `preflight()` hook, which loads `stripe`,
|
|
16
|
+
so a missing package fails startup with `pnpm add stripe` instead of the
|
|
17
|
+
first checkout. A passed-in `client` skips the import.
|
|
13
18
|
- `createFakeGateway()` for tests and local development. By default its
|
|
14
19
|
session URL is the success URL (see [Local checkout](#local-checkout-without-stripe)
|
|
15
20
|
for a click-through flow), and
|
|
@@ -34,6 +39,14 @@ only knows sessions created since the process started (404 otherwise), and
|
|
|
34
39
|
option on `NODE_ENV !== 'production'` so one config works in every
|
|
35
40
|
environment.
|
|
36
41
|
|
|
42
|
+
The completed order looks like a real one: buyer email `dev-buyer@example.com`,
|
|
43
|
+
a sample US address, and the session's first offered shipping rate (so the
|
|
44
|
+
total includes shipping; no rate means no shipping line). Override the buyer
|
|
45
|
+
with `devCheckout: { buyer: { email, name, address } }`, or pass
|
|
46
|
+
`buyer: (sessionId) => ({ ... })` to compute it per session. Omitted fields keep
|
|
47
|
+
their defaults, so `onOrderPaid` hooks that send a confirmation email or buy a
|
|
48
|
+
label can run locally without a custom dev route.
|
|
49
|
+
|
|
37
50
|
## Discounts
|
|
38
51
|
|
|
39
52
|
A discount code is an amount off the merchandise subtotal, never shipping. The
|