Hosted checkout and purchase lifecycle
Send buyers to the hosted checkout flow. Preserve the original purchase attempt until the server can confirm its outcome.
Prefer the hosted journey
- Read the public storefront or obtain an owner checkout link for the chosen plan.
- Redirect to the returned URL on pay.mybexo.com. Do not construct checkout tokens.
- Let the buyer complete the sign-in, account and consent/terms steps shown in that flow.
- Use server reconciliation and the current membership state to determine the result.
- Show pending or failed states honestly; a browser callback is not payment capture.
The code contains provider order/subscription fields, but payment enablement and provider ownership are configuration and contract concerns. Confirm the payment arrangement with BEXO before accepting live payments. This reference does not promise vendor-owned settlement, automatic renewals or a refund-management API.
A purchase fixes a snapshot
For a new purchase the server validates the buyer’s recent phone verification and acceptedTermsVersion returned by the link. It reads the plan and fixes the amount, currency, duration and plan name. The client cannot activate a membership by choosing a price or setting a status.
curl --fail-with-body "$BEXO_API_BASE/vendor/checkout" \
-H "Authorization: Bearer $BEXO_BUYER_SESSION" \
-H "Idempotency-Key: purchase_example_20261019_001" \
-H "Content-Type: application/json" \
--data '{"checkoutToken":"<TOKEN_FROM_HOSTED_LINK>","acceptedTerms":true,"acceptedTermsVersion":"<VERSION_FROM_LINK>"}'This example illustrates the contract; use the hosted flow unless BEXO has agreed a custom buyer implementation with you. Never send the buyer session to an unrelated vendor server.
Retry the same attempt with the same key
Idempotency-Key is 16–128 letters, numbers, underscores or hyphens. Generate it once for the buyer’s intended purchase and retain it across network errors. A repeat can return HTTP 200 with the original purchase rather than HTTP 201. Reusing a key with a different token returns IDEMPOTENCY_CONFLICT.
| Situation | Recovery |
|---|---|
| CHECKOUT_PENDING / uncertain order creation | Retry with the same key. Do not start another order because a response was lost. |
| PHONE_VERIFICATION_REQUIRED | Complete the required phone verification through the supported flow. |
| PLAN_PAUSED / vendor unavailable | Refresh the offer or contact the vendor; do not use cached checkout data. |
| CHECKOUT_EXPIRED on resume | The 30-minute pending window ended. Begin a new purchase from the vendor. |
Payment confirmation and reconciliation
The reconcile endpoint takes provider callback identifiers and a signature for the original order or subscription. The server verifies proof and capture facts before confirming entitlement. Provider-signed webhooks are a separate server-to-server input to BEXO; vendors must not call that gateway webhook endpoint as a payment-success shortcut.
Do not store payment signatures, checkout tokens or complete buyer payloads in analytics. Keep order and membership references for reconciliation. If a payment appears captured while membership is pending, use the supported reconciliation/support workflow instead of creating another charge.
Endpoint reference
Paths below include the API prefix. The OpenAPI server URL already includes /api.
/api/vendor/public/{slug}/{tenantId}Read a public storefront
Returns public vendor metadata, availability and plans. Unapproved vendors return coming_soon and an empty plans list.
Parameters
| Name | Location | Requirement |
|---|---|---|
tenantId | path | Required. Resource identifier. |
slug | path | Required. Resource identifier. |
Request template
Set the API base to include /api. Replace path placeholders before running.
curl --fail-with-body -X GET "$BEXO_API_BASE/vendor/public/{slug}/{tenantId}"Response fields
| Field | Type | Details |
|---|---|---|
vendor | object | See the downloadable schema for nested fields. |
availability | string | Storefront state.available · coming_soon |
plans | array | See the downloadable schema for nested fields. |
/api/vendor/public/checkout-linkCreate a public checkout link
Requires an active plan and a production_active vendor. Prefer redirecting the buyer to the returned hosted URL. This route returns a server-issued token; never construct one.
Request template
Set the API base to include /api. Replace path placeholders and prepare request.json from the body fields below before running.
curl --fail-with-body -X POST "$BEXO_API_BASE/vendor/public/checkout-link" \
-H "Content-Type: application/json" \
--data @request.jsonRequest body
| Field | Type | Details |
|---|---|---|
tenantPlanIdRequired | string | An active plan belonging to the vendor. |
Response fields
| Field | Type | Details |
|---|---|---|
linkId | string | Link reference. The owner route may return the plan ID in this field; treat it as opaque. |
token | string | Hosted checkout token. Do not log it. |
url | string | Hosted checkout URL. |
expiresAt | string | Server-returned availability hint. Plan and vendor checks still apply. |
/api/vendor/checkout-linksCreate an owner checkout link
Requires an approved owner and active, tenant-owned plan. Returns a stable hosted plan URL; every purchase still checks current availability.
Request template
Set the API base to include /api. Replace path placeholders and prepare request.json from the body fields below before running.
curl --fail-with-body -X POST "$BEXO_API_BASE/vendor/checkout-links" \
-H "Authorization: Bearer $BEXO_MEMBER_TOKEN" \
-H "Content-Type: application/json" \
--data @request.jsonRequest body
| Field | Type | Details |
|---|---|---|
tenantPlanIdRequired | string | An active plan belonging to the vendor. |
Example request body
{
"tenantPlanId": "11111111-1111-4111-8111-111111111111"
}Response fields
| Field | Type | Details |
|---|---|---|
linkId | string | Link reference. The owner route may return the plan ID in this field; treat it as opaque. |
token | string | Hosted checkout token. Do not log it. |
url | string | Hosted checkout URL. |
expiresAt | string | Server-returned availability hint. Plan and vendor checks still apply. |
/api/vendor/checkout-links/{token}Read checkout details
Returns the vendor, plan, termsVersion, billingPeriod and expiresAt. PLAN_PAUSED means the business has disabled this plan. Keep the token and buyer details out of logs.
Parameters
| Name | Location | Requirement |
|---|---|---|
token | path | Required. Checkout token; do not log. |
Request template
Set the API base to include /api. Replace path placeholders before running.
curl --fail-with-body -X GET "$BEXO_API_BASE/vendor/checkout-links/{token}"Response fields
| Field | Type | Details |
|---|---|---|
vendor | object | See the downloadable schema for nested fields. |
plan | Plan | See the downloadable schema for nested fields. |
termsVersion | string | Version to pass at checkout. |
billingPeriod | string | Server mode. |
expiresAt | string | Returned availability hint. |
/api/vendor/checkoutStart or retry a purchase
Buyer session, not vendor-owner authority. A new purchase requires phone verification within the last hour and current terms acceptance. Keep the same Idempotency-Key for a retry of the same token. The server fixes the amount, currency, duration and plan name.
Parameters
| Name | Location | Requirement |
|---|---|---|
Idempotency-Key | header | Required. Stable key per buyer purchase attempt; do not change it after an uncertain response. |
Request template
Set the API base to include /api. Replace path placeholders and prepare request.json from the body fields below before running.
curl --fail-with-body -X POST "$BEXO_API_BASE/vendor/checkout" \
-H "Authorization: Bearer $BEXO_MEMBER_TOKEN" \
-H "Idempotency-Key: $BEXO_PURCHASE_ATTEMPT_ID" \
-H "Content-Type: application/json" \
--data @request.jsonRequest body
| Field | Type | Details |
|---|---|---|
checkoutTokenRequired | string | Token from a supported hosted checkout link. |
acceptedTermsRequired | boolean | Must be true for a new purchase.true |
acceptedTermsVersionRequired | string | Use the version returned by checkout-link retrieval. |
couponCode | string | Optional coupon code. |
Example request body
{
"checkoutToken": "<CHECKOUT_TOKEN>",
"acceptedTerms": true,
"acceptedTermsVersion": "<TERMS_VERSION_FROM_LINK>"
}Response fields
| Field | Type | Details |
|---|---|---|
membershipId | string | Original membership/purchase reference. |
orderId | string · nullable | Provider order reference. |
subscriptionId | string · nullable | Provider subscription reference, where enabled. |
keyId | string | Public provider checkout key, not the provider secret. |
amountPaise | integer | Amount in integer INR paise; 100 paise equals INR 1. |
currency | string | INR. |
durationDays | integer | Snapshotted duration. |
planName | string | Snapshotted name. |
billingPeriod | string | Server-returned billing mode. |
status | string | Current purchase state. |
pendingExpiresAt | string · nullable | Pending-purchase expiry, returned by resume. |
/api/vendor/checkout/{membershipId}/reconcileReconcile provider payment proof
Buyer must own the purchase. Supply the original provider callback proof. The server verifies the signature and capture facts; a client callback alone cannot activate access.
Parameters
| Name | Location | Requirement |
|---|---|---|
membershipId | path | Required. Resource identifier. |
Request template
Set the API base to include /api. Replace path placeholders and prepare request.json from the body fields below before running.
curl --fail-with-body -X POST "$BEXO_API_BASE/vendor/checkout/{membershipId}/reconcile" \
-H "Authorization: Bearer $BEXO_MEMBER_TOKEN" \
-H "Content-Type: application/json" \
--data @request.jsonRequest body
| Field | Type | Details |
|---|---|---|
razorpay_payment_idRequired | string | Provider payment ID. |
razorpay_order_id | string | Original provider order, for an order payment. |
razorpay_subscription_id | string | Original subscription, for a subscription payment. |
razorpay_signatureRequired | string | Provider signature from the supported checkout callback. |
Response fields
| Field | Type | Details |
|---|---|---|
membership | object | See the downloadable schema for nested fields. |
/api/vendor/memberships/{membershipId}/resumeResume a pending purchase
Buyer-owned pending purchases may be resumed for 30 minutes after creation. Reuses the original provider references. Returns 410 CHECKOUT_EXPIRED after the window.
Parameters
| Name | Location | Requirement |
|---|---|---|
membershipId | path | Required. Resource identifier. |
Request template
Set the API base to include /api. Replace path placeholders before running.
curl --fail-with-body -X POST "$BEXO_API_BASE/vendor/memberships/{membershipId}/resume" \
-H "Authorization: Bearer $BEXO_MEMBER_TOKEN"Response fields
| Field | Type | Details |
|---|---|---|
membershipId | string | Original membership/purchase reference. |
orderId | string · nullable | Provider order reference. |
subscriptionId | string · nullable | Provider subscription reference, where enabled. |
keyId | string | Public provider checkout key, not the provider secret. |
amountPaise | integer | Amount in integer INR paise; 100 paise equals INR 1. |
currency | string | INR. |
durationDays | integer | Snapshotted duration. |
planName | string | Snapshotted name. |
billingPeriod | string | Server-returned billing mode. |
status | string | Current purchase state. |
pendingExpiresAt | string · nullable | Pending-purchase expiry, returned by resume. |