Skip to content
BaxPayDevelopers

Online shop

Storefront API

The endpoints the BaxPay storefront itself uses, open to any front end: a custom site, a mobile app or a kiosk. They need no API key and only expose what the shop already shows the public.

All paths are under https://api.baxpay.com/v1. Each shop is addressed by its slug (the {slug} in https://{slug}.baxpay.com). Prices are always computed on the server from the merchant's catalog: what the client sends is never trusted.

A checkout in four calls

Read the catalog

GET /shop/{slug}/catalog lists products, variants, add-ons and categories.

Price the cart

POST /shop/{slug}/quote returns the priced lines, delivery and pickup options and any discount.

Check out

POST /shop/{slug}/checkout creates the order and returns a payUrl.

Send the customer to pay

Redirect to payUrl. After paying, the customer lands on the shop's order page; read it with GET /shop/{slug}/orders/{token}.

Resolve a shop

GET/shop/resolve?host=shop.example.com

Finds a shop from a host name ({slug}.baxpay.com or a verified custom domain). Without ?host=, the request's Host header is used. Returns the shop's public face:

curl "https://api.baxpay.com/v1/shop/resolve?host=maison-lumiere.baxpay.com"
{
  "success": true,
  "data": {
    "slug": "maison-lumiere",
    "displayName": "…",
    "language": "fr",
    "currency": "EUR",
    "productsEnabled": true,
    "theme": {},
    "settings": {}
  }
}
slugstring
Shop address.
displayNamestring
The business's trading name.
logoUrl, iconUrlstring | null
Public image URLs.
themeobject
Colors, fonts and homepage sections chosen by the merchant.
settingsobject
Delivery and pickup, contact, social links, policies (terms, returns, shipping, privacy, legal notice, cookies), SEO and checkout options.
languagestring
The shop's language (fr, en, de…). Use it as the default locale.
currencystring
EUR.

Read the catalog

GET/shop/{slug}/catalog
GET/shop/{slug}/products/{productId}one product + up to 4 related

Only active products sold ONLINE or BOTH are listed (up to 500).

curl https://api.baxpay.com/v1/shop/maison-lumiere/catalog
{
  "success": true,
  "data": {
    "slug": "maison-lumiere",
    "displayName": "…",
    "categories": [],
    "products": [{ "id": "…", "name": "…", "currency": "EUR", "priceType": "FIXED" }]
  }
}

One product comes with its shop and up to 4 related products:

curl https://api.baxpay.com/v1/shop/maison-lumiere/products/6f0e2c4a-…
{
  "success": true,
  "data": {
    "slug": "maison-lumiere",
    "displayName": "…",
    "product": { "id": "6f0e2c4a-…", "name": "…", "unitAmount": 12.5, "priceType": "FIXED", "booking": null },
    "related": []
  }
}

Each product looks like this (shown with variants and add-ons):

response (one product)
{
  "id": "…",
  "name": "Lampe Arc",
  "description": "Brass and linen, 160 cm.",
  "unitAmount": 189,
  "currency": "EUR",
  "priceType": "FIXED",
  "quantityStep": 1,
  "categoryId": "…",
  "supplyType": "GOODS",
  "imageUrl": "https://cdn.baxpay.com/…",
  "trackInventory": true,
  "stockHint": "low",
  "variants": [{ "id": "…", "name": "Black", "sku": "ARC-BLK", "unitAmount": 199, "imageUrl": null }],
  "modifierGroups": [
    { "id": "…", "name": "Bulb", "minSelect": 0, "maxSelect": 1,
      "modifiers": [{ "id": "…", "name": "Warm LED", "priceDelta": 9 }] }
  ],
  "booking": null
}
unitAmountnumber
Price in major units (euros), VAT included. A variant with its own unitAmount overrides it.
priceTypeFIXED | VARIABLE
VARIABLE (a gift card, a donation): the customer sets the price, sent as unitAmount in the cart.
stockHintstring
in_stock, low (5 or fewer), out or unlimited (stock not tracked), at the shop's stock location.
supplyTypeGOODS | SERVICES
Services never need delivery.
bookingobject | null
Set on bookable services: the customer must pick a time slot. See Bookings.

Price a cart

POST/shop/{slug}/quote
curl -X POST https://api.baxpay.com/v1/shop/maison-lumiere/quote \
  -H "Content-Type: application/json" \
  -d '{ "items": [{ "productId": "6f0e2c4a-…", "quantity": 2 }], "country": "FR", "discountCode": "WELCOME10" }'
{
  "success": true,
  "data": {
    "subtotalMinor": 2500,
    "hasGoods": true,
    "lines": [{ "productId": "6f0e2c4a-…", "quantity": 2, "unitMinor": 1250, "lineMinor": 2500, "supplyType": "GOODS" }],
    "discount": null,
    "discountError": { "reason": "NOT_FOUND" },
    "options": [{ "id": "standard", "kind": "SHIPPING", "priceMinor": 590 }]
  }
}

The request body in full:

