diff --git a/README.md b/README.md index 044cd69..97d973e 100644 --- a/README.md +++ b/README.md @@ -716,6 +716,10 @@ 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`. +Collective (monthly) orders are kept **per app**: your subscriptions are never billed on the same +order as another app's, even if both use the same `group` (or none); clients delegated to your app +bill on your app's orders. `$order->oauth_client_id` is the app that bills the order. + Orders also list their lines and their invoice, e.g. to show a team's invoices or to attribute amounts (commissions) per subscription: @@ -736,8 +740,10 @@ $pdf = app(AnikeenId::class)->appOrders()->invoicePdf($orderId); // the invoice 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. +(usually seconds after the payment; try again later). Legacy orders from before collective orders +were kept per app (`oauth_client_id` null) can also bill other apps' subscriptions: there `items` +lists only your lines (the totals are the whole order's), `invoice->pdf_url` is null and +`invoicePdf()` throws `ApiException` with 403 `invoice_includes_other_apps`. ### Billing portal links (trusted apps) diff --git a/README.stub b/README.stub index e9e2303..25c8710 100644 --- a/README.stub +++ b/README.stub @@ -716,6 +716,10 @@ 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`. +Collective (monthly) orders are kept **per app**: your subscriptions are never billed on the same +order as another app's, even if both use the same `group` (or none); clients delegated to your app +bill on your app's orders. `$order->oauth_client_id` is the app that bills the order. + Orders also list their lines and their invoice, e.g. to show a team's invoices or to attribute amounts (commissions) per subscription: @@ -736,8 +740,10 @@ $pdf = app(AnikeenId::class)->appOrders()->invoicePdf($orderId); // the invoice 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. +(usually seconds after the payment; try again later). Legacy orders from before collective orders +were kept per app (`oauth_client_id` null) can also bill other apps' subscriptions: there `items` +lists only your lines (the totals are the whole order's), `invoice->pdf_url` is null and +`invoicePdf()` throws `ApiException` with 403 `invoice_includes_other_apps`. ### Billing portal links (trusted apps) diff --git a/src/Id/Resources/AppOrders.php b/src/Id/Resources/AppOrders.php index 9d3b1ae..41750bb 100644 --- a/src/Id/Resources/AppOrders.php +++ b/src/Id/Resources/AppOrders.php @@ -13,12 +13,14 @@ 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, 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 + * Each order is an object with id, user_id, oauth_client_id (the app that bills it: collective + * orders are kept per app, even for the same group; null on legacy orders that billed several + * apps), 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, + * - items: the order's lines of the app's subscriptions (all lines, except on legacy orders that + * also billed other apps), 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, diff --git a/src/Id/Resources/Order.php b/src/Id/Resources/Order.php index 9602a7f..6996874 100644 --- a/src/Id/Resources/Order.php +++ b/src/Id/Resources/Order.php @@ -9,6 +9,9 @@ use Throwable; /** * @property string $id * @property null|string $confirmation_url where the user confirms it (third-party apps, or `confirm`) + * @property null|string $oauth_client_id the app that bills the order: the app that created it, or for a monthly + * (collective) order the app whose subscriptions it bills; collective orders + * are kept per app. null for Anikeen's own (and legacy orders of several apps) * @property null|string $group * @property null|object $metadata the app's own data, returned as is * @property null|string $return_url where the user returns after confirming