Products and prices
Search, paginate, and retrieve the published offers shown to buyers.
A product describes what you sell. Its prices are the one-time, recurring, free, or custom-amount offers a buyer can select.
products.list(options)
Use this method for catalog grids, search, and pagination. Search runs on the server across the full catalog.
| Input | Type | Meaning |
|---|---|---|
status |
draft | published | archived | all |
Lifecycle filter. Buyer pages normally use published. |
query |
string |
Server-side name and description search. |
pageSize |
number |
Maximum items requested for this page. |
cursor |
string |
Opaque nextCursor from the previous page. |
// Ask Cipay for the first matching page of published products.
const page = await cipay.products.list({
status: "published",
query: "coffee",
pageSize: 20,
});
console.log({
count: page.items.length,
nextCursor: page.nextCursor,
});
{
"items": [
{
"id": "product_coffee",
"name": "Coffee membership",
"description": "Fresh beans delivered monthly.",
"lifecycle": "published",
"revision": 4,
"offers": []
}
],
"nextCursor": "opaque-next-page-or-null"
}
Continue pagination
Reuse a cursor only with the same query, status, and pageSize. Start again without a cursor when any filter changes.
// null means there are no more results.
const nextPage = page.nextCursor
? await cipay.products.list({
status: "published",
query: "coffee",
pageSize: 20,
cursor: page.nextCursor,
})
: null;
products.retrieve(id)
Use this method for a product detail screen or before selecting one of its attached offers. It returns one Product; an unknown or inaccessible ID returns a typed 404 error.
// Product IDs come from products.list or your trusted database mapping.
const product = await cipay.products.retrieve(productId);
console.log({
id: product.id,
name: product.name,
lifecycle: product.lifecycle,
offerCount: product.offers.length,
revision: product.revision,
});
{
"id": "product_coffee",
"name": "Coffee membership",
"description": "Fresh beans delivered monthly.",
"lifecycle": "published",
"revision": 4,
"offers": [
{
"id": "price_monthly",
"name": "Monthly",
"offerType": "recurring",
"status": "published",
"currency": "SAR",
"amountHalalas": 4500,
"cadence": { "intervalUnit": "month", "intervalCount": 1 }
}
]
}
prices.list(productId, options)
Use this method when you need independent price pagination or merchant tooling. For most buyer product cards, product.offers already contains the selectable offers.
| Input | Type | Meaning |
|---|---|---|
productId |
string |
Parent product ID. |
status |
draft | published | retired | all |
Price lifecycle filter. |
pageSize |
number |
Maximum prices requested. |
cursor |
string |
Opaque cursor from the previous response. |
// Only published prices should appear in buyer checkout choices.
const prices = await cipay.prices.list(productId, {
status: "published",
pageSize: 50,
});
const defaultPrice = prices.items.find((price) => price.isDefault);
console.log({ defaultPrice, nextCursor: prices.nextCursor });
{
"items": [
{
"id": "price_monthly",
"productId": "product_coffee",
"name": "Monthly",
"offerType": "recurring",
"status": "published",
"isDefault": true,
"revision": 2,
"currency": "SAR",
"amountHalalas": 4500,
"cadence": { "intervalUnit": "month", "intervalCount": 1 }
}
],
"nextCursor": null
}