---
title: "Where the charge behind the caller's latest invoice stands, and why."
method: GET
path: "/api/v1/billing/payment-issue"
tags: ["Billing"]
---

# Where the charge behind the caller's latest invoice stands, and why.

`GET /api/v1/billing/payment-issue`

## Request

- Base URL: `https://api.avatarfactory.io` (the document declares no server; this is the origin it was published from)
- URL: `https://api.avatarfactory.io/api/v1/billing/payment-issue`
- Auth: one of:
  - API key in cookie `session`
  - HTTP bearer

<br/>Split from ``GET /subscription`` on purpose: that route runs on every page<br/>load and must stay free of provider calls, while this one costs up to three<br/>Stripe reads and is only called by the paywall while a charge the user just<br/>started has not unlocked anything yet.<br/><br/>Always 200. A user looking at a paywall gets a worse outcome from a 500 than<br/>from ``state: "unknown"``, and guessing either way is harmful: ``settled``<br/>strands them, ``failed`` tells them to pay twice (weekly review 2026-09-28,<br/>item 1: two users looped on the paywall with no message at all, one of them<br/>for 13.6 minutes, the other forever).

## Response `200`

OK

- PaymentIssueResponse: Why the charge behind the caller's latest invoice has not unlocked anything. ``state`` is ``none`` (nothing billed), ``settled``, ``pending``, ``failed`` or ``unknown``. The decline fields are Stripe's own message and code, passed through verbatim, and are null in every state but ``failed``.
  - `decline_code` string, nullable
  - `decline_reason` string, nullable
  - `hosted_invoice_url` string, nullable
  - `invoice_id` string, nullable
  - `state` string, required

## Other responses

- `422`: Unprocessable Entity

---

[API](https://skmtc.dev/avatarfactory/apis/avatarfactory-api.md) · [All operations](https://skmtc.dev/avatarfactory/apis/avatarfactory-api/llms.txt) · [OpenAPI document](https://skmtc.dev/avatarfactory/apis/avatarfactory-api/revisions/66fc7aa3f296?raw)
