Documentation Index
Fetch the complete documentation index at: https://docs.chipipay.com/llms.txt
Use this file to discover all available pages before exploring further.
@chipi-stack/chipi-react exposes the Bills flow as four React Query hooks. They wrap the same calls documented in the Node guide — same CreateSkuPurchaseParams shape, same Transaction response — but give you isLoading / isError / data state for free.
The React hooks call the public Chipi API through chipiSDK from ChipiProvider. They don’t sign transactions in the browser themselves — encryptKey and the encrypted private key flow through the SDK’s standard server-side gasless wallet pattern (see Gasless Wallets — overview).
Install
npm install @chipi-stack/chipi-react @tanstack/react-query
Wrap your app in ChipiProvider
ChipiProvider accepts your ChipiSDKConfig and an optional passkeyAdapter. It includes a built-in QueryClient, so you don’t need to mount one separately for Chipi hooks.
import { ChipiProvider } from "@chipi-stack/chipi-react";
export function App({ children }) {
return (
<ChipiProvider
config={{
apiPublicKey: process.env.NEXT_PUBLIC_CHIPI_PUBLIC_KEY!,
// baseUrl is only needed if you're targeting staging
baseUrl: process.env.NEXT_PUBLIC_CHIPI_BASE_URL,
}}
>
{children}
</ChipiProvider>
);
}
Browse the catalog — useGetSkuList
Returns the paginated catalog. Filter via category, chipiCategory (curated taxonomy: "RECARGAS", "GIFT_CARDS", "GENERAL", "GAMING", "TELEFONIA"), carrierName (case-insensitive substring), or provider.
import { useGetSkuList } from "@chipi-stack/chipi-react";
function TelcelOptions({ getBearerToken }) {
const { data, isLoading, isError } = useGetSkuList({
query: {
chipiCategory: "RECARGAS",
carrierName: "Telcel",
limit: 20,
},
getBearerToken,
});
if (isLoading) return <p>Loading…</p>;
if (isError) return <p>Failed to load catalog</p>;
return (
<ul>
{data.data.map((sku) => (
<li key={sku.id}>
{sku.name} — ${sku.fixedAmount} {sku.currency}
</li>
))}
</ul>
);
}
getBearerToken is a function that returns your auth provider’s user token (Clerk’s getToken(), BetterAuth’s session token, etc.). The hook is automatically disabled if getBearerToken isn’t provided — useful for rendering the catalog only after the user signs in.
data shape: PaginatedResponse<Sku> — { data: Sku[], total, page, limit, totalPages }.
Look up a single SKU — useGetSku
import { useGetSku } from "@chipi-stack/chipi-react";
function SkuDetail({ skuId, getBearerToken }) {
const { data: sku, isLoading } = useGetSku({ id: skuId, getBearerToken });
if (isLoading || !sku) return <p>Loading…</p>;
return <p>{sku.name} costs ${sku.fixedAmount} {sku.currency}</p>;
}
Charge the user’s wallet — usePurchaseSku
A mutation hook. Call purchaseSku() (fire-and-forget) or purchaseSkuAsync() (Promise) with a CreateSkuPurchaseParams object — same shape as the Node SDK.
import { usePurchaseSku } from "@chipi-stack/chipi-react";
import { Chain, ChainToken, Currency } from "@chipi-stack/types";
function BuyButton({ sku, userWallet, userPin, getBearerToken }) {
const { purchaseSkuAsync, isLoading, error } = usePurchaseSku();
const onBuy = async (userPhone: string) => {
const bearerToken = await getBearerToken();
if (!bearerToken) throw new Error("Sign in first");
const result = await purchaseSkuAsync({
params: {
skuId: sku.id,
skuReference: userPhone,
currencyAmount: sku.fixedAmount,
currency: Currency.MXN,
chain: Chain.STARKNET,
token: ChainToken.USDC,
encryptKey: userPin,
wallet: {
publicKey: userWallet.publicKey,
encryptedPrivateKey: userWallet.encryptedPrivateKey,
},
},
bearerToken,
});
console.log("Purchase id:", result.id);
};
return (
<button onClick={() => onBuy("5512345678")} disabled={isLoading}>
{isLoading ? "Charging…" : "Buy"}
</button>
);
}
The mutation returns a Transaction. The id is what you pass to useGetSkuPurchase to poll for settlement.
Poll for settlement — useGetSkuPurchase
import { useGetSkuPurchase } from "@chipi-stack/chipi-react";
function PurchaseStatus({ purchaseId, getBearerToken }) {
const { data: purchase } = useGetSkuPurchase({
id: purchaseId,
getBearerToken,
queryOptions: {
// React Query refetches while status is non-terminal
refetchInterval: (q) => {
const s = q.state.data?.status;
return s === "SUCCESS" || s === "FAILED" ? false : 2_500;
},
},
});
if (!purchase) return <p>Loading…</p>;
if (purchase.status === "SUCCESS") return <p>✅ Done</p>;
if (purchase.status === "FAILED") return <p>❌ Failed</p>;
return <p>{purchase.status}…</p>;
}
For production, prefer a webhook over polling — configure one at /configure/notifications in the dashboard.
Putting it together
A self-contained component: pick a Telcel SKU, charge the wallet, watch settlement.
import { useState } from "react";
import {
useGetSkuList,
usePurchaseSku,
useGetSkuPurchase,
} from "@chipi-stack/chipi-react";
import { Chain, ChainToken, Currency } from "@chipi-stack/types";
export function TelcelRecharge({ userWallet, userPin, getBearerToken }) {
const [purchaseId, setPurchaseId] = useState<string | null>(null);
const { data: list } = useGetSkuList({
query: { chipiCategory: "RECARGAS", carrierName: "Telcel", limit: 20 },
getBearerToken,
});
const { purchaseSkuAsync } = usePurchaseSku();
const { data: purchase } = useGetSkuPurchase({
id: purchaseId ?? "",
getBearerToken,
queryOptions: {
enabled: !!purchaseId,
refetchInterval: (q) => {
const s = q.state.data?.status;
return s === "SUCCESS" || s === "FAILED" ? false : 2_500;
},
},
});
const buy = async (sku, userPhone) => {
const bearerToken = await getBearerToken();
const result = await purchaseSkuAsync({
params: {
skuId: sku.id,
skuReference: userPhone,
currencyAmount: sku.fixedAmount,
currency: Currency.MXN,
chain: Chain.STARKNET,
token: ChainToken.USDC,
encryptKey: userPin,
wallet: userWallet,
},
bearerToken,
});
setPurchaseId(result.id);
};
return (
<div>
{list?.data.map((sku) => (
<button key={sku.id} onClick={() => buy(sku, "5512345678")}>
${sku.fixedAmount} {sku.currency}
</button>
))}
{purchase && <p>Status: {purchase.status}</p>}
</div>
);
}
What’s next
- Need server-side billing instead? See the Node guide.
- Need to filter by carrier? Use
carrierName — case-insensitive substring (e.g. "telcel" matches “Telcel” and “TELCEL POSPAGO”).
- Need credits to actually charge for purchases? See Billing & Credits.
✅ Verified on 2026-05-10.
Hooks: usePurchaseSku.ts, useGetSkuList.ts, useGetSkuPurchase.ts (all 666702b, 2026-03-12), and useGetSku.ts (90c7020, 2026-03-13).
Param shapes lifted from the matching unit tests in chipi-react/src/__tests__/ — 40d3966 for useGetSku/useGetSkuPurchase/usePurchaseSku, dd17914 for useGetSkuList.