lomi.

How do I use hosted checkout?

Create a checkout session, redirect the customer to lomi., then confirm the result with redirects, dashboard records, and webhooks.

Hosted checkout is the recommended first integration for most teams. Your server creates a session, your customer pays on a lomi.-hosted page, and your system confirms the result.

Good to know: Sessions expire after 60 minutes by default. Test keys run in sandbox mode. Cross-cutting rules (coupons, line items, payment state) live in Checkout behavior.

When to use hosted checkout

Use hosted checkout when you want:

  • A complete payment page with multiple payment methods.
  • Less payment UI and compliance work in your own app.
  • A clear success and cancel redirect flow.
  • Support for product-based or amount-based checkout.
  • A reliable path from sandbox testing to live payments.

Create a checkout session

Your server calls POST /checkout-sessions and redirects the customer to checkout_url.

API reference (full request schema): Create checkout session

Essential fields:

FieldNotes
currency_codeRequired, XOF, USD, or EUR
amountRequired unless you pass product_id or line_items
product_id / price_idCatalog-backed checkout; amount derived from the price
success_url / cancel_urlWhere to send the customer after pay or cancel
customer_emailOptional, pre-fills the hosted form
require_name / require_email / require_phone / require_billing_addressOptional, control checkout form fields; see Checkout form fields

Pass metadata for your own order IDs. See the API page for every optional field.

Pay what you want products

When product_id or price_id points to a price with pricing_model: pay_what_you_want:

  • Omit amount: the session uses the suggested unit price (amount on the price row) × quantity.
  • Pass amount: must be a valid subtotal: unit price within [minimum_amount, maximum_amount] × quantity. The server rejects out-of-range values.
  • Hosted checkout: the buyer can change the unit price before paying; validation runs client-side and server-side.
  • Programmatic flows: set amount at session creation when you already know the chosen total.

Multi-item checkout (line_items)

When you pass line_items, each row must reference a one-time price with standard (fixed) pricing. The API returns 400 with one of:

CodeMeaning
line_items_pwyw_not_supportedA line uses pay_what_you_want pricing, use a single-product session instead
line_items_recurring_not_supportedA line references a recurring product
line_items_usage_based_not_supportedUsage-based pricing (not yet supported in carts)
line_items_mixed_product_typesMixed one-time and recurring products in one cart

Recurring (subscription) products

When product_id refers to a recurring product:

  • Omit amount: the session uses the catalog price for the selected price_id (quantity is not multiplied for subscriptions).
  • Pass amount: must match the catalog recurring price for validation (unless using prorated first charge at payment time; see Checkout behavior: Subscription checkout).
  • Trial or non_initial signup: the customer may pay $0 at checkout, but the session record often still stores the catalog price so create_checkout_session satisfies amount > 0. Do not pass amount: 0 unless you omit it and rely on product-derived pricing.
  • Completing checkout creates a subscription (SUBSCRIPTION_CREATED webhook), not a one-time payment transaction only.

Example:

const subscriptionSession = await lomi.checkoutSessions.create({
  product_id: 'prod_recurring_abc...',
  price_id: 'price_monthly_xyz...',
  currency_code: 'XOF',
  customer_email: 'customer@example.com',
  success_url: 'https://your-site.com/welcome',
  allow_coupon_code: true,
});
import { LomiSDK } from '@lomi./sdk';

const lomi = new LomiSDK({
  apiKey: process.env.LOMI_SECRET_KEY!,
  environment: 'live',
});

// Simple checkout with amount
const session = await lomi.checkoutSessions.create({
  amount: 10000,
  currency_code: 'XOF',
  title: 'Order #12345',
  description: 'Payment for items in cart',
  customer_email: 'customer@example.com',
  success_url: 'https://your-site.com/success',
  cancel_url: 'https://your-site.com/cancel',
  metadata: {
    order_id: 'ORD-12345',
  },
});

// Product-based checkout
const productSession = await lomi.checkoutSessions.create({
  product_id: 'prod_abc123...',
  currency_code: 'XOF',
  quantity: 2,
  allow_coupon_code: true,
  success_url: 'https://your-site.com/success',
});

