SplitEasy
Receipt-attached bill-splitting integration. Diners scan a QR on the printed receipt to split the meal with friends.
Integration shape
| Slug | spliteasy |
| Category | receipt |
| Capabilities | receipt_qr, payment_webhook_inbound |
| Mode | add (stacks with other receipt_qr installs) |
| Auth | platform_shared · HS256, 32-byte secret |
| Intake host | api.spliteasy.uncle-z.com |
| Diner landing | s.uncle-z.com/b/<token> |
| Data residency | id (servers in Indonesia) |
End-to-end flow
- Owner installs SplitEasy from
posz.uncle-z.com/integrations. One-click. No SplitEasy account needed. - Posz POSTs
/v1/external/merchantsto SplitEasy with the merchant payload — SplitEasy creates a merchant record and returnsexternal_merchant_id. - Cashier closes an order. Posz’s receipt builder calls the SplitEasy adapter for capability
receipt_qr. - Adapter signs a 15-min JWT and POSTs
/v1/external/billswith the bill payload — SplitEasy returns abill_token. - Posz embeds the QR encoding
https://s.uncle-z.com/b/<bill_token>in the receipt footer. - Diner scans → SplitEasy landing → split with friends → settle inter-friend.
- SplitEasy webhooks Posz at
/v1/integrations/spliteasy/webhookwithbill.settled/bill.expired/bill.cancelled. Posz writes an audit log row.
Auth JWT
HS256 signed with the platform-shared 32-byte secret. 15-minute TTL — sign a fresh JWT per request; never cache.
{
"iss": "posz",
"aud": "spliteasy",
"exp": 1714493700,
"iat": 1714492800,
"merchant": {
"id": "<posz_merchant_uuid>",
"name": "Maple & Oat",
"location": "Senopati",
"city": "Jakarta",
"email": "owner@mapleandoat.id",
"phone": null,
"currency": "IDR",
"locale": "id",
"timezone": "Asia/Jakarta",
"logo_url": "https://posz-uploads.s3.ap-southeast-3.amazonaws.com/..."
}
} Why platform-shared (not OAuth)
SplitEasy is consumer-facing — diners use the app, not merchants. There’s nothing for the merchant to log into and authorize. Posz vouches for whichever merchant.id it embeds; SplitEasy auto-creates the merchant record on first encounter.
Bill intake
Currency in minor units (_cents) end-to-end.
ISO 4217 currency code separate. For zero-decimal currencies
(IDR, JPY, KRW, VND…) the minor unit equals the major unit, so
49000 means Rp 49.000.
POST /v1/external/bills HTTP/1.1
Host: api.spliteasy.uncle-z.com
Authorization: Bearer <jwt>
Content-Type: application/json {
"merchant_ref": "posz_order_<order_uuid>",
"currency": "IDR",
"items": [
{
"name": "Nasi Goreng",
"qty": 1,
"unit_price_cents": 4900000,
"modifiers": [{ "name": "Extra telur", "price_cents": 500000 }]
}
],
"discounts": [
{ "name": "Member 10%", "amount_cents": 1200000, "scope": "order" }
],
"service": { "type": "percent", "value": 5.5 },
"tax": { "type": "percent", "value": 11 },
"tip_amount_cents": 0,
"loyalty_discount_amount_cents": 0,
"metadata": {
"table_id": "B1",
"server": "Agus",
"posz_order_id": "<uuid>"
}
} {
"bill_token": "splt_aB3kP9zL2qR4",
"share_url": "https://s.uncle-z.com/b/splt_aB3kP9zL2qR4",
"expires_at": "2026-06-01T08:30:00Z"
} Idempotency on retry
Dedup key is (jwt.iss, merchant_ref). Posz’s merchant_ref is posz_order_<uuid> — globally unique per order. Posz can safely retry (e.g. transient network failure) and SplitEasy returns the same bill_token.
Settlement webhook
SplitEasy posts events back to Posz at
/v1/integrations/spliteasy/webhook. Signed with
the same shared secret using HMAC-SHA256 in
X-SplitEasy-Signature. Three event types.
| Event | When | Posz behavior |
|---|---|---|
bill.settled | Group has finished paying | Audit log row + usage stats |
bill.expired | 30-day token TTL hit, never settled | Cleans up audit placeholder |
bill.cancelled | Paymaster aborted (closed app, restaurant voided bill) | Cleans up audit placeholder |
POST https://api.posz.uncle-z.com/v1/integrations/spliteasy/webhook
X-SplitEasy-Signature: <hex>
Content-Type: application/json {
"event": "bill.settled",
"merchant_ref": "posz_order_<uuid>",
"bill_token": "splt_aB3kP9zL2qR4",
"settled_at": "2026-04-30T14:23:11+07:00",
"group_size": 4,
"settlement_methods": ["bifast", "cash", "qris"]
} Non-blocking by design
The settlement webhook is informational — Posz already got paid by the paymaster at the cashier. If SplitEasy can’t reach Posz, nothing breaks operationally. Posz will retain the unsettled placeholder until a future bill.expired arrives.
Disconnect semantics
Owner clicks Uninstall in /integrations. Posz fires
POST /v1/external/merchants/{id}:disconnect
on SplitEasy. SplitEasy flips merchant status to
disconnected.
- Subsequent intake calls return 410 Gone (not 401 — the JWT is still valid; it’s the merchant relationship that’s severed).
- Existing printed receipts stay scannable until 30-day bill_token TTL — never strand a customer mid-meal.
- A subsequent re-Connect re-activates the same record (no duplicate; analytics continuity preserved).