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

+30 -3
View File
@@ -13,9 +13,20 @@ use Illuminate\Database\Eloquent\Model;
* scope orders:client) - of one user ($user->appOrders()) or of all users
* (app(AnikeenId::class)->appOrders()). Nothing is requested until a method is called.
*
* Each order is an object with id, user_id, group, status (processing|pending|completed|cancelled),
* is_collective, net_total, vat_total, gross_total, currency_iso, created_at, paid_at,
* payment_attempts, next_payment_attempt_at and last_payment_error ({code, message} or null).
* Each order is an object with id, user_id, group, metadata, status
* (processing|pending|completed|cancelled), is_collective, net_total, vat_total, gross_total,
* currency_iso, created_at, paid_at, payment_attempts, next_payment_attempt_at,
* last_payment_error ({code, message} or null), and
*
* - items: all lines of the order, each {id, name, description, type, image_url, units, unit,
* price, vat_rate, subscription_id, meter, monthly_cap, discount, period_start, period_end,
* net_amount, vat_amount, gross_amount}. period_end is exclusive (the start of the next
* period); both are null for lines without a period. discount ({label, percent_off,
* amount_off}) marks the negative line of a subscription's discount, else null. The net amount
* a subscription was billed is the sum of net_amount of its lines.
* - invoice: null until the order is paid, then {id, number, type, status (pending while the PDF
* is being rendered, then completed), service_period_from, service_period_to, created_at,
* pdf_url}; see invoicePdf().
*/
class AppOrders
{
@@ -81,6 +92,22 @@ class AppOrders
return !$this->billable || $order->user_id === $this->userId() ? $order : null;
}
/**
* The PDF of an order's invoice (binary string, with the embedded e-invoice), the same as its
* `invoice.pdf_url` with the app's token.
*
* @throws ApiException 404 "order_not_found", 404 "invoice_not_found" (not paid yet),
* 409 "invoice_not_ready" (still being rendered, try again later)
*/
public function invoicePdf(string $orderId): string
{
if ($this->billable && !$this->find($orderId)) {
throw new ApiException('Order not found.', 404, 'order_not_found');
}
return (string) AppRequest::send('GET', sprintf('v1/client/orders/%s/invoice', $orderId))->response()?->getBody();
}
private function userId(): string
{
return (new AppSubscriptions($this->billable))->userId();
+6
View File
@@ -33,6 +33,12 @@ final class EventType
*/
public const SUBSCRIPTION_UPDATED = 'subscription.updated';
/**
* A new period of a running subscription was booked on its collective order; `data.period`
* describes it. Opt-in: only endpoints that select this event get it (not through `*`).
*/
public const SUBSCRIPTION_RENEWED = 'subscription.renewed';
/** The user confirmed a checkout session: all of its subscriptions are running (sent after their subscription.activated). */
public const CHECKOUT_COMPLETED = 'checkout.completed';