Read order lines and invoices of app orders

Document the items (subscription, period, net amount, discount), invoice
and metadata of orders read with the app's token, add
AppOrders::invoicePdf() for the invoice PDF and the opt-in
subscription.renewed event type with its period data.
This commit is contained in:
Maurice Preuß committed 2026-11-04 21:25:56 +01:00
1 parent db3c50af72
commit bd56f114da
6 files changed
+188 -3

No files matched your search

+31
View File
@@ -469,6 +469,14 @@ arrive more than once or out of order: use `$event['id']` to skip duplicates, an
object through the API when the current state matters. The admin panel can send a
`webhook.test` event to check your endpoint.
`subscription.renewed` (`EventType::SUBSCRIPTION_RENEWED`) is sent for every period after the
first that was booked on the user's monthly order, with `$event['data']['period']`:
`{subscription_id, period_start, period_end, periods, unit, order_id, order_item_id, items,
currency_iso, net_amount}` (`net_amount` after a discount). Hourly subscriptions renew every hour,
so this event is **opt-in**: select it explicitly for your endpoint, `*` doesn't include it.
`order.*` events list the order's `items` and `invoice` like
[app orders](#app-orders-trusted-apps-no-user-token).
### Errors
Every failed request throws `Anikeen\Id\Exceptions\ApiException`, with the user's token as
@@ -630,6 +638,29 @@ Each order has `status` (`processing` = current month, still collecting; `pendin
paid; `completed`; `cancelled`), the totals, `payment_attempts`, `next_payment_attempt_at` (null =
no more automatic retries) and `last_payment_error`.
Orders also list their lines and their invoice, e.g. to show a team's invoices or to attribute
amounts (commissions) per subscription:
```php
$order = app(AnikeenId::class)->appOrders()->find($orderId);
foreach ($order->items as $item) {
$item->subscription_id; // null for lines that bill no subscription
$item->period_start; $item->period_end; // ISO 8601, the end is exclusive; null without a period
$item->net_amount; // units × price, rounded to cents
$item->discount; // {label, percent_off, amount_off} on a discount line, else null
}
$order->invoice; // null until paid, then {id, number, status, …, pdf_url}
$pdf = app(AnikeenId::class)->appOrders()->invoicePdf($orderId); // the invoice PDF (binary)
```
The net amount actually billed for a subscription is the sum of `net_amount` of its lines
(discount lines are negative). `invoicePdf()` throws `ApiException` with 404 `invoice_not_found`
before the order is paid and 409 `invoice_not_ready` while the PDF is still being rendered
(usually seconds after the payment; try again later). A collective order can also bill
subscriptions of other apps of the same user and group.
### Checkout sessions (the user confirms several subscriptions at once)
Instead of checking subscriptions out itself, an app can let the user confirm them on Anikeen ID,