---
title: "Checkout"
description: "Integrate guest, authenticated discovery, and learner-cart checkout flows."
source: "https://docs.blend-ed.com/blendx-discovery/checkout/"
---

# Checkout

BlendX supports 3 checkout entry points. All prices are calculated again on the server from published items and configured modes. Never send or trust a price from the browser.

| Flow | Endpoint | Identity |
| --- | --- | --- |
| Guest storefront | `POST /blendxapi/discovery/guest/checkout/` | `clientapisecret`; authenticated users are rejected. |
| Authenticated storefront | `POST /blendxapi/discovery/checkout/` | `clientapisecret` and the learner’s LMS session. |
| Existing learner cart | `POST /blendxapi/payment/checkout/` | Authenticated learner plus `orgName` organisation context. |

## Choose guest or authenticated checkout
The storefront can inspect the forwarded LMS session before choosing a flow:

```
GET /blendxapi/discovery/user/clientapisecret: <CLIENT_API_SECRET>Cookie: sessionid=<forwarded-when-present>
```
An active session returns `authenticated: true` with the user’s email, username, and name. A machine-authenticated request without a learner session returns:

```
{"authenticated": false}
```
## Request items
Guest and authenticated discovery checkout accept:

```
{  "organisation_id": "organisation-uuid",  "items": [    {"type": "course", "id": "course-uuid", "mode": "verified"},    {"type": "program", "id": "program-uuid", "mode": "honor"}  ],  "billing_address": {    "name": "Amina Khan",    "line1": "10 Market Street",    "line2": "",    "city": "Dubai",    "state": "Dubai",    "postal_code": "00000",    "country": "AE"  }}
```
`billing_address` is optional in the API shape but may be required by the configured payment provider or local payment rules. Item IDs are BlendX UUIDs, not Open edX `course-v1:` keys. Course modes must exist and be visible for purchase. Items must be published.

## Guest checkout
Add `email` to the common request body:

```
curl -X POST "https://<LMS_HOST>/blendxapi/discovery/guest/checkout/" \  -H "Content-Type: application/json" \  -H "clientapisecret: $BLENDX_CLIENT_API_SECRET" \  -d '{    "email": "learner@example.com",    "organisation_id": "organisation-uuid",    "items": [{"type":"course","id":"course-uuid","mode":"verified"}]  }'
```
For a free basket, the response has `status: "free"`. An existing account is enrolled; an unknown email receives a registration link and its enrollment is held until registration with that address.

For a paid basket, `status` is `success` and the response is gateway-specific:

- Stripe: `client_secret` , `publishable_key` , and optionally `customer_session_client_secret` .
- PayTabs: `redirect_url` and `payment_id` .
- Razorpay or Geidea: `order_id` , `key_id` , and the amount in subunits as `amount` .

All variants also return `gateway`, `total_price`, `currency`, and `items_detail`. Complete payment with the provider’s client integration. Access is granted only after a signed success webhook is processed.

## Authenticated discovery checkout
Send the same body without `email`. Your server must forward the learner’s LMS session cookie and add `clientapisecret`:

```
POST /blendxapi/discovery/checkout/Content-Type: application/jsonCookie: sessionid=<learner-session>clientapisecret: <CLIENT_API_SECRET>
```
The server creates cart records before starting payment. If the learner already holds another course mode, checkout charges only the non-negative difference between the selected and current mode prices. Regional pricing is applied after that upgrade difference is calculated.

A free basket returns `status: "free"` and queues the cart for enrollment without requiring payment-provider credentials.

## Learner-cart checkout decision
Use this route when the learner already built a server-side cart:

```
POST /blendxapi/payment/checkout/Cookie: sessionid=<learner-session>; orgName=acme
```
The request has no price or item body. The server loads the cart for the signed-in user and selected organisation.

A free cart returns:

```
{  "status": "enrollment_pending",  "total_price": 0,  "currency": "usd",  "redirect_url": "https://<LMS_HOST>/blendxapi/payment/stripe/payment_status_free/"}
```
A paid cart returns `status: "payment_required"`, `gateway`, `total_price`, `currency`, and a gateway `redirect_url`. Open that URL to continue through the existing hosted payment flow.

## Inspect payment capabilities
Use the signed-in learner’s organisation context to decide which payment UI to show:

```
GET /blendxapi/payment/capabilities/Cookie: sessionid=<learner-session>; orgName=acme
```
```
{  "gateway": "razorpay",  "configured": true,  "supports_subscriptions": true,  "supports_billing_portal": false,  "currency": "inr"}
```
`configured` reports whether the selected gateway has its required organisation credentials. Capability flags describe the gateway even when configuration is incomplete.

## Regional pricing
Checkout resolves the visitor’s region once, applies the configured per-item discount, and records `region`, `discountPct`, and `originalPrice` with the transaction. The checkout total is authoritative even if catalog data was cached earlier.

## Production controls
- Rate-limit guest checkout at the storefront edge by IP and, where practical,
normalized email.
- Verify payment webhooks and keep gateway secrets server-side.
- Treat timeouts as unknown outcomes: query provider/payment state before
creating another charge.
- Use idempotency controls supplied by the configured gateway.
- Monitor `400` , `401` , `429` , and webhook-processing failures separately.
