The Stripe invoice query maps payment intents to a portal type so no component ever imports from Stripe
The getClientInvoices function in lib/portal/stripe-queries.ts takes a stripeCustomerId string and returns an array of PortalInvoice objects — a seven-field interface defined in the same file. The function calls stripe.paymentIntents.list, filters to succeeded and requires_payment_method statuses, then maps each PaymentIntent into the portal shape. The description field falls through three options — metadata.productName first, then pi.description, then the literal string Payment. The date converts from a Unix timestamp by multiplying pi.created by one thousand and formatting with Intl.DateTimeFormat in en-GB locale. The status maps Stripe values to simpler labels: succeeded becomes succeeded, anything else becomes pending, both typed with as const so TypeScript narrows the union. The receiptUrl builds a Stripe dashboard link from the PaymentIntent ID rather than using Stripe's own receipt_url. If the Stripe API call fails, the function catches the error, logs it, and returns an empty array — the invoice section simply does not render rather than crashing the page. The InvoiceTable component that displays this data receives PortalInvoice and never imports anything from the Stripe SDK. The mapping function is the boundary. Every Stripe-specific field name, timestamp format, and status enum stays inside one forty-four-line file, and everything downstream works with a plain TypeScript interface that could be backed by any payment provider.
Comments coming soon
Sign in with TikTok to leave a comment. Coming soon.