checkout-management
npx skills add https://github.com/saleor/storefront --skill checkout-management
Agent 安装分布
Skill 文档
Checkout Management Skill
Overview
This skill covers how checkout sessions are created, stored, and managed in the Saleor storefront.
Checkout ID Storage
Checkout IDs are stored in two places:
1. Cookie (Primary Storage)
Cookie name: checkoutId-{channel}
Example: checkoutId-default-channel
The cookie is set in src/lib/checkout.ts:
export async function saveIdToCookie(channel: string, checkoutId: string) {
const cookieName = `checkoutId-${channel}`;
(await cookies()).set(cookieName, checkoutId, {
sameSite: "lax",
secure: shouldUseHttps,
});
}
2. URL Query Parameter
URL: /checkout?checkout=Q2hlY2tvdXQ6YThjN2Y4YjgtZmU0NS00ZTRkLThhZmItZDdjYWI2YTM5MTdm
The checkout ID is a base64-encoded Saleor global ID.
Checkout Lifecycle
Creation
A new checkout is created when:
- User adds first item to an empty cart
- No valid checkout ID exists in cookie
- Existing checkout is not found in Saleor
// src/lib/checkout.ts
export async function findOrCreate({ channel, checkoutId }) {
if (!checkoutId) {
return (await create({ channel })).checkoutCreate?.checkout;
}
const checkout = await find(checkoutId);
return checkout || (await create({ channel })).checkoutCreate?.checkout;
}
Persistence
The checkout persists across:
- Page refreshes
- Browser sessions (cookie-based)
- Cart modifications
Completion
When checkoutComplete mutation succeeds:
- Checkout is converted to an Order
- The checkout ID becomes invalid
- A new checkout should be created for future purchases
Common Issues
Hydration Mismatch with Checkout ID
Problem: extractCheckoutIdFromUrl() called during SSR reads an empty URL, causing React hydration mismatch and “PageNotFound” flash.
Symptom: Checkout page briefly shows error then loads correctly on refresh.
Fix: Delay extraction until after client-side mount:
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
const id = useMemo(() => (mounted ? extractCheckoutIdFromUrl() : null), [mounted]);
See src/checkout/hooks/useCheckout.ts for the full implementation.
Stale Checkout with Failed Transactions
Problem: If payment fails multiple times, the checkout accumulates partial transactions. Subsequent payment attempts may fail with:
CHECKOUT_NOT_FULLY_PAID: The authorized amount doesn't cover the checkout's total amount.
Solutions:
- Clear cookies – Delete
checkoutId-{channel}cookie - Use incognito – Test in a private browser window
- Remove URL param – Navigate to checkout without
?checkout=XXX
Checkout Amount Mismatch
Problem: Checkout total changes after transactions are initialized (e.g., shipping added).
Solution: Always use live checkout data via useCheckout() hook before payment:
const { checkout: liveCheckout } = useCheckout();
const checkout = liveCheckout || initialCheckout;
const totalAmount = checkout.totalPrice.gross.amount;
Key Files
| File | Purpose |
|---|---|
src/lib/checkout.ts |
Checkout creation, cookie management |
src/checkout/hooks/useCheckout.ts |
React hook for checkout data |
src/checkout/lib/utils/url.ts |
URL query param extraction |
src/graphql/CheckoutCreate.graphql |
Checkout creation mutation |
Debugging Checkout Issues
1. Check Current Checkout ID
// In browser console
document.cookie.split(";").find((c) => c.includes("checkoutId"));
2. Decode Checkout ID
// Base64 decode the checkout ID from URL
atob("Q2hlY2tvdXQ6YThjN2Y4YjgtZmU0NS00ZTRkLThhZmItZDdjYWI2YTM5MTdm");
// Returns: "Checkout:a8c7f8b8-fe45-4e4d-8afb-d7cab6a3917f"
3. Query Checkout in Saleor
Use GraphQL playground to inspect checkout state:
query {
checkout(id: "Q2hlY2tvdXQ6...") {
id
totalPrice {
gross {
amount
currency
}
}
transactions {
id
chargedAmount {
amount
}
authorizedAmount {
amount
}
}
}
}
Payment App Issues
Transaction Fails with “AUTHORIZATION_FAILURE”
Symptom: Transaction is created but fails immediately:
{
"transaction": { "id": "...", "actions": [] },
"transactionEvent": {
"message": "Failed to delivery request.",
"type": "AUTHORIZATION_FAILURE"
}
}
Cause: The payment app (e.g., Dummy Gateway, Stripe, Adyen) is not responding.
Solutions:
- Check Saleor Dashboard â Apps – is the payment app active/healthy?
- Check if the payment app URL is accessible
- Restart the payment app if self-hosted
- Check Saleor Cloud status if using cloud-hosted apps
“CHECKOUT_NOT_FULLY_PAID” Error
Symptom: checkoutComplete fails with:
The authorized amount doesn't cover the checkout's total amount.
Causes:
- Payment app is down – transaction was created but authorization failed
- Stale checkout – previous partial transactions exist
- Amount mismatch – checkout total changed after transaction init
Debug steps:
- Check
[Payment] Transaction init result:logs fortransactionEvent.type - If
AUTHORIZATION_FAILUREâ payment app is down/unreachable - If transaction succeeded but amount is wrong â checkout data is stale
Best Practices
- Always use live checkout data for payment amounts
- Handle checkout not found gracefully (create new checkout)
- Clear checkout after completion to avoid stale data
- Test with fresh checkouts when debugging payment issues
- Check payment app health when transactions fail with
AUTHORIZATION_FAILURE