@doswiftly/storefront-operations 7.0.0 → 7.1.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/AGENTS.md +183 -0
- package/CHANGELOG.md +33 -0
- package/README.md +673 -137
- package/fragments.graphql +137 -53
- package/llms-full.txt +4994 -0
- package/mutations.graphql +47 -3
- package/operations.json +3149 -0
- package/package.json +9 -2
- package/queries.graphql +55 -16
package/mutations.graphql
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
# Cart Mutations
|
|
8
8
|
# ============================================
|
|
9
9
|
|
|
10
|
+
# Creates a new cart and optionally pre-populates it with line items. Cart ID is a UUID stored by the SDK in the `cart-id` cookie (30-day TTL); the cart itself expires server-side after 72 hours of inactivity. The `warnings` field is reserved for non-blocking issues — current implementation returns it empty in this path.
|
|
10
11
|
mutation CartCreate($input: CartCreateInput) {
|
|
11
12
|
cartCreate(input: $input) {
|
|
12
13
|
cart {
|
|
@@ -21,6 +22,7 @@ mutation CartCreate($input: CartCreateInput) {
|
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
# Adds line items to a cart. Each line is `{ merchandiseId, quantity, attributes?, attributeSelections? }`. If the same variant + identical attributes are added twice, quantities merge into one row instead of duplicating. Validates stock (`INSUFFICIENT_STOCK`) and configurator attributes (`ATTRIBUTE_REQUIRED`, `ATTRIBUTE_OPTION_INVALID`). Triggers cart re-pricing including discount recalculation.
|
|
24
26
|
mutation CartAddLines($id: ID!, $lines: [CartLineInput!]!) {
|
|
25
27
|
cartAddLines(id: $id, lines: $lines) {
|
|
26
28
|
cart {
|
|
@@ -35,6 +37,7 @@ mutation CartAddLines($id: ID!, $lines: [CartLineInput!]!) {
|
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
# Updates quantity and/or attributes of existing cart lines by `id`. Setting `quantity: 0` auto-deletes the line. Passing `attributes: []` clears them; omitting the field preserves existing values. Re-validates stock and re-prices the cart after each update.
|
|
38
41
|
mutation CartUpdateLines($id: ID!, $lines: [CartLineUpdateInput!]!) {
|
|
39
42
|
cartUpdateLines(id: $id, lines: $lines) {
|
|
40
43
|
cart {
|
|
@@ -49,6 +52,7 @@ mutation CartUpdateLines($id: ID!, $lines: [CartLineUpdateInput!]!) {
|
|
|
49
52
|
}
|
|
50
53
|
}
|
|
51
54
|
|
|
55
|
+
# Removes specific lines from cart by `lineIds[]`. Internally delegates to `cartUpdateLines` with `quantity: 0` — both endpoints are functionally equivalent; this one exists for API ergonomics when intent is explicit removal. Triggers cart re-pricing.
|
|
52
56
|
mutation CartRemoveLines($id: ID!, $lineIds: [ID!]!) {
|
|
53
57
|
cartRemoveLines(id: $id, lineIds: $lineIds) {
|
|
54
58
|
cart {
|
|
@@ -63,6 +67,7 @@ mutation CartRemoveLines($id: ID!, $lineIds: [ID!]!) {
|
|
|
63
67
|
}
|
|
64
68
|
}
|
|
65
69
|
|
|
70
|
+
# Replaces (NOT appends) the cart's discount codes with the given list. Pass `[]` to clear all codes. Each code is validated against `discounts` table (existence, active status); invalid codes appear in `userErrors[]` as `DISCOUNT_CODE_INVALID`. Triggers cart re-pricing — discount allocations are recomputed and stored in `cart.discountAmount`.
|
|
66
71
|
mutation CartApplyDiscountCodes($id: ID!, $discountCodes: [String!]!) {
|
|
67
72
|
cartApplyDiscountCodes(id: $id, discountCodes: $discountCodes) {
|
|
68
73
|
cart {
|
|
@@ -77,6 +82,7 @@ mutation CartApplyDiscountCodes($id: ID!, $discountCodes: [String!]!) {
|
|
|
77
82
|
}
|
|
78
83
|
}
|
|
79
84
|
|
|
85
|
+
# Associates a customer with the cart. Despite the input shape accepting `email`, `phone`, `countryCode`, `languageCode`, only `customerId` is currently persisted — other fields are silently ignored. Does NOT trigger tax / shipping recalculation; pure cart-to-customer linking. Use during login or guest-to-account upgrade.
|
|
80
86
|
mutation CartUpdateBuyerIdentity($id: ID!, $buyerIdentity: CartBuyerIdentityInput!) {
|
|
81
87
|
cartUpdateBuyerIdentity(id: $id, buyerIdentity: $buyerIdentity) {
|
|
82
88
|
cart {
|
|
@@ -91,6 +97,7 @@ mutation CartUpdateBuyerIdentity($id: ID!, $buyerIdentity: CartBuyerIdentityInpu
|
|
|
91
97
|
}
|
|
92
98
|
}
|
|
93
99
|
|
|
100
|
+
# Sets a free-text note on the cart (gift message, special instructions). Pass empty string to clear. Stored on the `Cart` row, propagated to the `Order` at checkout completion, visible to merchant in admin.
|
|
94
101
|
mutation CartUpdateNote($id: ID!, $note: String!) {
|
|
95
102
|
cartUpdateNote(id: $id, note: $note) {
|
|
96
103
|
cart {
|
|
@@ -109,6 +116,7 @@ mutation CartUpdateNote($id: ID!, $note: String!) {
|
|
|
109
116
|
# Customer Auth Mutations
|
|
110
117
|
# ============================================
|
|
111
118
|
|
|
119
|
+
# Registers a new customer. Returns `customerAccessToken` immediately — the customer record is created with status `ACTIVE` (no pending state). The activation email containing `customerActivate` token is sent for `emailVerified=true` confirmation, but is NOT required for login. Cookie: `customerAccessToken`, 30-day max-age, httpOnly. JWT TTL: 24h. Bot-protection guarded.
|
|
112
120
|
mutation CustomerSignup($input: CustomerCreateInput!) {
|
|
113
121
|
customerSignup(input: $input) {
|
|
114
122
|
customer {
|
|
@@ -123,6 +131,7 @@ mutation CustomerSignup($input: CustomerCreateInput!) {
|
|
|
123
131
|
}
|
|
124
132
|
}
|
|
125
133
|
|
|
134
|
+
# Logs in with email + password. JWT lifetime 24h; cookie max-age 30d (cookie outlives JWT — call `customerRefreshToken` before JWT expiry to extend session). Brute-force protected: 10 failed attempts per email = 15-min Redis-backed lockout. Failed attempts are recorded for non-existent emails too (timing-attack safe).
|
|
126
135
|
mutation CustomerLogin($input: CustomerAccessTokenCreateInput!) {
|
|
127
136
|
customerLogin(input: $input) {
|
|
128
137
|
customerAccessToken {
|
|
@@ -134,6 +143,7 @@ mutation CustomerLogin($input: CustomerAccessTokenCreateInput!) {
|
|
|
134
143
|
}
|
|
135
144
|
}
|
|
136
145
|
|
|
146
|
+
# Clears the `customerAccessToken` cookie. Note: the JWT itself is NOT server-side invalidated — it remains valid until its 24h expiry. Server-side token revocation is on the roadmap. Idempotent.
|
|
137
147
|
mutation CustomerLogout {
|
|
138
148
|
customerLogout {
|
|
139
149
|
deletedAccessToken
|
|
@@ -144,6 +154,7 @@ mutation CustomerLogout {
|
|
|
144
154
|
}
|
|
145
155
|
}
|
|
146
156
|
|
|
157
|
+
# Issues a fresh JWT (24h TTL) for the currently-authenticated customer. Reads identity from the current cookie/Bearer token; takes no input. Use proactively before JWT expiry or reactively on a 401 retry. The new token replaces the cookie value.
|
|
147
158
|
mutation CustomerRefreshToken {
|
|
148
159
|
customerRefreshToken {
|
|
149
160
|
customerAccessToken {
|
|
@@ -159,6 +170,7 @@ mutation CustomerRefreshToken {
|
|
|
159
170
|
# Customer Profile Mutations
|
|
160
171
|
# ============================================
|
|
161
172
|
|
|
173
|
+
# Updates the logged-in customer's profile. Supported fields include `firstName`, `lastName`, `phone`, marketing preferences, and B2B identity (`customerType`, `companyName`, `taxId`, `vatNumber`, `regon`). Concurrent updates from the storefront and the merchant admin are reconciled safely — the loser of a race retries against the latest version. Marketing consent changes are recorded separately for audit purposes.
|
|
162
174
|
mutation CustomerUpdate($customer: CustomerUpdateInput!) {
|
|
163
175
|
customerUpdate(customer: $customer) {
|
|
164
176
|
customer {
|
|
@@ -174,6 +186,7 @@ mutation CustomerUpdate($customer: CustomerUpdateInput!) {
|
|
|
174
186
|
# Customer Address Mutations
|
|
175
187
|
# ============================================
|
|
176
188
|
|
|
189
|
+
# Adds a new mailing address. If `isDefaultShipping` or `isDefaultBilling` is `true` in the input, the new address is set as default and any other address for this customer holding that flag is atomically cleared in the same transaction.
|
|
177
190
|
mutation CustomerAddAddress($address: MailingAddressInput!) {
|
|
178
191
|
customerAddAddress(address: $address) {
|
|
179
192
|
address {
|
|
@@ -185,6 +198,7 @@ mutation CustomerAddAddress($address: MailingAddressInput!) {
|
|
|
185
198
|
}
|
|
186
199
|
}
|
|
187
200
|
|
|
201
|
+
# Updates an existing address owned by the logged-in customer. If `isDefaultShipping` or `isDefaultBilling` toggles to `true`, default flag is atomically cleared on all other addresses for this customer.
|
|
188
202
|
mutation CustomerUpdateAddress($id: ID!, $address: MailingAddressInput!) {
|
|
189
203
|
customerUpdateAddress(id: $id, address: $address) {
|
|
190
204
|
address {
|
|
@@ -196,6 +210,7 @@ mutation CustomerUpdateAddress($id: ID!, $address: MailingAddressInput!) {
|
|
|
196
210
|
}
|
|
197
211
|
}
|
|
198
212
|
|
|
213
|
+
# Hard-deletes an address row from `customer_addresses`. Historical orders that referenced this address are unaffected (address is snapshotted into the order at checkout completion).
|
|
199
214
|
mutation CustomerRemoveAddress($id: ID!) {
|
|
200
215
|
customerRemoveAddress(id: $id) {
|
|
201
216
|
deletedAddressId
|
|
@@ -205,6 +220,7 @@ mutation CustomerRemoveAddress($id: ID!) {
|
|
|
205
220
|
}
|
|
206
221
|
}
|
|
207
222
|
|
|
223
|
+
# Marks the given address as the customer's default **shipping** address. Atomically clears the shipping-default flag from all other addresses. Note: there is no separate setter for default billing — set `isDefaultBilling: true` via `customerAddAddress` / `customerUpdateAddress` instead.
|
|
208
224
|
mutation CustomerSetDefaultAddress($addressId: ID!) {
|
|
209
225
|
customerSetDefaultAddress(addressId: $addressId) {
|
|
210
226
|
customer {
|
|
@@ -220,6 +236,7 @@ mutation CustomerSetDefaultAddress($addressId: ID!) {
|
|
|
220
236
|
# Customer Password Mutations
|
|
221
237
|
# ============================================
|
|
222
238
|
|
|
239
|
+
# Sends a password reset email. Always returns success regardless of whether the email exists (no account enumeration). The email is dispatched asynchronously, so a small delay between request and inbox arrival is normal. Rate-limited to 3 requests per 10 minutes.
|
|
223
240
|
mutation CustomerRequestPasswordReset($email: String!) {
|
|
224
241
|
customerRequestPasswordReset(email: $email) {
|
|
225
242
|
userErrors {
|
|
@@ -228,6 +245,7 @@ mutation CustomerRequestPasswordReset($email: String!) {
|
|
|
228
245
|
}
|
|
229
246
|
}
|
|
230
247
|
|
|
248
|
+
# Activates a newly-created account using the 64-hex activation token from the welcome email + a chosen password. Token TTL is 24h, single-use (atomically marked `used_at`). On success: sets `email_verified=true`, transitions status `INACTIVE`→`ACTIVE`, returns a fresh JWT for auto-login. Rate-limited.
|
|
231
249
|
mutation CustomerActivate($token: String!, $password: String!) {
|
|
232
250
|
customerActivate(token: $token, password: $password) {
|
|
233
251
|
customer {
|
|
@@ -242,6 +260,7 @@ mutation CustomerActivate($token: String!, $password: String!) {
|
|
|
242
260
|
}
|
|
243
261
|
}
|
|
244
262
|
|
|
263
|
+
# Resets the password using the 64-hex reset token from the password-reset email. Token TTL is 1h, single-use (atomically marked `used_at`). On success: updates the password hash and returns a fresh JWT for auto-login (no second login step needed). Rate-limited.
|
|
245
264
|
mutation CustomerResetPassword($token: String!, $newPassword: String!) {
|
|
246
265
|
customerResetPassword(token: $token, newPassword: $newPassword) {
|
|
247
266
|
customer {
|
|
@@ -260,6 +279,7 @@ mutation CustomerResetPassword($token: String!, $newPassword: String!) {
|
|
|
260
279
|
# Checkout Mutations
|
|
261
280
|
# ============================================
|
|
262
281
|
|
|
282
|
+
# Creates a new checkout session for the given cart (or a fresh cart if `cartId` is omitted). Inherits applied discounts and gift cards from the cart by reference (not snapshot). Errors: `CART_NOT_FOUND`, `EMPTY_CART`, `ALREADY_COMPLETED` (cart already converted to order). NOT idempotent — multiple calls create multiple checkouts.
|
|
263
283
|
mutation CheckoutCreate($input: CheckoutCreateInput!) {
|
|
264
284
|
checkoutCreate(input: $input) {
|
|
265
285
|
checkout {
|
|
@@ -271,6 +291,7 @@ mutation CheckoutCreate($input: CheckoutCreateInput!) {
|
|
|
271
291
|
}
|
|
272
292
|
}
|
|
273
293
|
|
|
294
|
+
# Sets the shipping address (full replace, not patch). Triggers cart re-pricing. Address format is NOT validated here — full validation runs at `checkoutComplete` (`validateOrderReadiness`).
|
|
274
295
|
mutation CheckoutShippingAddressUpdate($checkoutId: ID!, $shippingAddress: CheckoutAddressInput!) {
|
|
275
296
|
checkoutShippingAddressUpdate(checkoutId: $checkoutId, shippingAddress: $shippingAddress) {
|
|
276
297
|
checkout {
|
|
@@ -282,6 +303,7 @@ mutation CheckoutShippingAddressUpdate($checkoutId: ID!, $shippingAddress: Check
|
|
|
282
303
|
}
|
|
283
304
|
}
|
|
284
305
|
|
|
306
|
+
# Sets the billing address (full replace). Independent of shipping address — pass it explicitly even when "billing same as shipping".
|
|
285
307
|
mutation CheckoutBillingAddressUpdate($checkoutId: ID!, $billingAddress: CheckoutAddressInput!) {
|
|
286
308
|
checkoutBillingAddressUpdate(checkoutId: $checkoutId, billingAddress: $billingAddress) {
|
|
287
309
|
checkout {
|
|
@@ -293,6 +315,7 @@ mutation CheckoutBillingAddressUpdate($checkoutId: ID!, $billingAddress: Checkou
|
|
|
293
315
|
}
|
|
294
316
|
}
|
|
295
317
|
|
|
318
|
+
# Sets or updates the contact email on the checkout (used for guest checkout, order confirmation, and tracking emails). Validated against a regex; returns `INVALID` for malformed format.
|
|
296
319
|
mutation CheckoutEmailUpdate($checkoutId: ID!, $email: String!) {
|
|
297
320
|
checkoutEmailUpdate(checkoutId: $checkoutId, email: $email) {
|
|
298
321
|
checkout {
|
|
@@ -304,6 +327,7 @@ mutation CheckoutEmailUpdate($checkoutId: ID!, $email: String!) {
|
|
|
304
327
|
}
|
|
305
328
|
}
|
|
306
329
|
|
|
330
|
+
# Selects a shipping method by `shippingRateHandle` (a stable shipping-method UUID, NOT an opaque per-request token). The id comes from `availableShippingMethods` query, computed for the current address + cart subtotal at request time.
|
|
307
331
|
mutation CheckoutShippingLineUpdate($checkoutId: ID!, $shippingRateHandle: String!) {
|
|
308
332
|
checkoutShippingLineUpdate(checkoutId: $checkoutId, shippingRateHandle: $shippingRateHandle) {
|
|
309
333
|
checkout {
|
|
@@ -315,6 +339,7 @@ mutation CheckoutShippingLineUpdate($checkoutId: ID!, $shippingRateHandle: Strin
|
|
|
315
339
|
}
|
|
316
340
|
}
|
|
317
341
|
|
|
342
|
+
# Appends a discount code to the cart's `discount_codes` array. Note: while multiple codes can be stored, the pricing engine currently uses **only the first applied code** — codes do not stack. Validated for existence, active status, and customer usage limits via `discountService.validateDiscount`.
|
|
318
343
|
mutation CheckoutDiscountCodeApply($checkoutId: ID!, $discountCode: String!) {
|
|
319
344
|
checkoutDiscountCodeApply(checkoutId: $checkoutId, discountCode: $discountCode) {
|
|
320
345
|
checkout {
|
|
@@ -326,6 +351,7 @@ mutation CheckoutDiscountCodeApply($checkoutId: ID!, $discountCode: String!) {
|
|
|
326
351
|
}
|
|
327
352
|
}
|
|
328
353
|
|
|
354
|
+
# Removes a code from the cart's `discount_codes` array (filters by exact match). Triggers re-pricing.
|
|
329
355
|
mutation CheckoutDiscountCodeRemove($checkoutId: ID!, $discountCode: String!) {
|
|
330
356
|
checkoutDiscountCodeRemove(checkoutId: $checkoutId, discountCode: $discountCode) {
|
|
331
357
|
checkout {
|
|
@@ -337,6 +363,7 @@ mutation CheckoutDiscountCodeRemove($checkoutId: ID!, $discountCode: String!) {
|
|
|
337
363
|
}
|
|
338
364
|
}
|
|
339
365
|
|
|
366
|
+
# READ-ONLY validation of a discount code against the current checkout — does NOT modify state. Returns `{ isValid, discount, error }` for previewing the effect (e.g. inline UI feedback as the user types).
|
|
340
367
|
mutation CheckoutDiscountCodeValidate($checkoutId: ID!, $discountCode: String!) {
|
|
341
368
|
checkoutDiscountCodeValidate(checkoutId: $checkoutId, discountCode: $discountCode) {
|
|
342
369
|
result {
|
|
@@ -362,6 +389,7 @@ mutation CheckoutDiscountCodeValidate($checkoutId: ID!, $discountCode: String!)
|
|
|
362
389
|
}
|
|
363
390
|
}
|
|
364
391
|
|
|
392
|
+
# Selects a payment method by `paymentMethodId` (UUID from `availablePaymentMethods` query). Validates existence and active status; no pre-authorization is performed here.
|
|
365
393
|
mutation CheckoutPaymentMethodUpdate($checkoutId: ID!, $paymentMethodId: ID!) {
|
|
366
394
|
checkoutPaymentMethodUpdate(checkoutId: $checkoutId, paymentMethodId: $paymentMethodId) {
|
|
367
395
|
checkout {
|
|
@@ -373,6 +401,7 @@ mutation CheckoutPaymentMethodUpdate($checkoutId: ID!, $paymentMethodId: ID!) {
|
|
|
373
401
|
}
|
|
374
402
|
}
|
|
375
403
|
|
|
404
|
+
# Finalizes the checkout: creates the `Order`, marks the cart `CONVERTED`, deducts gift cards, emits `ORDER_CREATED` (and `ORDER_CONFIRMED` for COD) — all in a single serializable transaction. **Idempotent on `idempotencyKey`** (NOT on `checkoutId`); auto-generated from `cartId + timestamp` if the caller omits it. The `paymentUrl` field is reserved but is NOT populated here — for hosted gateways (PayU, P24) the storefront calls a separate `paymentCreate` mutation after this returns.
|
|
376
405
|
mutation CheckoutComplete($checkoutId: ID!, $input: CheckoutCompleteInput) {
|
|
377
406
|
checkoutComplete(checkoutId: $checkoutId, input: $input) {
|
|
378
407
|
checkout {
|
|
@@ -389,9 +418,10 @@ mutation CheckoutComplete($checkoutId: ID!, $input: CheckoutCompleteInput) {
|
|
|
389
418
|
}
|
|
390
419
|
|
|
391
420
|
# ============================================
|
|
392
|
-
# Gift Card Checkout Mutations
|
|
421
|
+
# Gift Card Checkout Mutations
|
|
393
422
|
# ============================================
|
|
394
423
|
|
|
424
|
+
# Applies a gift card to the cart, stackable with discount codes. Consumption is FIFO: each card consumes `min(remainingBalance, paymentDue)` against the current cart total in the order they were applied. The gift card balance is NOT debited yet — actual deduction happens atomically inside the `checkoutComplete` transaction. Errors: `GIFT_CARD_NOT_FOUND`, `GIFT_CARD_DEPLETED`, `GIFT_CARD_UNUSABLE`, `GIFT_CARD_ALREADY_APPLIED`.
|
|
395
425
|
mutation CheckoutGiftCardApply($checkoutId: ID!, $giftCardCode: String!) {
|
|
396
426
|
checkoutGiftCardApply(checkoutId: $checkoutId, giftCardCode: $giftCardCode) {
|
|
397
427
|
checkout {
|
|
@@ -403,6 +433,7 @@ mutation CheckoutGiftCardApply($checkoutId: ID!, $giftCardCode: String!) {
|
|
|
403
433
|
}
|
|
404
434
|
}
|
|
405
435
|
|
|
436
|
+
# Removes a gift card from the applied list and recalculates FIFO `appliedAmount` for the remaining cards. Since gift card balances are only debited at `checkoutComplete`, removing before completion has no effect on the underlying gift card balance.
|
|
406
437
|
mutation CheckoutGiftCardRemove($checkoutId: ID!, $giftCardCode: String!) {
|
|
407
438
|
checkoutGiftCardRemove(checkoutId: $checkoutId, giftCardCode: $giftCardCode) {
|
|
408
439
|
checkout {
|
|
@@ -414,6 +445,7 @@ mutation CheckoutGiftCardRemove($checkoutId: ID!, $giftCardCode: String!) {
|
|
|
414
445
|
}
|
|
415
446
|
}
|
|
416
447
|
|
|
448
|
+
# Sets per-line-item recipient details (name, email, message, delivery date) for digital gift card products in the cart (variants with `type: GIFT_CARD`). Required before `checkoutComplete` for any line item with a gift-card variant. Recipient details are associated with each gift-card line item and propagated to the resulting order.
|
|
417
449
|
mutation CheckoutGiftCardRecipientUpdate($input: CheckoutGiftCardRecipientInput!) {
|
|
418
450
|
checkoutGiftCardRecipientUpdate(input: $input) {
|
|
419
451
|
checkout {
|
|
@@ -426,9 +458,10 @@ mutation CheckoutGiftCardRecipientUpdate($input: CheckoutGiftCardRecipientInput!
|
|
|
426
458
|
}
|
|
427
459
|
|
|
428
460
|
# ============================================
|
|
429
|
-
# Return Mutations
|
|
461
|
+
# Return Mutations
|
|
430
462
|
# ============================================
|
|
431
463
|
|
|
464
|
+
# Creates an RMA in `REQUESTED` status (awaits merchant approval — NOT auto-approved). Input: `orderId`, `reason`, `items[{ variantId, quantity, reason, condition }]`, optional `compensationType` (REFUND or STORE_CREDIT) and `customerNote`. Validates the order's `fulfillmentStatus` permits returns and that requested quantities don't exceed already-shipped/unreturned quantities. Supports optional `idempotencyKey` for retry-safe creation.
|
|
432
465
|
mutation ReturnCreate($input: ReturnCreateInput!) {
|
|
433
466
|
returnCreate(input: $input) {
|
|
434
467
|
return {
|
|
@@ -440,6 +473,7 @@ mutation ReturnCreate($input: ReturnCreateInput!) {
|
|
|
440
473
|
}
|
|
441
474
|
}
|
|
442
475
|
|
|
476
|
+
# Cancels a return that is currently in `REQUESTED`, `APPROVED`, or `DRAFT` status (cancellation is allowed even AFTER merchant approval, as long as the return shipment hasn't been processed). Sets `cancelled_at` timestamp. Customer can only cancel returns they own.
|
|
443
477
|
mutation ReturnCancel($id: ID!) {
|
|
444
478
|
returnCancel(id: $id) {
|
|
445
479
|
return {
|
|
@@ -452,15 +486,17 @@ mutation ReturnCancel($id: ID!) {
|
|
|
452
486
|
}
|
|
453
487
|
|
|
454
488
|
# ============================================
|
|
455
|
-
# Loyalty Program Mutations
|
|
489
|
+
# Loyalty Program Mutations
|
|
456
490
|
# ============================================
|
|
457
491
|
|
|
492
|
+
# Redeems a loyalty reward by `rewardId`. Three reward types are supported, distinguished by which output field is populated: `discountCode` (issues a `LOYALTY-XXXX` code with 30-day expiry), `productDiscountCode` (issues a single-use 100%-off code for a specific product), or `giftCardCode` (creates a new gift card for the customer). Points are deducted atomically inside a transaction — if external creation (e.g. gift card service) fails after deduction, points are reversed.
|
|
458
493
|
mutation RedeemLoyaltyReward($input: RedeemRewardInput!) {
|
|
459
494
|
redeemLoyaltyReward(input: $input) {
|
|
460
495
|
...RedeemRewardPayload
|
|
461
496
|
}
|
|
462
497
|
}
|
|
463
498
|
|
|
499
|
+
# Returns the customer's referral code, generating one on first call. Idempotent UPSERT — subsequent calls return the existing code from `customers.referral_code`. Format: `REF-XXXXXXXX` (8 random alphanumeric chars). Output also includes a `shareUrl` built from the shop's domain.
|
|
464
500
|
mutation GenerateReferralCode {
|
|
465
501
|
generateReferralCode {
|
|
466
502
|
...GenerateReferralCodePayload
|
|
@@ -471,6 +507,7 @@ mutation GenerateReferralCode {
|
|
|
471
507
|
# Review Mutations
|
|
472
508
|
# ============================================
|
|
473
509
|
|
|
510
|
+
# Submits a product review (rating 1-5, content 10-5000 chars, sanitized via `sanitizePlainText`). Default state is `PENDING` — review is hidden from public until merchant approves. If the input includes `orderId`, `isVerifiedPurchase` is auto-set to `true`. Bot-protected and rate-limited (10/min by default).
|
|
474
511
|
mutation ReviewCreate($input: ReviewCreateInput!) {
|
|
475
512
|
reviewCreate(input: $input) {
|
|
476
513
|
review {
|
|
@@ -482,6 +519,7 @@ mutation ReviewCreate($input: ReviewCreateInput!) {
|
|
|
482
519
|
}
|
|
483
520
|
}
|
|
484
521
|
|
|
522
|
+
# Records an upvote (helpful) on a review. UPSERT semantics — one `ReviewVote` row per `(reviewId, customerId)`. Calling upvote twice is a no-op; calling downvote afterwards replaces the vote. Increments `helpful_count` on the review (and decrements `unhelpful_count` if replacing a downvote).
|
|
485
523
|
mutation ReviewUpvote($reviewId: ID!) {
|
|
486
524
|
reviewUpvote(reviewId: $reviewId) {
|
|
487
525
|
review {
|
|
@@ -493,6 +531,7 @@ mutation ReviewUpvote($reviewId: ID!) {
|
|
|
493
531
|
}
|
|
494
532
|
}
|
|
495
533
|
|
|
534
|
+
# Records a downvote (unhelpful) on a review. Same UPSERT semantics as `reviewUpvote` — one vote row per `(reviewId, customerId)`, replacing any prior vote. Increments `unhelpful_count`.
|
|
496
535
|
mutation ReviewDownvote($reviewId: ID!) {
|
|
497
536
|
reviewDownvote(reviewId: $reviewId) {
|
|
498
537
|
review {
|
|
@@ -508,6 +547,7 @@ mutation ReviewDownvote($reviewId: ID!) {
|
|
|
508
547
|
# Wishlist Mutations
|
|
509
548
|
# ============================================
|
|
510
549
|
|
|
550
|
+
# Creates a new wishlist for the logged-in customer. `name` is optional (defaults to "My Wishlist"); name uniqueness is NOT enforced — customers can have multiple lists with the same name. Setting `isPublic: true` generates a 16-byte hex `shareToken` for public sharing.
|
|
511
551
|
mutation WishlistCreate($input: WishlistCreateInput!) {
|
|
512
552
|
wishlistCreate(input: $input) {
|
|
513
553
|
wishlist {
|
|
@@ -517,6 +557,7 @@ mutation WishlistCreate($input: WishlistCreateInput!) {
|
|
|
517
557
|
}
|
|
518
558
|
}
|
|
519
559
|
|
|
560
|
+
# Adds an item by `productId` (and optional `variantId`) to a wishlist. Idempotent on the `(wishlist_id, product_id, variant_id)` unique constraint — adding an already-present item is a silent no-op (`ON CONFLICT DO NOTHING`). Captures `priceAtAdd` for price-drop notifications.
|
|
520
561
|
mutation WishlistAddItem($wishlistId: ID!, $input: WishlistItemInput!) {
|
|
521
562
|
wishlistAddItem(wishlistId: $wishlistId, input: $input) {
|
|
522
563
|
wishlist {
|
|
@@ -526,6 +567,7 @@ mutation WishlistAddItem($wishlistId: ID!, $input: WishlistItemInput!) {
|
|
|
526
567
|
}
|
|
527
568
|
}
|
|
528
569
|
|
|
570
|
+
# Hard-deletes a wishlist item by `itemId` (the `WishlistItem` row id, NOT the product id).
|
|
529
571
|
mutation WishlistRemoveItem($wishlistId: ID!, $itemId: ID!) {
|
|
530
572
|
wishlistRemoveItem(wishlistId: $wishlistId, itemId: $itemId) {
|
|
531
573
|
wishlist {
|
|
@@ -535,6 +577,7 @@ mutation WishlistRemoveItem($wishlistId: ID!, $itemId: ID!) {
|
|
|
535
577
|
}
|
|
536
578
|
}
|
|
537
579
|
|
|
580
|
+
# Hard-deletes the wishlist row. All `WishlistItem` rows are removed via FK cascade. No soft-delete; cannot be undone.
|
|
538
581
|
mutation WishlistDelete($wishlistId: ID!) {
|
|
539
582
|
wishlistDelete(wishlistId: $wishlistId) {
|
|
540
583
|
wishlist {
|
|
@@ -548,6 +591,7 @@ mutation WishlistDelete($wishlistId: ID!) {
|
|
|
548
591
|
# Cart Attributes
|
|
549
592
|
# ============================================
|
|
550
593
|
|
|
594
|
+
# Replaces (NOT merges) the cart's custom attributes — free-form `[{ key, value }]` pairs visible to merchant in admin. Use for delivery instructions, gift wrap flags, B2B PO numbers, etc. Limit: 250 pairs per cart (returns `CART_ATTRIBUTES_LIMIT_EXCEEDED`); each `key` max 255 chars.
|
|
551
595
|
mutation CartUpdateAttributes($id: ID!, $attributes: [CartAttributeInput!]!) {
|
|
552
596
|
cartUpdateAttributes(id: $id, attributes: $attributes) {
|
|
553
597
|
cart {
|