Billing over WebSocket
Renting phones costs money. These four funs read your rental and hand back links for a person to open: a Stripe Checkout page, or an approval page. Both are inert until someone acts on them.
All four need the billing ability. See Frames for the request and reply
envelope, and Events for the billing_request push.
| Fun | Data fields | Reply |
|---|---|---|
/app/billing/summary | (none) | 200 with the rental summary |
/app/billing/rentals | phones + country, or items, plus accept_terms | 201 with a checkout link |
/app/billing/requests | phones or add, plus accept_terms | 201 applied, or 202 pending approval |
/app/billing/requests/get | request_id | 200 with the request's status |
accept_terms is required
Both funs that can spend money require accept_terms: true. Before you send it, show the
person terms and privacy and get their
agreement, including to a monthly renewal charge for each assigned phone.
Leaving it out, or sending false, is refused with 422 and
Show the user https://0bull.net/terms and https://0bull.net/privacy, get their agreement, and pass accept_terms: true.
Read the rental
/app/billing/summary takes no fields.
Code
| Field | Meaning |
|---|---|
subscribed | Whether any rental is active. |
phones | How many phones the rental covers. |
price_per_phone | List price for one phone a month. |
monthly | Total a month. Present once there is a rental. |
on_grace_period | Whether any phone is cancelling but still running. Present once there is a rental. |
pending_orders | Orders waiting for an admin to assign phones. Nothing has been charged for these. |
lines | One entry per phone. Present once there is a rental. |
hint | A short next step. Present only while there is no rental. |
Every assigned phone runs its own monthly cycle, from its own assignment date. That is why
lines carries one entry per phone, each with its own status and renews_at. Adding a phone
never moves an existing renewal date, and nothing is prorated.
Start here before proposing any change.
Start a rental
/app/billing/rentals opens a Stripe Checkout page for a first subscription.
| Field | Type | Description | |
|---|---|---|---|
phones | integer | one of | Phones for one country, 1 to 50. Give this or items. |
country | string | optional | ISO 3166-1 alpha-2 country for phones, such as ES, US or JP. Defaults to ES. |
items | array | one of | Rows of {country, quantity} for a multi-country order. 1 to 10 rows, at most 50 phones in total, each country once. |
accept_terms | boolean | required | Must be true. |
The reply is 201 with checkout_url, the total phones, the normalized items, and a
short next describing what the link does.
What the checkout link does. Opening it saves the card and holds one full month per phone on it. The order is placed once checkout finishes. The held amount is charged when an admin assigns each phone, and each assigned phone then runs its own monthly cycle from that date. Nothing is charged at order time, and a card that cannot cover the hold places no order.
Refusals:
You already have an active rental subscription. Change the phone count instead.(409)That is more than 50 phones in total. Split it into separate orders.(409)Each country can only appear once. Put the whole amount for a country on one line.(409)Could not start checkout. Please try again.(502) when Stripe cannot be reached.
Change the phone count
/app/billing/requests changes how many phones the subscription covers.
| Field | Type | Description | |
|---|---|---|---|
phones | integer | one of | The total the subscription should cover afterwards, 1 to 50. |
add | integer | one of | Signed change to the current count, from -49 to 49. add: 1 adds one, add: -2 removes two. |
accept_terms | boolean | required | Must be true. |
Give one of phones or add, not both.
Read applied in the reply to know what happened:
applied | Status | What happened | What you get |
|---|---|---|---|
false | 202 | Filed, waiting on the account owner. | request_id, approval_url, expires_at and an estimated charge. Nothing has been charged. |
true | 201 | Placed under a standing allowance the owner set themselves. | request_id, from_phones, to_phones. The card is charged when each phone is assigned. |
A standing allowance only ever covers an increase that lands at or below it. An increase past it, and every decrease, still asks. When a bank needs to confirm the card, the reply comes back pending instead, with an approval link that leads to the confirmation.
A request expires in an hour and can be answered once.
Adding phones costs full list price per phone, held now and charged on assignment. Removing phones takes effect at each removed phone's own renewal, with no credit now.
Refusals:
No active rental subscription. Start a rental before changing the phone count.(409)The subscription already covers 3 phones.(409) when the change is a no-op.That would leave the subscription at 0 phones. It has to end up between 1 and 50.(409)
Check a filed request
/app/billing/requests/get reads what the owner decided.
| Field | Type | Description | |
|---|---|---|---|
request_id | string | required | The request_id from /app/billing/requests. |
The reply carries request_id, status, to_phones, approval_url, expires_at and
resolved_at. status is one of pending, approved, declined, failed (the card was
refused, nothing was charged) or expired.
A request belonging to anyone else answers 404 with Billing request not found.

