Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ From your backend: `norbix.hub.code.get_code_functions(integrationId=...)`,
`get_code_function_qualifiers(...)`, `invoke_code_function(functionRef=..., payload=..., invocationType="Sync")`,
`get_code_function_event_sample(source="database")` and
`get_code_function_runs(triggerId=...)`. Details and the typed views
(`as_user_change`, `as_file_change`, `as_payment`, `as_scheduled_task`, `payload()`):
(`as_user_change`, `as_file_change`, `as_payment`, `as_payment_order`, `as_scheduled_task`, `payload()`):
[docs/hub/code.md](./docs/hub/code.md).

## Errors
Expand Down
1 change: 1 addition & 0 deletions docs/api/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
| [`echo`](./echo.md) | 1 |
| [`files`](./files.md) | 10 |
| [`membership`](./membership.md) | 18 |
| [`payments`](./payments.md) | 4 |
| [`public`](./public.md) | 2 |
49 changes: 49 additions & 0 deletions docs/api/payments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# API · Payments

Access with `norbix.api.payments`.

| Method | Verb | Path | Scope |
| --- | --- | --- | --- |
| `create_payment_order` | `POST` | `/{version}/payments/orders` | `project` |
| `get_payment_order` | `GET` | `/{version}/payments/orders/{Id}` | `project` |
| `get_payment_orders` | `GET` | `/{version}/payments/orders` | `project` |
| `start_payment_checkout` | `POST` | `/{version}/payments/orders/{Id}/checkout` | `project` |

A payment order is what a buyer pays for: lines priced in minor units (cents),
a merchant reference and a price version. The order total is the sum of the
lines; Norbix computes it.

## Taking a payment

1. `create_payment_order` stores the order and gives back its id (`ord_…`).
Send an `idempotencyKey` (8 characters or more): the same body with the
same key gives back the same id, so a retry never makes a second order.
2. `start_payment_checkout` starts the provider checkout (Montonio, …) with a
payments integration of the project. Send the buyer to
`checkout["paymentUrl"]`. While a checkout is open, the call hands it back
with `reused` = `True`.
3. When the provider confirms the payment, the order becomes `Paid` and a
webhook destination or a Code function hears `payments.order.paid` (see
[webhooks-receiver.md](../webhooks-receiver.md) and
`FunctionEvent.as_payment_order()`).

```python
created = client.api.payments.create_payment_order(
buyerUserId=user_id,
merchantReference="ad-1042-promo-1",
idempotencyKey="ad-1042-promo-1",
currency="EUR",
priceVersion="2026-10-01",
lines=[{"sku": "star-day", "title": "Star × day", "quantity": 21, "unitAmountMinor": 30}],
metadata={"adId": "ad_1042"},
)
started = client.api.payments.start_payment_checkout(
created["id"],
integrationId=montonio_integration_id,
returnUrl="https://shop.example.com/orders/return",
)
redirect_to = started["checkout"]["paymentUrl"]

order = client.api.payments.get_payment_order(created["id"])["item"]
paid = client.api.payments.get_payment_orders(buyerUserId=user_id, status="Paid")["list"]["items"]
```
3 changes: 2 additions & 1 deletion docs/hub/code.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ def handler(raw, _context): # AWS Lambda; Azure / Google p
| `as_database_record_change()` | `collection`, `schema_id`, `schema_version`, `record_id`, `old`, `new`, `truncated`, `old_ref` / `new_ref` (over 200 KB the documents are left out and the refs say where to fetch them) |
| `as_user_change()` | `user_id`, `old`, `new` |
| `as_file_change()` | `old` (delete), `new` (upload), `path` |
| `as_payment()` | `integration_id`, `provider`, `provider_event_id`, `event_name`, `status`, `amount_minor`, `currency`, `external_ids`, `occurred_on`, `received_on` |
| `as_payment()` | `integration_id`, `provider`, `provider_event_id`, `event_name`, `order_id`, `status`, `amount_minor`, `currency`, `external_ids`, `occurred_on`, `received_on`; on `payments.order.*` it is the callback that changed the order |
| `as_payment_order()` | `payments.order.*` (`data.order`): `order_event_id` (the de-duplication key, the same on every retry), `event`, `from_`, `to`, `reason`, `order` (`id`, `status`, `lines`, `total_minor`, `settled_minor`, `refunded_minor`, `metadata`, `checkouts`, …), `payment` |
| `as_scheduled_task()` | `task_id`, `name`, `fired_at` |
| `as_ai_usage()` | `payload`, `target_user_id` |
| `payload()` | the caller's JSON on a manual invoke, or a scheduler task's static payload; `None` when there is none |
Expand Down
1 change: 1 addition & 0 deletions docs/webhooks-receiver.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ flips give the **entity directly**; only an arbitrary mutation gives a
| `database.records.inserted` | batch | `list` of documents |
| `files.file.uploaded` | entity | `FileResourceRef` |
| `payments.webhook.received` | entity | `PaymentFact` — the normalized payment fact (`event_name` = the provider's own name, `status`, `raw["amountMinor"]`, …); `event.metadata.payment` = provider ids |
| `payments.order.paid` / `partially_refunded` / `refunded` / `reversed` / `expired` | entity | `PaymentOrderEvent` — `order` (the order as stored after the change: `status`, `settled_minor`, `refunded_minor`, `metadata`, …), `payment` (the callback, a `PaymentFact`), `from_` / `to`; de-duplicate on `order_event_id` (the same on every retry). `event.metadata.order` = `{id, order_event_id, status}`, `event.metadata.payment` / `integration_id` from `data.payment` |

Wrapper identifiers (entity id, schema, record ids) are lifted onto
`event.metadata` (`event.metadata.user`, `.schema`, `.record`, `.records`).
Expand Down
Loading
Loading