Docs/Getting started/Quickstart
Quickstart
Create your first Add to wallet checkout using your assigned issuer wallet.
1. Prepare your account#
- Sign in to Business and create an API key in Integrations. Store it only on your backend.
- Enable API signing for that key. Allow your chosen bytes32 loyalty ID, set a per-operation amount limit, and choose a daily signing limit. Authorize this with your assigned issuer wallet.
- Configure a webhook endpoint for issuance and redemption completion. Save its signing secret securely. Follow Receipts and webhooks for verification and deduplication.
- Use an intended customer wallet. On Base, the customer needs USD for the action and network fees. Mainnet checkouts spend real funds; use local fixtures for automated failure testing.
This path needs no external issuer wallet or brand-managed private key. Your assigned wallet signs after API signing is authorized. The customer still authorizes and pays for checkout.
2. Reserve points and create checkout#
Authenticate the customer in your app. Atomically reserve one eligible point in your ledger and store a conversion row. Download api-quickstart.mjs beside the following Node.js script. Use Node.js 20 or later. Set the environment variables with your API key, allowed loyalty ID, customer wallet, and existing program name and symbol. The example uses a non-expiring bucket; use it only if your program allows that.
import { readFile, writeFile } from "node:fs/promises";
import { prepareIssuance, createCheckout } from "./api-quickstart.mjs";
// One durable file per already-reserved conversion for this local example.
// Production: store the payload in the same database transaction as the reservation.
const file = process.argv[2];
if (!file) throw new Error("Pass a private payload file for this reservation");
let payload;
try { payload = JSON.parse(await readFile(file, "utf8")); }
catch (error) {
if (error.code !== "ENOENT") throw error;
payload = prepareIssuance({
factory: "0xd86053cfdd87A1F45058a888D7514898c9694853",
loyaltyId: process.env.LOYFIN_LOYALTY_ID,
customer: process.env.LOYFIN_CUSTOMER,
name: process.env.LOYFIN_PROGRAM_NAME,
symbol: process.env.LOYFIN_PROGRAM_SYMBOL,
});
// Exclusive creation prevents concurrent runs from replacing the saved payload.
await writeFile(file, JSON.stringify(payload), { flag: "wx", mode: 0o600 });
}
const result = await createCheckout({
apiUrl: process.env.LOYFIN_API_URL || "https://api.loyfin.com",
apiKey: process.env.LOYFIN_API_KEY,
payload,
});
// Private local output: contains the checkout bearer URL. Do not send to analytics.
await writeFile(file + ".session.json", JSON.stringify(result), { mode: 0o600 });
console.log("Session saved. Share its checkoutUrl only with the intended customer.");node create-checkout.mjs ./conversion-001.jsonThe payload omits both issuer and signature. Never send null or empty substitutes. Run the same command with the same file to retry an interrupted request; do not regenerate its nonce, operationHash, or deadline. Keep both files out of source control. An expired request requires reconciliation before a replacement.
3. Complete checkout#
Save issuance.id and issuance.operationId with the conversion. Open issuance.checkoutUrl from the private session response in the customer journey. The customer authorizes the intended wallet, reviews the current USD fee, and submits. The first successful issuance creates the program for this issuer and loyalty ID.
4. Confirm the conversion#
Verify the webhook signature over the raw request body. For a finalized loyfin.issued event, match the saved operationHash, issuer, chain, wallet, amount, and expiry bucket. In one database transaction, record the event idempotency key and mark the reserved conversion completed. A successful POST or a checkout redirect is not proof of completion.
If delivery is delayed, reconcile the completed receipt using the polling fallback. Handle duplicate and reversal events. Next, implement Add from wallet to restore offchain points only after a finalized burn.