Fix: invoices.downloadPdf() now returns PDF bytes instead of throwing
Fix: invoices.downloadPdf() now returns PDF bytes instead of throwing
Release: v1.0.92
Background
Calmony Pay's SDK exposes an invoices.downloadPdf(id) method intended to retrieve a generated PDF invoice by its ID. Internally, the server-side route GET /v1/invoices/:id/pdf serves the PDF as raw binary data with a Content-Type: application/pdf response header.
What was broken
Prior to v1.0.92, invoices.downloadPdf(id) was implemented by delegating to the SDK's shared request() helper:
// Old implementation (broken)
downloadPdf(id: string) {
return this.request<object>("GET", `/v1/invoices/${id}/pdf`);
}
The request() helper always calls response.json() to parse the server's reply. Because the /pdf route returns binary bytes — not JSON — this produced a runtime exception every time the method was called:
SyntaxError: Unexpected token '%', "\x25PDF-1."... is not valid JSON
The method was effectively unusable.
What changed
The invoices.downloadPdf(id) method now issues its HTTP request directly, skipping the shared request() helper entirely, and resolves to an ArrayBuffer containing the raw PDF bytes:
// New implementation (correct)
async downloadPdf(id: string): Promise<ArrayBuffer> {
const response = await fetch(`/v1/invoices/${id}/pdf`);
return response.arrayBuffer();
}
The server-side route GET /v1/invoices/:id/pdf is unchanged — it already returned the correct binary PDF content. Only the client SDK method needed fixing.
How to use it
Node.js — write PDF to disk
import { writeFile } from 'fs/promises';
import { calmonyPay } from '@/lib/calmony-pay/client';
const buffer = await calmonyPay.invoices.downloadPdf('inv_abc123');
await writeFile('invoice.pdf', Buffer.from(buffer));
Browser — trigger a download
import { calmonyPay } from '@/lib/calmony-pay/client';
const buffer = await calmonyPay.invoices.downloadPdf('inv_abc123');
const blob = new Blob([buffer], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'invoice.pdf';
a.click();
URL.revokeObjectURL(url);
Next.js API route — proxy PDF to a browser client
// app/api/invoice-download/route.ts
import { calmonyPay } from '@/lib/calmony-pay/client';
import { NextRequest, NextResponse } from 'next/server';
export async function GET(req: NextRequest) {
const id = req.nextUrl.searchParams.get('id')!;
const buffer = await calmonyPay.invoices.downloadPdf(id);
return new NextResponse(buffer, {
headers: {
'Content-Type': 'application/pdf',
'Content-Disposition': `attachment; filename="invoice-${id}.pdf"`,
},
});
}
Affected files
| File | Change |
|---|---|
src/lib/calmony-pay/client.ts | invoices.downloadPdf() bypasses request() helper; returns ArrayBuffer |
src/app/api/v1/invoices/[id]/pdf/route.ts | No change — already correct |