Generate an invoice PDF at billing-cycle close with PHP
This recipe shows the composition step around the same endpoint documented on the main API page — read that first for the full field reference. It builds an invoice from your own subscription record and calls the API with plain PHP's curl extension — no framework or Composer package required.
What you'll need
- An InvoiceCraftly API key (see the API reference).
- PHP 8 or later with the
curlextension enabled (enabled by default in most installs). - Your own subscription/billing record — the example below reads name, address, and plan price from an array shaped like the row your billing table already stores.
Generate the invoice when a billing period closes
<?php
// Plain PHP, curl extension only — no Composer package required
function build_invoice(array $subscription): array {
return [
'type' => 'invoice',
'number' => $subscription['invoice_number'],
'issueDate' => $subscription['period_end'],
'currency' => $subscription['currency'],
'seller' => ['name' => 'Your SaaS Company', 'addressLines' => ['Your Company Address']],
'buyer' => ['name' => $subscription['customer_name'], 'addressLines' => [$subscription['customer_address']]],
'items' => [[
'description' => $subscription['plan_name'] . ' subscription',
'quantity' => 1,
'unitPrice' => $subscription['plan_price'],
'taxRate' => $subscription['tax_rate'] ?? 0,
]],
];
}
function generate_invoice_pdf(array $subscription): string {
$invoice = build_invoice($subscription);
$apiKey = getenv('INVOICECRAFTLY_API_KEY');
$ch = curl_init('https://invoicecraftly.com/api/v1/documents/pdf');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer {$apiKey}",
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($invoice),
CURLOPT_RETURNTRANSFER => true,
]);
$pdf = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) {
throw new RuntimeException("InvoiceCraftly render failed: HTTP {$status}");
}
if (!is_dir('invoices')) {
mkdir('invoices', 0755, true);
}
$path = "invoices/{$invoice['number']}.pdf";
file_put_contents($path, $pdf);
return $path;
}
A billing cycle should not silently skip its invoice
generate_invoice_pdf throws on any non-200 status rather than writing a corrupt file, so a failed render surfaces in your own billing job's error handling instead of leaving a customer without an invoice. Check the HTTP status against the error table — a 429 RATE_LIMITED at billing-run time usually means too many subscriptions renewed in the same batch; stagger the run or wait for the Retry-After delay.
Get an API key and try this recipe.
Free during the beta, 100 renders included per month.