Add a cart and checkout
Requires: A publishable or secret key with
WRITE_CART;WRITE_SALESfor checkout.
The cart lives on Cobuntu, so a buyer can fill it on your site and find the same cart in the community app. Your site holds a cart token; when the buyer moves to another origin, you mint a one-time code for the URL.
POST /api/v1/communities/:communityTag/cart
GET /api/v1/communities/:communityTag/cart
POST /api/v1/communities/:communityTag/cart/items
PATCH /api/v1/communities/:communityTag/cart/items/:itemId
DELETE /api/v1/communities/:communityTag/cart/items/:itemId
POST /api/v1/communities/:communityTag/cart/merge
POST /api/v1/communities/:communityTag/cart/handoff
POST /api/v1/communities/:communityTag/cart/claimScope WRITE_CART, carried by the publishable key. The token travels in the
X-Cobuntu-Cart-Token header; the client sends it for you.
At a glance
# Create a cart: answers { cart, token }
curl -X POST "https://api.cobuntu.com/api/v1/communities/your-community/cart" -H "X-API-Key: $COBUNTU_PUBLISHABLE_KEY"
# Add a line with the token
curl -X POST "https://api.cobuntu.com/api/v1/communities/your-community/cart/items" \
-H "X-API-Key: $COBUNTU_PUBLISHABLE_KEY" \
-H "X-Cobuntu-Cart-Token: $CART_TOKEN" \
-H "Content-Type: application/json" \
-d '{"productId":"prd_1","tierId":"tier_a5","quantity":1}'The cart
Every call answers the cart with prices resolved live:
{
"id": "cart_1", "communityId": "com_1",
"items": [{
"id": "ci_1", "productId": "prd_1", "checkoutProductId": "prd_1a", "tierId": "tier_a5",
"quantity": 2, "name": "Acme Notebook", "tierName": "A5", "sku": "acme-notebook",
"unitPrice": 2500, "lineTotal": 5000, "currency": "EUR",
"imageUrl": "https://example.com/n.jpg", "productType": "PHYSICAL", "unavailableReason": null
}],
"subtotal": 5000, "currency": "EUR", "totalItems": 2
}- The cart stores references, never prices. A price is what the product costs now.
- A line that can no longer be bought (pulled, or its sales window closed)
stays with
unavailableReasonset and is left out ofsubtotal. Show it, with the reason, instead of dropping it. checkoutProductIdis the id a checkout or quote must use for that line. A variant has its own price row, so sendingproductIdwould price the parent.- Up to 50 different lines, quantity up to 99 each.
Create and keep the token
const { cart, token } = await cobuntu.cart.create();
// 201 for a guest: store token in your own httpOnly cookie
cookies().set("cart_token", token!, { httpOnly: true, secure: true, sameSite: "lax", maxAge: 60 * 60 * 24 * 90 });The token is shown once (it is stored only as a hash). It grants that one cart
and nothing else. token is null only for a signed-in community app session,
whose cart is addressed by the user.
Add, change, remove
const t = cookies().get("cart_token")?.value;
if (!t) throw new Error("no cart yet: call cart.create() first");
let cart = await cobuntu.cart.addItem(t, { productId: "prd_1", tierId: "tier_a5", quantity: 1 });
cart = await cobuntu.cart.updateItem(t, "ci_1", 3); // 0 removes the line
cart = await cobuntu.cart.removeItem(t, "ci_1");
cart = await cobuntu.cart.get(t);Adding a product already in the cart adds to its quantity. Codes and messages:
| Answer | Means |
|---|---|
404 Cart not found | The token does not resolve (purged or wrong). Create a new cart; do not retry the same token. |
404 Product not found | Not purchasable here, including members-only products. |
404 Item not found | The line id is not in this cart. |
400 | productId or quantity missing or out of range, an unknown variant, or more than 50 lines. |
Checkout
Pass the token instead of items:
const r = await cobuntu.checkout.create({ cartToken: t, successUrl, cancelUrl });
redirect(r.checkoutUrl!);Send either items or cartToken, never both (400 AMBIGUOUS_BODY). The
quote takes the same: checkout.quote({ cartToken }).
Carry the cart to another origin
Never put the cart token in a URL: URLs end up in history, Referer headers
and logs. Mint a code instead:
const { code, expiresAt } = await cobuntu.cart.handoff(t); // valid 60 seconds, single use
redirect(`https://your-community.cobuntu.com/checkout?c=${code}`);The receiving side exchanges it:
const { cart, token } = await cobuntu.cart.claim(code);A guest claimer gets a fresh token for this browser, and the issuing browser's
token keeps working. A signed-in claimer has the cart folded into their own.
An expired or used code is 404 That checkout link has expired; a missing one
is 400 code is required.
Merge on sign-in
cart.merge(cartToken) folds a guest cart into the signed-in user's cart. It
needs the community app's own signed-in session as well as the guest token.
A member session from your site is not that session, so from your server it
answers 401 Sign in to merge a cart. The community app merges for you when a
buyer who arrived with a cart signs in there.