Carts and checkout
Make and fill a cart, estimate shipping and hand it to the shop's checkout page with a single-use link.
A cart is named by its token. Make one with createCart, keep its cartToken, and reach it again with cart. The CartSession that cart returns sends the token as the X-Cart-Token header for you. Every change answers with the whole cart, with its lines, totals and coupon.
Each write takes $idempotencyKey last: your own Idempotency-Key, or false for none. One is made for you when null (see retries).
Create a cart
$storefront->createCart(string|false|null $idempotencyKey = null): array
A new empty cart, named by the returned cartToken. Keep the token, in the shopper's session or a cookie, to come back to the cart. Made on a client with a shopper's access token, the cart is theirs and priced for them. Limited like cart writes (30 a minute, 300 an hour per visitor).
Parameters
| Parameter |
Type |
|
$idempotencyKey |
string|false|null |
Your own Idempotency-Key, or false for none. One is made for you when null. |
Returns
The new Cart array:
| Field | Type | Description |
|---|
cartToken | string | Send it as X-Cart-Token. |
mode | string | enquiry: a wholesale member's cart, sent to the shop as a quotation request. One of checkout, enquiry. |
customerId | integer or null | The shopper the cart belongs to; null for a guest cart. |
items | array of CartItem | |
items[].id | integer | |
items[].productId | integer | |
items[].slug | string or null | |
items[].title | string or null | |
items[].image | string (uri) or null | |
items[].quantity | integer | |
items[].unitPrice | Money | Fields: amount, currency, formatted. |
items[].lineTotal | Money | Fields: amount, currency, formatted. |
totalQuantity | integer | |
subtotal | Money | Fields: amount, currency, formatted. |
discount | Money | Fields: amount, currency, formatted. |
coupon | object or null | |
coupon.code | string | |
coupon.autoApplied | boolean | |
couponError | string or null | Why the coupon on the cart doesn't apply now. |
offers | array of string | Labels of the offers applied. |
taxAdded | Money | Fields: amount, currency, formatted. |
taxIncluded | Money | Fields: amount, currency, formatted. |
totalBeforeShipping | Money | Fields: amount, currency, formatted. |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | Not found, or the shop's plan has no Integration API. |
409 | cart_disabled, or idempotency_in_progress. |
422 | Validation errors ({ message, errors }), or an idempotency refusal(idempotency_key_reused, idempotency_key_invalid). |
429 | Rate limited. |
Example
$cart = $storefront->createCart();
$request->session()->put('cartToken', $cart['cartToken']);
Open a cart
$storefront->cart(string $cartToken): CartSession
A CartSession for the cart a token names. Its methods below act on that cart, and its token property holds the token.
Parameters
| Parameter |
Type |
|
$cartToken |
string |
The cart's token. |
Returns
A CartSession. No request is made.
Example
$session = $storefront->cart($cart['cartToken']);
Get the cart
$session->get(): array
The cart as it is now, priced as the shop's own cart page prices it.
Returns
A Cart array:
| Field | Type | Description |
|---|
cartToken | string | Send it as X-Cart-Token. |
mode | string | enquiry: a wholesale member's cart, sent to the shop as a quotation request. One of checkout, enquiry. |
customerId | integer or null | The shopper the cart belongs to; null for a guest cart. |
items | array of CartItem | |
items[].id | integer | |
items[].productId | integer | |
items[].slug | string or null | |
items[].title | string or null | |
items[].image | string (uri) or null | |
items[].quantity | integer | |
items[].unitPrice | Money | Fields: amount, currency, formatted. |
items[].lineTotal | Money | Fields: amount, currency, formatted. |
totalQuantity | integer | |
subtotal | Money | Fields: amount, currency, formatted. |
discount | Money | Fields: amount, currency, formatted. |
coupon | object or null | |
coupon.code | string | |
coupon.autoApplied | boolean | |
couponError | string or null | Why the coupon on the cart doesn't apply now. |
offers | array of string | Labels of the offers applied. |
taxAdded | Money | Fields: amount, currency, formatted. |
taxIncluded | Money | Fields: amount, currency, formatted. |
totalBeforeShipping | Money | Fields: amount, currency, formatted. |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | cart_not_found: the token names no open cart of this shop (opened at checkout, checked out, or another's), or the plan has no Integration API. |
409 | cart_disabled: this shop takes no orders through a cart. |
429 | Rate limited. |
Example
$cart = $session->get();
Add a product
$session->addItem(int $productId, int $quantity = 1, string|false|null $idempotencyKey = null): array
Adds a product, or more of one already in the cart. No stock is held until the cart is claimed at checkout. Limited like cart writes.
Parameters
| Parameter |
Type |
|
$productId |
int |
The product's id. |
$quantity |
int |
How many, from 1 to 10000. Default 1. |
$idempotencyKey |
string|false|null |
Your own Idempotency-Key, or false for none. |
Returns
The changed Cart array:
| Field | Type | Description |
|---|
cartToken | string | Send it as X-Cart-Token. |
mode | string | enquiry: a wholesale member's cart, sent to the shop as a quotation request. One of checkout, enquiry. |
customerId | integer or null | The shopper the cart belongs to; null for a guest cart. |
items | array of CartItem | |
items[].id | integer | |
items[].productId | integer | |
items[].slug | string or null | |
items[].title | string or null | |
items[].image | string (uri) or null | |
items[].quantity | integer | |
items[].unitPrice | Money | Fields: amount, currency, formatted. |
items[].lineTotal | Money | Fields: amount, currency, formatted. |
totalQuantity | integer | |
subtotal | Money | Fields: amount, currency, formatted. |
discount | Money | Fields: amount, currency, formatted. |
coupon | object or null | |
coupon.code | string | |
coupon.autoApplied | boolean | |
couponError | string or null | Why the coupon on the cart doesn't apply now. |
offers | array of string | Labels of the offers applied. |
taxAdded | Money | Fields: amount, currency, formatted. |
taxIncluded | Money | Fields: amount, currency, formatted. |
totalBeforeShipping | Money | Fields: amount, currency, formatted. |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | cart_not_found: the token names no open cart of this shop (opened at checkout, checked out, or another's), or the plan has no Integration API. |
409 | cart_disabled, or idempotency_in_progress. |
422 | cart_change_refused with the shopper-worded reason (out of stock…), validation errors, or an idempotency refusal. |
429 | Rate limited. |
Example
$cart = $session->addItem(12, 2);
Change a line's quantity
$session->updateItem(int $itemId, int $quantity, string|false|null $idempotencyKey = null): array
Sets a line's quantity. A quantity of 0 removes the line. Limited like cart writes.
Parameters
| Parameter |
Type |
|
$itemId |
int |
The cart line's id (items[].id). |
$quantity |
int |
The new quantity, from 0 to 10000. |
$idempotencyKey |
string|false|null |
Your own Idempotency-Key, or false for none. |
Returns
The changed Cart array:
| Field | Type | Description |
|---|
cartToken | string | Send it as X-Cart-Token. |
mode | string | enquiry: a wholesale member's cart, sent to the shop as a quotation request. One of checkout, enquiry. |
customerId | integer or null | The shopper the cart belongs to; null for a guest cart. |
items | array of CartItem | |
items[].id | integer | |
items[].productId | integer | |
items[].slug | string or null | |
items[].title | string or null | |
items[].image | string (uri) or null | |
items[].quantity | integer | |
items[].unitPrice | Money | Fields: amount, currency, formatted. |
items[].lineTotal | Money | Fields: amount, currency, formatted. |
totalQuantity | integer | |
subtotal | Money | Fields: amount, currency, formatted. |
discount | Money | Fields: amount, currency, formatted. |
coupon | object or null | |
coupon.code | string | |
coupon.autoApplied | boolean | |
couponError | string or null | Why the coupon on the cart doesn't apply now. |
offers | array of string | Labels of the offers applied. |
taxAdded | Money | Fields: amount, currency, formatted. |
taxIncluded | Money | Fields: amount, currency, formatted. |
totalBeforeShipping | Money | Fields: amount, currency, formatted. |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | cart_not_found: the token names no open cart of this shop (opened at checkout, checked out, or another's), or the plan has no Integration API. |
409 | cart_disabled, or idempotency_in_progress. |
422 | cart_change_refused with the shopper-worded reason (out of stock…), validation errors, or an idempotency refusal. |
429 | Rate limited. |
Example
$cart = $session->updateItem($cart['items'][0]['id'], 3);
Remove a line
$session->removeItem(int $itemId, string|false|null $idempotencyKey = null): array
Removes a line from the cart.
Parameters
| Parameter |
Type |
|
$itemId |
int |
The cart line's id (items[].id). |
$idempotencyKey |
string|false|null |
Your own Idempotency-Key, or false for none. |
Returns
The changed Cart array:
| Field | Type | Description |
|---|
cartToken | string | Send it as X-Cart-Token. |
mode | string | enquiry: a wholesale member's cart, sent to the shop as a quotation request. One of checkout, enquiry. |
customerId | integer or null | The shopper the cart belongs to; null for a guest cart. |
items | array of CartItem | |
items[].id | integer | |
items[].productId | integer | |
items[].slug | string or null | |
items[].title | string or null | |
items[].image | string (uri) or null | |
items[].quantity | integer | |
items[].unitPrice | Money | Fields: amount, currency, formatted. |
items[].lineTotal | Money | Fields: amount, currency, formatted. |
totalQuantity | integer | |
subtotal | Money | Fields: amount, currency, formatted. |
discount | Money | Fields: amount, currency, formatted. |
coupon | object or null | |
coupon.code | string | |
coupon.autoApplied | boolean | |
couponError | string or null | Why the coupon on the cart doesn't apply now. |
offers | array of string | Labels of the offers applied. |
taxAdded | Money | Fields: amount, currency, formatted. |
taxIncluded | Money | Fields: amount, currency, formatted. |
totalBeforeShipping | Money | Fields: amount, currency, formatted. |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | cart_not_found: the token names no open cart of this shop (opened at checkout, checked out, or another's), or the plan has no Integration API. |
409 | cart_disabled, or idempotency_in_progress. |
422 | cart_change_refused with the shopper-worded reason (out of stock…), validation errors, or an idempotency refusal. |
429 | Rate limited. |
Example
$cart = $session->removeItem($cart['items'][0]['id']);
Apply a coupon
$session->applyCoupon(string $code, string|false|null $idempotencyKey = null): array
Checked as the shop's own cart page checks it. A coupon only a known customer can use (first order, per customer) stays on the cart for checkout to decide. Limited to 10 attempts a minute and 60 an hour per visitor.
Parameters
| Parameter |
Type |
|
$code |
string |
The coupon code, up to 50 characters, any case. |
$idempotencyKey |
string|false|null |
Your own Idempotency-Key, or false for none. |
Returns
The changed Cart array:
| Field | Type | Description |
|---|
cartToken | string | Send it as X-Cart-Token. |
mode | string | enquiry: a wholesale member's cart, sent to the shop as a quotation request. One of checkout, enquiry. |
customerId | integer or null | The shopper the cart belongs to; null for a guest cart. |
items | array of CartItem | |
items[].id | integer | |
items[].productId | integer | |
items[].slug | string or null | |
items[].title | string or null | |
items[].image | string (uri) or null | |
items[].quantity | integer | |
items[].unitPrice | Money | Fields: amount, currency, formatted. |
items[].lineTotal | Money | Fields: amount, currency, formatted. |
totalQuantity | integer | |
subtotal | Money | Fields: amount, currency, formatted. |
discount | Money | Fields: amount, currency, formatted. |
coupon | object or null | |
coupon.code | string | |
coupon.autoApplied | boolean | |
couponError | string or null | Why the coupon on the cart doesn't apply now. |
offers | array of string | Labels of the offers applied. |
taxAdded | Money | Fields: amount, currency, formatted. |
taxIncluded | Money | Fields: amount, currency, formatted. |
totalBeforeShipping | Money | Fields: amount, currency, formatted. |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | cart_not_found: the token names no open cart of this shop (opened at checkout, checked out, or another's), or the plan has no Integration API. |
409 | cart_disabled, or idempotency_in_progress. |
422 | The code is unknown or can't be used (errors.code), or an idempotency refusal. |
429 | Rate limited. |
Example
$cart = $session->applyCoupon('WELCOME10');
Remove the coupon
$session->removeCoupon(string|false|null $idempotencyKey = null): array
Removes the cart's coupon. Removing an auto-applied coupon stops coupons auto-applying to this cart.
Parameters
| Parameter |
Type |
|
$idempotencyKey |
string|false|null |
Your own Idempotency-Key, or false for none. |
Returns
The changed Cart array:
| Field | Type | Description |
|---|
cartToken | string | Send it as X-Cart-Token. |
mode | string | enquiry: a wholesale member's cart, sent to the shop as a quotation request. One of checkout, enquiry. |
customerId | integer or null | The shopper the cart belongs to; null for a guest cart. |
items | array of CartItem | |
items[].id | integer | |
items[].productId | integer | |
items[].slug | string or null | |
items[].title | string or null | |
items[].image | string (uri) or null | |
items[].quantity | integer | |
items[].unitPrice | Money | Fields: amount, currency, formatted. |
items[].lineTotal | Money | Fields: amount, currency, formatted. |
totalQuantity | integer | |
subtotal | Money | Fields: amount, currency, formatted. |
discount | Money | Fields: amount, currency, formatted. |
coupon | object or null | |
coupon.code | string | |
coupon.autoApplied | boolean | |
couponError | string or null | Why the coupon on the cart doesn't apply now. |
offers | array of string | Labels of the offers applied. |
taxAdded | Money | Fields: amount, currency, formatted. |
taxIncluded | Money | Fields: amount, currency, formatted. |
totalBeforeShipping | Money | Fields: amount, currency, formatted. |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | cart_not_found: the token names no open cart of this shop (opened at checkout, checked out, or another's), or the plan has no Integration API. |
409 | cart_disabled, or idempotency_in_progress. |
422 | cart_change_refused with the shopper-worded reason (out of stock…), validation errors, or an idempotency refusal. |
429 | Rate limited. |
Estimate shipping
$session->estimateShipping(array $destination, string|false|null $idempotencyKey = null): array
The shipping rules the shopper could choose at checkout, priced for a destination as checkout prices them. Only when the shop turned its shipping calculator on (shippingEstimates from shop()). A shop without zones ships anywhere, free. Limited to 30 a minute and 300 an hour per visitor.
Parameters
| Parameter |
Type |
|
$destination |
array |
The destination's fields, below. |
$idempotencyKey |
string|false|null |
Your own Idempotency-Key, or false for none. |
Request fields
| Field | Type | Description |
|---|
shippingCountryCode required | string | ISO 3166-1 alpha-3. At most 3 characters. |
shippingCity | string or null | At most 100 characters. |
shippingState | string or null | At most 255 characters. |
shippingPostalCode | string or null | At most 50 characters. |
shippingAddressLine1 | string or null | At most 1000 characters. |
shippingStateId | integer or null | |
shippingCityId | integer or null | |
shippingNeighbourhoodId | integer or null | |
shippingLocationUnlisted | boolean or null | |
Returns
A ShippingEstimate array:
| Field | Type | Description |
|---|
shipsTo | boolean | Whether the shop ships to the country. |
shipsFree | boolean | True for a shop without shipping zones (ships anywhere, free). |
quotes | array of object | Empty for an empty or digital-only cart. |
quotes[].methodId | integer | |
quotes[].name | string | |
quotes[].cost | number | A plain amount in the shop's base currency (not a Money object). |
quotes[].isLiveRate | boolean | |
quotes[].isCarrierPrice | boolean | |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | shipping_estimates_off, cart_not_found, or the shop's plan has no Integration API. |
409 | cart_disabled, or idempotency_in_progress. |
422 | Validation errors ({ message, errors }), or an idempotency refusal(idempotency_key_reused, idempotency_key_invalid). |
429 | Rate limited. |
Example
$estimate = $session->estimateShipping([
'shippingCountryCode' => 'SAU',
'shippingCity' => 'Riyadh',
]);
if ($estimate['shipsTo']) {
foreach ($estimate['quotes'] as $quote) {
echo $quote['name'], ': ', $quote['cost'], PHP_EOL;
}
}
Get a checkout link
$session->checkout(array $options = [], string|false|null $idempotencyKey = null): array
A single-use link to the shop's checkout page with this cart, valid for 30 minutes, for the shopper's browser to open now. Once opened, the cart belongs to that browser and the token no longer reaches it. When requiresSignIn is true, the cart belongs to a shopper, who must be signed in on the shop. A wholesale member's cart (mode enquiry) leads to the shop's cart page, where it is sent as a quotation request. Limited like cart writes.
Parameters
| Parameter |
Type |
|
$options |
array |
The checkout's fields, below. |
$idempotencyKey |
string|false|null |
Your own Idempotency-Key, or false for none. |
Request fields
| Field | Type | Description |
|---|
returnPath | string or null | Path on the shop's site (Settings → Integration API) the receipt's "Continue shopping" links to. Starts with a single /. At most 500 characters. |
cartPath | string or null | Path on the shop's site checkout's back link leads to. Starts with a single /. At most 500 characters. |
attribution | object or null | The visit's UTM tags and click ids, recorded when the link is opened. |
attribution.utmSource | string or null | At most 255 characters. |
attribution.utmMedium | string or null | At most 255 characters. |
attribution.utmCampaign | string or null | At most 255 characters. |
attribution.fbclid | string or null | At most 255 characters. |
attribution.gclid | string or null | At most 255 characters. |
attribution.gbraid | string or null | At most 255 characters. |
attribution.wbraid | string or null | At most 255 characters. |
attribution.dclid | string or null | At most 255 characters. |
attribution.msclkid | string or null | At most 255 characters. |
attribution.ttclid | string or null | At most 255 characters. |
attribution.liFatId | string or null | At most 255 characters. |
attribution.twclid | string or null | At most 255 characters. |
Returns
A CheckoutLink array:
| Field | Type | Description |
|---|
checkoutUrl | string (uri) | Open it in the shopper's browser. Single use. |
expiresAt | string (date-time) | |
mode | string | One of checkout, enquiry. |
requiresSignIn | boolean | The browser must be signed in on the checkout host as the cart's owner. |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | cart_not_found: the token names no open cart of this shop (opened at checkout, checked out, or another's), or the plan has no Integration API. |
409 | cart_disabled, or idempotency_in_progress. |
422 | Invalid paths or attribution, cart_empty, or an idempotency refusal. |
429 | Rate limited. |
Example
$link = $session->checkout([
'returnPath' => '/thanks',
'cartPath' => '/cart',
'attribution' => ['utmSource' => 'newsletter', 'utmCampaign' => 'november'],
]);
return redirect()->away($link['checkoutUrl']);