Create a checkout
Create Maya's order and obtain a customer checkout URL.
This continues the Design kit example: Maya buys the configured
STRIPE one-time variant for $19.99. First create the purchase
through the orders API, then get the checkout URL to send to Maya.
1. Prepare the variant and receiver
You need Bash, cURL, jq, an invoice-enabled API token with store permission,
and a purchasable variant from the same store. Replace the key, slug, and
variant ID with your own values.
Before creating the order, configure and test a signed webhook receiver for
order.created, order.paid, and order.completed.
The advanced walkthrough
includes the registration and test requests. The receiver must save the full
payload and recoverable pending work before returning a success response.
These are real store records. Opening and completing checkout can charge the customer.
set -euo pipefail
export SELLAPP_API_BASE_URL='https://sell.app/api'
export SELLAPP_API_KEY='replace-me'
export SELLAPP_STORE='launch-lab'
api() {
local method="$1" path="$2"
shift 2
curl --silent --show-error --fail-with-body \
--request "$method" --url "${SELLAPP_API_BASE_URL}${path}" \
--header "Authorization: Bearer ${SELLAPP_API_KEY}" \
--header "X-STORE: ${SELLAPP_STORE}" \
--header 'Accept: application/json' \
--dump-header /dev/stderr "$@"
}
api GET '/v2/products?limit=1' | jq '.data'export SELLAPP_VARIANT_ID='4321'2. Create Maya's order and checkout session
order_json=$(api POST /v2/orders \
--header 'Idempotency-Key: launch-lab-maya-order-001' \
--header 'Content-Type: application/json' \
--data "$(jq -nc --arg variant "$SELLAPP_VARIANT_ID" '{
customer_email:"maya@example.com",
payment_method:"STRIPE",
product_variants:{($variant):{quantity:1}}
}')")
SELLAPP_ORDER_ID=$(jq -er '.data.id' <<< "$order_json")
checkout_json=$(api POST "/v2/orders/${SELLAPP_ORDER_ID}/checkout" \
--header 'Idempotency-Key: launch-lab-maya-checkout-001')
SELLAPP_CHECKOUT_URL=$(jq -er '.data.payment.checkout_url' <<< "$checkout_json")
printf '%s\n' "$SELLAPP_CHECKOUT_URL"Order creation returns 201; checkout returns 201 for a new payment session
or 200 when reusing an existing session. Both return an order
under data and include data.payment.checkout_url when a URL is available.
{"data":{"id":9001,"status":"PENDING","customer":{"email":"maya@example.com"},"payment":{"checkout_url":"https://checkout.stripe.com/c/pay/cs_example"},"totals":{"currency":"USD","total_cents":1999}}}{"data":{"id":9001,"status":"PENDING","payment":{"checkout_url":"https://checkout.stripe.com/c/pay/cs_example"}}}Open the URL printed by your request, not the illustrative URL above. Completing checkout can charge the configured payment method. Use a unique idempotency key for each intended order and a separate key for checkout. If a response is lost, retry the same operation with the same key and identical body. Do not reuse these illustrative keys for another purchase. See idempotency for retention and conflicts.
3. Check what happened after checkout
api GET "/v2/orders/${SELLAPP_ORDER_ID}" \
| jq '.data | {id, status, customer, totals, line_items}'For this paid example, expect data.payment.checkout_url. A zero-priced checkout
can instead return 200 with a paid order and no URL; fulfillment may still be queued.
Do not redirect to an absent URL or mark the purchase complete based on a
browser return alone.
Use signed webhooks and retrieved state. order.paid records entry into the
paid state; order.completed records the completed order state, not completion
of every external task, such as granting access in your app.
See Fulfil an order for processing that can recover after a crash.