Skip to main content

Replacing Checkout (Server-Side)

Use this approach when you want your server to redirect users to Simpler Checkout at the point where they would normally enter your CMS's native checkout.

If you don't need server-side control and just want a simple frontend redirect, see Replacing Checkout (Client-Side) instead.

No Authentication Required

This endpoint does not require any authentication headers. You only need your merchant_id (App ID).

Overview

The flow has three steps:

  1. Build a Cart Request object describing the user's cart.
  2. POST it to the Simpler endpoint.
  3. Forward the redirect response to the user's browser.

After the redirect, the user lands on the Simpler Checkout page where they complete their purchase. Simpler will then call your Platform Interface APIs to price the cart, resolve shipping, and submit the order.

Cart Request Schema

The Cart Request is a JSON-serializable object with the following fields:

FieldTypeRequiredDescription
merchant_idstringYesYour App ID.
currencystringYesISO 4217 currency code (e.g. EUR, USD, GBP). Any valid ISO 4217 code is accepted.
localestringYesIETF language tag per RFC 5646 (e.g. en, el, fr). Any valid tag is accepted.
itemsarrayYesOne or more line items (must not be empty). See below.
couponstringNoA coupon code to pre-apply in the Simpler checkout form.
metadataobjectNoString key-value pairs passed through to your backend on every subsequent Platform Interface call for this session. Intended for read-only attribution or tracking (A/B test buckets, internal references). Do not use it as a mutable state store — see Cart Metadata.

items[]

FieldTypeRequiredDescription
idstringYesProduct identifier as used in your Platform Interface product catalog.
quantityintegerYesNumber of units. Integer, minimum 1.
attributesobjectNoKey-value pairs identifying a specific product variant (e.g. {"color": "blue", "size": "L"}). For products with variants, keys must match a catalog variant definition in your Platform Interface. Omit for simple products with no variants.
Field names differ from the client-side flow

The client-side flow uses the shorter names qty and attrs for the same fields. This difference is intentional — each transport has its own naming.

Sending the Request

POST the cart data as application/x-www-form-urlencoded to:

https://checkout.simpler.so/v1/carts

JSON request bodies are not supported. Because the payload is form-encoded, nested fields (items, attributes, metadata) are serialised using bracket notation — this is what http_build_query() produces. For example, a single item serialises on the wire as:

merchant_id=YOUR_APP_ID&currency=EUR&locale=en&items%5B0%5D%5Bid%5D=PRODUCT_ID&items%5B0%5D%5Bquantity%5D=1&items%5B0%5D%5Battributes%5D%5Bcolor%5D=blue

On success, the server responds with a 303 See Other status and a Location header pointing to the Simpler Checkout page. Your server should forward this redirect to the user's browser:

HTTP/1.1 303 See Other
Location: https://checkout.simpler.so/carts/<uuid>
Content-Length: 0

The Location URL is reusable and does not expire. Note that each request creates a new checkout session — re-POSTing the same cart produces a duplicate session, and there is no idempotency key, so avoid blindly retrying this call on the redirect path.

PHP + cURL Example

$url = 'https://checkout.simpler.so/v1/carts';
$data = [
'merchant_id' => 'YOUR_APP_ID',
'currency' => 'EUR',
'locale' => 'en',
'items' => [
[
'id' => 'PRODUCT_ID',
'quantity' => 1,
'attributes' => [
'color' => 'blue',
'size' => 'L'
]
]
],
'coupon' => 'WELCOME10',
'metadata' => [
'source' => 'cart_page'
]
];

$ch = curl_init($url);

curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($data),
CURLOPT_FOLLOWLOCATION => false,
]);

$response = curl_exec($ch);
$info = curl_getinfo($ch);

if ($info['http_code'] == 303) {
preg_match('/^Location:\s+(.*)$/mi', $response, $matches);
$location = trim($matches[1]);

curl_close($ch);
header("Location: $location", true, 303);
exit;
}

// The request did not result in a redirect — something went wrong.
$headerSize = $info['header_size'];
$body = substr($response, $headerSize);
curl_close($ch);

error_log("Cart request failed with status {$info['http_code']}: $body");

The example sets CURLOPT_FOLLOWLOCATION => false on purpose: you want to capture the Location header and forward the redirect to the shopper's browser, not follow it server-side. Re-emitting it with header("Location: $location", true, 303) preserves the 303 status so the browser performs the redirect.

curl Example

curl -i -X POST https://checkout.simpler.so/v1/carts \
--data-urlencode 'merchant_id=YOUR_APP_ID' \
--data-urlencode 'currency=EUR' \
--data-urlencode 'locale=en' \
--data-urlencode 'items[0][id]=PRODUCT_ID' \
--data-urlencode 'items[0][quantity]=1' \
--data-urlencode 'items[0][attributes][color]=blue' \
--data-urlencode 'items[0][attributes][size]=L' \
--data-urlencode 'coupon=WELCOME10' \
--data-urlencode 'metadata[source]=cart_page'

The -i flag includes the response headers in the output, so you can see the 303 status and the Location header.

Error Responses

All error responses return a JSON body with a message field describing the issue.

Status CodeMeaning
400Invalid request body.
404Invalid App ID / Store not found.
500Internal server error.