// PWYW product with custom amount and quantity
const pwywSession = await lomi.checkoutSessions.create({
  product_id: 'prod_tip_jar...',
  currency_code: 'XOF',
  amount: 1500,
  quantity: 2,
  success_url: 'https://your-site.com/success',
});

console.log(`Redirect to: ${session.checkout_url}`);
from lomi import LomiClient
import os

client = LomiClient(
    api_key=os.environ["LOMI_SECRET_KEY"],
    environment="test"
)

# Simple checkout with amount
session = client.checkout_sessions.create({
    "amount": 10000,
    "currency_code": "XOF",
    "title": "Order #12345",
    "description": "Payment for items in cart",
    "customer_email": "customer@example.com",
    "success_url": "https://your-site.com/success",
    "cancel_url": "https://your-site.com/cancel",
    "metadata": {
        "order_id": "ORD-12345"
    }
})

# PWYW product with custom amount and quantity
pwyw_session = client.checkout_sessions.create({
    "product_id": "prod_tip_jar...",
    "currency_code": "XOF",
    "amount": 1500,
    "quantity": 2,
    "success_url": "https://your-site.com/success"
})

print(f"Redirect to: {session['checkout_url']}")
curl -X POST "https://api.lomi.africa/checkout-sessions" \
  -H "X-API-KEY: $LOMI_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 10000,
    "currency_code": "XOF",
    "title": "Order #12345",
    "description": "Payment for items in cart",
    "customer_email": "customer@example.com",
    "success_url": "https://your-site.com/success",
    "cancel_url": "https://your-site.com/cancel",
    "metadata": {
      "order_id": "ORD-12345"
    }
  }'

Response

{
  "id": "cs_abc123...",
  "checkout_url": "https://checkout.lomi.africa/cs_abc123...",
  "status": "open",
  "amount": 10000,
  "currency_code": "XOF",
  "title": "Order #12345",
  "customer_email": "customer@example.com",
  "expires_at": "2024-01-15T11:30:00Z",
  "created_at": "2024-01-15T10:30:00Z"
}

Embed instead of redirect

To keep the customer on your page, use the embed SDK with the same checkout_url:

import { loadLomiCheckout } from '@lomi./embed';

loadLomiCheckout({
  checkoutUrl: session.checkout_url,
  mode: 'modal',
  onComplete: (payload) => console.log(payload.transactionId),
});

See Embed checkout widget for modal, inline, and self-hosted setup.

Confirm the payment

Use redirects for customer experience, not final reconciliation. Your server should rely on webhooks or a server-side API read before fulfilling an order.

Confirm:

  • The checkout session status.
  • The related transaction status.
  • The amount, currency, customer, and metadata.
  • The webhook event signature and event ID.

Common variants

VariantUse when
Fixed amountYour backend already knows the amount
Product checkoutThe amount should come from a product or price
Subscription checkoutThe customer is starting a recurring plan
Coupon-enabled checkoutYou allow discount codes
Test checkoutYou are validating with lomi_sk_test_...

List and retrieve sessions

Use the API reference to list or fetch sessions by ID:

Webhooks

Merchant webhook subscriptions (lomi. calls your URL) use the webhook_event enum documented in Webhooks. The HTTP body field `event` and the `X-Lomi-Event` header use those values (for example `PAYMENT_SUCCEEDED`).

For checkout outcomes on your server, subscribe at minimum to:

authorized_events valueWhen it fires
`PAYMENT_SUCCEEDED`Payment completed; payload `data` follows the transaction shape (see Webhooks).
`PAYMENT_FAILED`Payment attempt failed (including some provider failure paths).

There is no separate `checkout.session.*` event in the `webhook_event` enum. Session expiry without a successful charge is reflected on the session and transaction records (e.g. status `expired`); use `GET /checkout-sessions/{id}` or list with `status=expired` / `completed` rather than expecting a merchant webhook named like checkout.session.expired.


On this page