request
{
  "items": [
    { "productId": "…", "variantId": "…", "quantity": 1, "modifierIds": ["…"] }
  ],
  "country": "FR",
  "discountCode": "WELCOME10",
  "email": "[email protected]"
}
itemsarrayrequired
1 to 50 lines: productId, optional variantId, quantity, modifierIds (add-ons), unitAmount for VARIABLE products, and booking for bookable services.
countrystring
ISO code: limits shipping rates to those serving that country.
discountCodestring
A code to check. A refused code is reported, not an error.
emailstring
Lets "once per customer" codes be checked.
response
{
  "subtotalMinor": 18900,
  "hasGoods": true,
  "lines": [
    { "productId": "…", "name": "Lampe Arc", "quantity": 1, "unitMinor": 18900, "lineMinor": 18900,
      "supplyType": "GOODS", "imageUrl": "https://…", "vatRate": 20 }
  ],
  "discount": { "code": "WELCOME10", "kind": "PERCENT", "discountMinor": 1890, "freeShipping": false },
  "discountError": null,
  "countries": ["BE", "FR"],
  "options": [
    { "id": "standard", "kind": "SHIPPING", "label": "Standard delivery", "priceMinor": 0, "detail": "2-4" },
    { "id": "pickup-1", "kind": "PICKUP", "label": "Workshop", "priceMinor": 0, "detail": "12 rue des Lampes, Paris" }
  ],
  "requirePhone": false,
  "allowNote": true
}

Amounts in the quote and order are in minor units (cents). options[].detail is the delivery time in days for shipping, or the address for pickup. A cart of services only gets one option of kind NONE. Free-over thresholds use the subtotal after the discount.

discountError is null or { reason, minSubtotalMinor? }, where reason is one of NOT_FOUND, NOT_STARTED, EXPIRED, USED_UP, ALREADY_USED, MIN_SUBTOTAL or NOT_ELIGIBLE.

Check out

POST/shop/{slug}/checkout
curl -X POST https://api.baxpay.com/v1/shop/maison-lumiere/checkout \
  -H "Content-Type: application/json" \
  -d '{
    "items": [{ "productId": "6f0e2c4a-…", "quantity": 1 }],
    "customer": { "name": "Camille Martin", "email": "[email protected]" },
    "fulfilmentOptionId": "standard",
    "address": { "line1": "12 rue des Lampes", "postalCode": "75011", "city": "Paris", "country": "FR" },
    "locale": "fr"
  }'
{
  "success": true,
  "data": { "orderNumber": 1001, "orderToken": "kW3c…", "payUrl": "https://pay.baxpay.com/plk_…", "totalMinor": 1840, "currency": "EUR" }
}

All the fields:

request
{
  "items": [{ "productId": "…", "quantity": 1 }],
  "customer": { "name": "Camille Martin", "email": "[email protected]", "phone": "+33 6 12 34 56 78" },
  "fulfilmentOptionId": "standard",
  "address": { "line1": "12 rue des Lampes", "postalCode": "75011", "city": "Paris", "country": "FR" },
  "note": "Please ring twice.",
  "locale": "fr",
  "discountCode": "WELCOME10"
}
itemsarrayrequired
Same lines as the quote. Stock is checked again.
customerobjectrequired
name (2–100 characters), email, and phone (required when the quote said requirePhone).
fulfilmentOptionIdstring
An options[].id from the quote. Required when the cart has goods.
addressobject
Required for shipping: line1, postalCode, city, country (one the rate serves; French postal codes have 5 digits), optional line2.
notestring
Up to 500 characters, when the shop allows notes.
localestring
The customer's language: their order emails use it.
discountCodestring
Checked again; a refused code fails the checkout.
response
{
  "orderNumber": 1042,
  "orderToken": "kW3c…",
  "payUrl": "https://pay.baxpay.com/plk_…",
  "totalMinor": 17010,
  "currency": "EUR"
}

Redirect the customer to payUrl. The link stays open for a while and holds the stock; an unpaid order stays PENDING_PAYMENT. Once paid, the customer is sent back to the shop's order page (https://{shop}/order/{orderToken}) and receives the order confirmation by email.

Show the order

GET/shop/{slug}/orders/{token}
curl https://api.baxpay.com/v1/shop/maison-lumiere/orders/kW3c…
{
  "success": true,
  "data": { "number": 1001, "status": "PENDING_PAYMENT", "totalMinor": 1840, "currency": "EUR", "payUrl": "https://pay.baxpay.com/plk_…", "bookings": [] }
}

The customer may also drop an unpaid order: POST /shop/{slug}/orders/{token}/cancel.

Returns what the customer may see: number, status (PENDING_PAYMENT, PAID, READY_FOR_PICKUP, SHIPPED, COMPLETED, CANCELLED), items, subtotalMinor, discountMinor, discountCode, shippingMinor, totalMinor, currency, the delivery choice and pickup point, the dates of each step, trackingNumber / trackingUrl, the order's bookings, and payUrl while it is still unpaid.

Errors

StatusWhenBody
400Empty or invalid cart, not enough stock, invalid customer or address, no delivery option chosen{ "message": "Only 2 × Lampe Arc left." }
400Refused discount code at checkout{ "message": "…", "reason": "EXPIRED" }
403Online shop not available on the platform{ "message": "…" }
404Unknown or unpublished shop, product or order{ "message": "…" }
409A booked time was taken meanwhile{ "message": "…", "reason": "SLOT_TAKEN" }
429Too many requests—
curl -X POST https://api.baxpay.com/v1/shop/maison-lumiere/quote \
  -H "Content-Type: application/json" \
  -d '{ "items": [] }'

message is a string, or an array of strings for validation errors (as above). It's in English and safe to show; use reason when you show your own text. See Errors for the shape.