Receipts

A receipt is the immutable record of a successful payment — issued automatically when a checkout session completes or an invoice is paid. Receipts have their own ID, their own URL, and their own template (controlled via the Templates namespace); they are decoupled from the underlying session or invoice so they survive even if the original is later voided. This page covers the plugipay.receipts namespace; for the underlying HTTP surface see API: Receipts, and for how receipts relate to other objects see Concepts → Receipt.

Namespace

plugipay.receipts — every method on this namespace:

plugipay.receipts.list(params?)
plugipay.receipts.get(id)

Receipts are created automatically by Plugipay on successful payment — you don't create them via the SDK. To re-issue / customize the rendered output, edit the receipt template and the change applies to subsequent receipts.

Methods

receipts.list

Signature. plugipay.receipts.list(params?): Promise<{ data: ReceiptSummary[]; cursor: string | null; hasMore: boolean }>

Cursor-paginated list of receipts. Filters: sourceType ('checkout_session' or 'invoice'), customerId, issuedAfter, issuedBefore (ISO-8601 strings), limit, cursor. See Pagination.

import { PlugipayClient } from '@forjio/plugipay-node';

const plugipay = new PlugipayClient({
  keyId: process.env.PLUGIPAY_KEY_ID!,
  secret: process.env.PLUGIPAY_SECRET!,
});

// All receipts for one customer:
const { data: customerReceipts } = await plugipay.receipts.list({
  customerId: 'cus_01HX...',
  limit: 50,
});

// All checkout receipts in the last 30 days:
const since = new Date(Date.now() - 30 * 86_400_000).toISOString();
const { data: recent } = await plugipay.receipts.list({
  sourceType: 'checkout_session',
  issuedAfter: since,
  limit: 100,
});

The list returns ReceiptSummary objects — lightweight versions with the headline fields. To render or download the full document, fetch by ID.

receipts.get

Signature. plugipay.receipts.get(id): Promise<unknown>

Fetches the full receipt document — the typed return is unknown because the document shape depends on the underlying receipt template (which is workspace-configurable JSON). Cast or validate as needed.

type FullReceipt = ReceiptSummary & {
  document: Record<string, unknown>;
  hostedUrl: string;
  pdfUrl: string;
};

const receipt = (await plugipay.receipts.get('rcp_01HX...')) as FullReceipt;
console.log(receipt.hostedUrl);  // browser-friendly receipt page
console.log(receipt.pdfUrl);     // direct PDF link

Types

interface ReceiptSummary {
  id: string;                                       // 'rcp_...'
  number: string;                                   // human-readable, e.g. 'RCP-2026-00118'
  sourceType: 'checkout_session' | 'invoice';
  sourceId: string;                                 // 'cs_...' or 'inv_...'
  customerId: string | null;
  amount: number;                                   // minor units
  currency: string;
  method: string | null;                            // 'qris' / 'card' / etc.
  adapter: string | null;                           // 'xendit' / 'midtrans' / ...
  issuedAt: string;                                 // ISO-8601
  emailedAt: string | null;
  emailedTo: string | null;
}

For the full document shape (which depends on the active receipt template), see API: Receipts and API: Templates.

Common patterns

Show a customer their receipt history

The customer-portal "Receipts" tab is just a filtered list:

async function customerReceipts(customerId: string) {
  const results: ReceiptSummary[] = [];
  let cursor: string | undefined;
  while (true) {
    const page = await plugipay.receipts.list({
      customerId,
      limit: 50,
      cursor,
    });
    results.push(...page.data);
    if (!page.hasMore) break;
    cursor = page.cursor!;
  }
  return results;
}

If you'd rather not build the UI yourself, mint a portal session and let the customer view receipts in the Plugipay-hosted portal.

Download a receipt PDF

async function downloadReceiptPdf(receiptId: string): Promise<Buffer> {
  const r = (await plugipay.receipts.get(receiptId)) as { pdfUrl: string };
  const res = await fetch(r.pdfUrl);
  return Buffer.from(await res.arrayBuffer());
}

const pdf = await downloadReceiptPdf('rcp_01HX...');
await fs.writeFile('receipt.pdf', pdf);

Reconcile receipts to ledger transactions

Every receipt corresponds to ledger entries posted at the moment of completion. To verify a receipt is accounted-for:

async function ledgerForReceipt(receiptId: string) {
  const receipt = await plugipay.receipts.get(receiptId) as ReceiptSummary & { document: any };
  // Receipts are derived from their source — query the ledger by the source ID:
  return plugipay.ledger.list({
    sourceType: receipt.sourceType,
    sourceId: receipt.sourceId,
  });
}

Find receipts sent to a specific address

The emailedTo field captures the address Plugipay sent each receipt to (it may differ from the customer's primary email if you overrode it). For "did we email finance@ this month's receipts?" audits:

async function receiptsEmailedTo(addr: string, from: string, to: string) {
  const all: ReceiptSummary[] = [];
  let cursor: string | undefined;
  while (true) {
    const page = await plugipay.receipts.list({
      issuedAfter: from,
      issuedBefore: to,
      limit: 100,
      cursor,
    });
    all.push(...page.data.filter((r) => r.emailedTo === addr));
    if (!page.hasMore) break;
    cursor = page.cursor!;
  }
  return all;
}

Build a monthly receipt export

For accounting hand-off:

async function* receiptsForMonth(year: number, month: number) {
  const start = new Date(Date.UTC(year, month - 1, 1)).toISOString();
  const end = new Date(Date.UTC(year, month, 1)).toISOString();
  let cursor: string | undefined;
  while (true) {
    const page = await plugipay.receipts.list({
      issuedAfter: start,
      issuedBefore: end,
      limit: 100,
      cursor,
    });
    for (const r of page.data) yield r;
    if (!page.hasMore) break;
    cursor = page.cursor!;
  }
}

const rows: string[] = ['number,customer,amount,currency,adapter,issuedAt'];
for await (const r of receiptsForMonth(2026, 11)) {
  rows.push([r.number, r.customerId ?? '', r.amount, r.currency, r.adapter ?? '', r.issuedAt].join(','));
}
await fs.writeFile('receipts-2026-11.csv', rows.join('\n'));

Errors

Code Status Cause
validation_error 400 Bad date format in issuedAfter / issuedBefore, unknown sourceType.
not_found 404 Receipt ID doesn't exist or is in another workspace.
forbidden 403 Key lacks the plugipay:receipt:read scope.

Next

Plugipay — Payments that don't tax your success