clean and refine product docs structure

This commit is contained in:
hamid
2026-06-24 01:32:46 +03:30
parent be07c703ec
commit 1df3cd9f64
113 changed files with 6078 additions and 4973 deletions
+195
View File
@@ -0,0 +1,195 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Database Model — Balinyaar docs</title>
<link rel="stylesheet" href="../assets/doc.css">
</head>
<body>
<div class="layout">
<aside class="sidebar">
<a class="brand" href="../index.html"><span class="dot"></span> Balinyaar docs</a>
<p class="tagline">Trust-first home-nursing marketplace · Iran</p>
<nav><div class="group"><div class="label">Start here</div><ul><li><a href="../index.html">Docs home</a></li><li><a href="../overview/platform-summary.html">Platform summary &amp; ground truths</a></li></ul></div><div class="group"><div class="label">Business requirements</div><ul><li><a href="../business/index.html">Overview &amp; MVP scope</a></li><li><a href="../business/01-actors-and-onboarding.html">1. Actors &amp; onboarding</a></li><li><a href="../business/02-nurse-verification.html">2. Nurse verification</a></li><li><a href="../business/03-service-catalog-and-pricing.html">3. Service catalog &amp; pricing</a></li><li><a href="../business/04-search-and-matching.html">4. Search &amp; matching</a></li><li><a href="../business/05-booking-and-scheduling.html">5. Booking &amp; scheduling</a></li><li><a href="../business/06-evv-and-service-delivery.html">6. EVV / service delivery</a></li><li><a href="../business/07-cancellation-and-refunds.html">7. Cancellation &amp; refunds</a></li><li><a href="../business/08-payments-and-escrow.html">8. Payments &amp; escrow</a></li><li><a href="../business/09-installments-bnpl.html">9. Installments / BNPL</a></li><li><a href="../business/10-payouts.html">10. Payouts to nurses</a></li><li><a href="../business/11-reviews-trust-and-safety.html">11. Reviews, trust &amp; safety</a></li><li><a href="../business/12-messaging-and-emergencies.html">12. Messaging &amp; emergencies</a></li><li><a href="../business/13-tax-invoicing-and-legal.html">13. Tax, invoicing &amp; legal</a></li><li><a href="../business/14-notifications-and-admin.html">14. Notifications &amp; admin</a></li></ul></div><div class="group"><div class="label">Database model</div><ul><li><a class="active" href="index.html">Overview &amp; decisions</a></li><li><a href="diagrams.html">Diagrams</a></li><li><a href="01-identity-and-access.html">1. Identity &amp; access</a></li><li><a href="02-geography.html">2. Geography</a></li><li><a href="03-services-and-pricing.html">3. Services &amp; pricing</a></li><li><a href="04-verification-and-credentials.html">4. Verification &amp; credentials</a></li><li><a href="05-booking-and-scheduling.html">5. Booking &amp; scheduling</a></li><li><a href="06-payments-ledger-and-refunds.html">6. Payments, ledger &amp; refunds</a></li><li><a href="07-payouts.html">7. Payouts</a></li><li><a href="08-bnpl.html">8. BNPL / installments</a></li><li><a href="09-messaging.html">9. Messaging</a></li><li><a href="10-reviews-and-records.html">10. Reviews &amp; records</a></li><li><a href="11-notifications.html">11. Notifications</a></li><li><a href="12-audit-config-and-reference.html">12. Audit, config &amp; reference</a></li><li><a href="13-partner-centers-and-future.html">13. Partner centers &amp; future</a></li></ul></div><div class="group"><div class="label">Payments deep-dive</div><ul><li><a href="../payments/index.html">Overview &amp; exec summary</a></li><li><a href="../payments/iranian-payment-reality.html">Iranian payment reality</a></li><li><a href="../payments/escrow-ledger.html">Escrow as a ledger</a></li><li><a href="../payments/bnpl-landscape.html">BNPL landscape &amp; finding</a></li><li><a href="../payments/cancellation-and-payout.html">Cancellation &amp; nurse payout</a></li><li><a href="../payments/integration-notes.html">Integration &amp; schema touchpoints</a></li><li><a href="../payments/sources.html">Recommendations &amp; sources</a></li></ul></div><div class="group"><div class="label">Research &amp; strategy</div><ul><li><a href="../research/index.html">Overview &amp; exec summary</a></li><li><a href="../research/market-and-competitors.html">Market &amp; competitors</a></li><li><a href="../research/problems-and-risks.html">Problems &amp; risks</a></li><li><a href="../research/verification.html">Verification (research)</a></li><li><a href="../research/legal-landscape.html">Legal landscape</a></li><li><a href="../research/go-to-market.html">Go-to-market &amp; sources</a></li></ul></div><div class="group"><div class="label">Notes &amp; more</div><ul><li><a href="../notes/open-questions.html">Open questions</a></li><li><a href="../notes/future-ideas.html">Future ideas</a></li><li><a href="../wireframes/index.html">Wireframes</a></li><li><a href="../fa/index.html">Farsi documents</a></li></ul></div></nav>
</aside>
<main class="main"><div class="content">
<div class="topbar"><button class="theme-toggle" type="button" onclick="__t()">theme</button></div>
<h1 id="database-model">Database Model</h1>
<blockquote><p><strong>Revision 2 — 2026-06-20.</strong> This is a research-driven refinement of the original 13-domain model. It closes the financial-correctness gaps the previous version flagged in its own _Advices_ section, resolves the two open BNPL questions, and grounds every money decision in verified research on the Iranian payment landscape (SnappPay, Digipay, Tara, Torob Pay, Shaparak/پرداخت‌یار rules). The previous revision is preserved in git history.</p>
<p>Companion documents: <strong><a href="../business/index.html">business requirements</a></strong> (per-section product requirements) and <strong><a href="../payments/index.html">payments deep-dive</a></strong> (the BNPL/escrow deep-dive with sources).</p>
</blockquote>
<h2 id="contents">Contents <a class="anchor" href="#contents" aria-hidden="true">#</a></h2>
<ul>
<li><a href="diagrams.html">Diagrams</a></li>
<li><a href="01-identity-and-access.html">Domain 1 — Identity &amp; Access</a></li>
<li><a href="02-geography.html">Domain 2 — Geographic Data</a></li>
<li><a href="03-services-and-pricing.html">Domain 3 — Services &amp; Pricing</a></li>
<li><a href="04-verification-and-credentials.html">Domain 4 — Verification &amp; Credentials</a></li>
<li><a href="05-booking-and-scheduling.html">Domain 5 — Booking &amp; Scheduling</a></li>
<li><a href="06-payments-ledger-and-refunds.html">Domain 6 — Payments, Ledger &amp; Refunds</a></li>
<li><a href="07-payouts.html">Domain 7 — Payouts to Nurses</a></li>
<li><a href="08-bnpl.html">Domain 8 — BNPL / Installments</a></li>
<li><a href="09-messaging.html">Domain 9 — Messaging (Ticket System)</a></li>
<li><a href="10-reviews-and-records.html">Domain 10 — Reviews &amp; Patient Records</a></li>
<li><a href="11-notifications.html">Domain 11 — Notifications</a></li>
<li><a href="12-audit-config-and-reference.html">Domain 12 — Audit, Config &amp; Reference</a></li>
<li><a href="13-partner-centers-and-future.html">Domain 13 — Partner Centers (launch) &amp; Future</a></li>
</ul>
<hr>
<h2 id="platform-summary">Platform Summary <a class="anchor" href="#platform-summary" aria-hidden="true">#</a></h2>
<p>Balinyaar is a trust-first home-nursing marketplace in Iran. Independent, individually-verified nurses register, list configurable services with their own pricing, and pass a multi-step verification pipeline anchored on the MoH <strong>پروانه صلاحیت حرفه‌ای</strong> (professional-competency license). Families search — filtered by city/district <strong>and same-gender caregiver preference</strong> — pick a nurse and a service variant, submit a booking request, and pay <strong>through the platform</strong> after the nurse accepts. The platform records the money as an <strong>internal escrow ledger state</strong> (not platform-held cash — see Principle 2), the nurse performs one or more EVV-verified visits, and the platform pays the nurse <strong>weekly, after the dispute window closes</strong>, minus a platform commission. All post-booking communication runs through an admin-readable ticket system.</p>
<p>At launch the platform operates under a <strong>partner licensed home-nursing center (مرکز مشاوره و ارائه مراقبت‌های پرستاری در منزل)</strong> — the Asanism-style model — which is the legal vehicle and the likely <strong>merchant-of-record</strong> for payments while Balinyaar's own MoH permit is in process.</p>
<hr>
<h2 id="what-changed-in-this-revision-decision-summary">What changed in this revision (decision summary) <a class="anchor" href="#what-changed-in-this-revision-decision-summary" aria-hidden="true">#</a></h2>
<div class="table-wrap"><table><thead><tr><th>#</th><th>Decision</th><th>Why</th><th>Schema effect</th></tr></thead><tbody>
<tr><td>1</td><td><strong>Escrow is a ledger _state_, not held cash</strong></td><td>An Iranian پرداخت‌یار (payment facilitator) is legally barred from custodying buyer funds; money flows card → PSP → Shaparak → registered IBANs.</td><td>New <code>ledger_entries</code> (double-entry); "escrow" derives from it.</td></tr>
<tr><td>2</td><td><strong>BNPL = full-upfront single settlement</strong></td><td>Verified: SnappPay/Digipay/Tara/Torob pay the <em>merchant</em> the whole amount minus commission in one lump and bear the customer-default risk.</td><td><strong>Cut</strong> <code>installment_plans</code>/<code>installment_entries</code>; <strong>replace</strong> with one <code>bnpl_transactions</code> row. No customer-installment tracking.</td></tr>
<tr><td>3</td><td><strong>Nurse paid by Balinyaar, on its own weekly schedule</strong></td><td>The customer's installments are owned by the BNPL provider and decoupled from our payout.</td><td>Three-way money split on <code>bookings</code>; payout independent of BNPL.</td></tr>
<tr><td>4</td><td><strong>Clawback is first-class</strong></td><td>A booking can be disputed/refunded after the nurse was already paid; the old model had nowhere to record the receivable.</td><td>New <code>nurse_clawbacks</code> + <code>dispute_window_ends_at</code> gating.</td></tr>
<tr><td>5</td><td><strong>Webhook idempotency before real money</strong></td><td>PSP/BNPL callbacks are at-least-once and retried.</td><td>New <code>payment_webhook_events</code> keyed on <code>external_event_id</code>.</td></tr>
<tr><td>6</td><td><strong>Multi-session engagements</strong></td><td>Elder care is dominantly multi-day / شبانه‌روزی (live-in); one-visit-per-booking can't model it.</td><td>New <code>booking_sessions</code>; EVV + payout move to the session.</td></tr>
<tr><td>7</td><td><strong>Partner licensed-center entity</strong></td><td>The recommended launch path is subcontracting to an MoH-licensed center; it may be the merchant-of-record/invoice issuer.</td><td>New <code>partner_centers</code> + <code>nurse_profiles.partner_center_id</code>.</td></tr>
<tr><td>8</td><td><strong>Structured credential registry</strong></td><td>The "verified" trust badge and renewal alerts need queryable license numbers/expiries, not just opaque PDF uploads.</td><td>New <code>nurse_credentials</code>.</td></tr>
<tr><td>9</td><td><strong>Cheaper search</strong></td><td>Nurse search needed 4+ joins from day one.</td><td>New denormalized <code>nurse_search_index</code>.</td></tr>
<tr><td>10</td><td><strong>Cancellation policy + tax/invoice</strong></td><td>"Default 100% refund" is naive; Iranian commission marketplaces owe VAT on commission.</td><td>New <code>cancellation_policies</code>, <code>invoices</code>; VAT <strong>10%</strong> (configurable).</td></tr>
<tr><td>11</td><td><strong>Integrity hardening</strong></td><td>Drift, double-pay, and tenancy-leak gaps the critiques found.</td><td>Drop duplicate <code>verification_status</code> &amp; <code>payout_released</code>; add uniqueness/CHECK/tenancy invariants.</td></tr>
</tbody></table></div>
<hr>
<h2 id="design-principles">Design Principles <a class="anchor" href="#design-principles" aria-hidden="true">#</a></h2>
<ol>
<li><strong>Money is <code>BIGINT</code> in Iranian Rials (IRR).</strong> Toman is a display concern only; conversion happens <strong>only at a provider's API boundary</strong> (e.g. SnappPay quotes Toman) and never internally. No floats anywhere on the money path.</li>
<li><strong>The platform never legally holds buyer cash.</strong> Funds settle through a licensed PSP/پرداخت‌یار to <strong>registered IBANs</strong> (the platform's commission IBAN and the nurse's IBAN, via تسهیم settlement-sharing, or to one merchant-of-record account). "Escrow" and "nurse balance" are <strong>derived ledger states</strong> over money custodied at the provider/partner bank — represented in <code>ledger_entries</code>, never as a Balinyaar-owned cash balance.</li>
<li><strong><code>ledger_entries</code> is the financial source of truth.</strong> Every capture, commission, payout, refund, and clawback posts <strong>balanced</strong> double-entry rows. Per-table money fields (e.g. <code>bookings.gross_price_irr</code>) remain the operational/pricing record; the ledger is the reconciliation truth that answers "how much do we owe nurses right now" and "how much is held but unreleased."</li>
<li><strong>Fee split is captured per booking and never derived from live config</strong>, so historical reporting survives commission-schedule changes. The booking stores three distinct amounts: <code>gross_price_irr</code>, <code>balinyaar_commission_irr</code>, <code>nurse_payout_amount</code>.</li>
<li><strong>PII fields</strong> (national ID, IBAN, phone, addresses, clinical data) are marked <strong>(encrypted)</strong> — column- or application-level. Clinical data has stricter access than financial data.</li>
<li><strong>Two-stage clinical disclosure is a hard rule, not a convention.</strong> At the request stage the nurse sees only <code>booking_requests.customer_notes</code>. The full encrypted <code>booking_care_instructions</code> are exposed <strong>only after</strong> the booking is confirmed. Enforced at the authorization layer.</li>
<li><strong>Soft deletes</strong> on <code>users</code>/<code>nurse_profiles</code> via <code>deleted_at</code>. Audit, payment, ledger, and payout records are <strong>never</strong> deleted.</li>
<li><strong>Audit trail is append-only.</strong> All state transitions on bookings, payments, refunds, payouts, verifications, reviews, and <code>platform_configs</code> produce an <code>audit_logs</code> row.</li>
<li><strong>Catalog/config tables are rows, not enums</strong> (service categories, verification step types, cancellation policies, Iranian holidays) so the business evolves without migrations. They carry <code>name_fa</code>/<code>name_en</code>.</li>
<li><strong>Idempotency is mandatory on the money path.</strong> Every PSP/BNPL callback is stored raw in <code>payment_webhook_events</code> and deduplicated on <code>external_event_id</code> <strong>before</strong> any money-state mutation.</li>
<li><strong>All timestamps are <code>DATETIME2(7)</code> UTC.</strong> Persian-calendar display is a UI concern — <strong>except</strong> that bank-closure scheduling uses the <code>iranian_holidays</code> table, because PAYA/SATNA transfers fail on holidays.</li>
<li><strong>Derived flags must not drift.</strong> <code>nurse_profiles.is_verified</code>, denormalized rating aggregates, and the search index are written <strong>only</strong> by the code path that owns their source of truth, inside the same transaction.</li>
<li><strong>Invariants are enforced, not just documented:</strong> CHECK constraints (<code>gross = commission + payout</code>, <code>rating BETWEEN 1 AND 5</code>, amounts ≥ 0, <code>end_time &gt; start_time</code>), filtered-UNIQUE for "one primary"/"one active", and tenancy checks (a booking's patient/address must belong to the same customer; its variant to the same nurse).</li>
</ol>
<hr>
<h2 id="the-two-questions-this-revision-answers">The two questions this revision answers <a class="anchor" href="#the-two-questions-this-revision-answers" aria-hidden="true">#</a></h2>
<p>These were the two hardest open questions (from <code>whatsInYourMind.txt</code>). Both are resolved against <strong>verified</strong> research that all mainstream Iranian provider-financed BNPLs use <strong>full-upfront settlement</strong> — the provider pays the merchant the whole amount minus commission and owns the customer's installments and default risk.</p>
<h3 id="q1-a-booking-paid-by-installments-bnpl-is-cancelled-or-refunded-mid-plan-what-happens">Q1 — A booking paid by installments (BNPL) is cancelled or refunded mid-plan. What happens? <a class="anchor" href="#q1-a-booking-paid-by-installments-bnpl-is-cancelled-or-refunded-mid-plan-what-happens" aria-hidden="true">#</a></h3>
<p>Money <strong>always</strong> flows <code>customer ↔ BNPL provider ↔ Balinyaar</code><strong>never</strong> nurse→customer, and <strong>never</strong> Balinyaar→customer directly for a BNPL order.</p>
<ol>
<li>Balinyaar initiates the reversal through the provider's API (SnappPay <code>revert</code> for full / <code>cancel</code>/<code>update</code> for partial, using the stored <code>external_payment_token</code>).</li>
<li>The provider then <strong>cancels the customer's unpaid installments</strong>, restores their credit, and <strong>refunds any already-paid installment to the customer's bank account in ~710 business days</strong> (asynchronous, owned by the provider).</li>
<li>Balinyaar records a <code>refunds</code> row with <code>refund_channel = 'bnpl_revert'</code>, carrying <code>external_revert_reference</code> and <code>expected_customer_refund_eta</code>; <code>refund_status</code> stays <code>processing</code> until a reconciliation job confirms.</li>
<li>The refund <strong>decomposes</strong> across the two fee legs — <code>platform_fee_refunded_irr</code> and <code>nurse_payout_refunded_irr</code> — and posts <strong>balanced ledger entries</strong>.</li>
<li><strong>If the nurse has not yet been paid</strong> (still inside the dispute window / not in a processed batch): the <code>nurse_payable</code> accrual is simply reversed; nothing leaves Balinyaar. Clean.</li>
<li><strong>If the nurse has already been paid:</strong> this is the <strong>clawback</strong> path — a <code>nurse_clawbacks</code> receivable + negative ledger entry; recovered from the next payout batch or written off.</li>
</ol>
<p>A shortened/partial visit maps to the provider's <code>update</code> endpoint with a reduced amount; record <code>refund_delta_irr</code> and reduce <code>bnpl_transactions.settled_amount_irr</code>.</p>
<h3 id="q2-under-bnpl-who-pays-the-nurse-and-when">Q2 — Under BNPL, who pays the nurse and when? <a class="anchor" href="#q2-under-bnpl-who-pays-the-nurse-and-when" aria-hidden="true">#</a></h3>
<p><strong>Balinyaar pays the nurse</strong>, on its <strong>own normal weekly payout schedule, after EVV completion and after the dispute window closes</strong><em>exactly the same path as a card-funded booking</em>. The BNPL provider never pays the nurse and is indifferent to the internal split.</p>
<p>The nurse's payout is computed from the booking's <strong><code>gross_price_irr</code> minus <code>balinyaar_commission_irr</code></strong><strong>never</strong> from the BNPL provider's net <code>settled_amount_irr</code>. The provider's commission (<code>bnpl_commission_irr</code>) is a <strong>platform cost of accepting BNPL</strong>, borne by Balinyaar, and must never touch the nurse's payout. Hence three separately stored amounts:</p>
<div class="table-wrap"><table><thead><tr><th>Amount</th><th>Meaning</th><th>Drives</th></tr></thead><tbody>
<tr><td><code>gross_price_irr</code></td><td>What the customer is charged (the booking price)</td><td>The invoice, the refund base</td></tr>
<tr><td><code>balinyaar_commission_irr</code></td><td>Platform's own cut</td><td>Platform revenue</td></tr>
<tr><td><code>bnpl_commission_irr</code></td><td>The BNPL provider's merchant discount (on <code>bnpl_transactions</code>)</td><td>Platform <strong>expense</strong> (never the nurse's)</td></tr>
</tbody></table></div>
<p><code>nurse_payout_amount = gross_price_irr balinyaar_commission_irr</code>. The nurse receives the identical amount and on the identical schedule whether the family paid by card or by SnappPay.</p>
<hr>
<h2 id="relationship-summary">Relationship Summary <a class="anchor" href="#relationship-summary" aria-hidden="true">#</a></h2>
<div class="table-wrap"><table><thead><tr><th>Relationship</th><th>Type</th><th>Notes</th></tr></thead><tbody>
<tr><td><code>users</code><code>nurse_profiles</code> / <code>customer_profiles</code></td><td>1:1</td><td>by <code>role</code></td></tr>
<tr><td><code>partner_centers</code><code>nurse_profiles</code></td><td>1:N</td><td>launch sponsor (NEW)</td></tr>
<tr><td><code>customer_profiles</code><code>patients</code> / <code>customer_addresses</code></td><td>1:N</td><td></td></tr>
<tr><td><code>nurse_profiles</code><code>nurse_service_variants</code> / <code>nurse_service_areas</code> / <code>nurse_bank_accounts</code> / <code>nurse_credentials</code></td><td>1:N</td><td></td></tr>
<tr><td><code>nurse_service_variants</code><code>nurse_service_variant_options</code></td><td>1:N</td><td>option combination</td></tr>
<tr><td><code>nurse_profiles</code><code>nurse_verifications</code></td><td>1:1</td><td></td></tr>
<tr><td><code>nurse_verifications</code><code>verification_steps</code><code>verification_documents</code></td><td>1:N → 1:N</td><td></td></tr>
<tr><td><code>booking_requests</code><code>bookings</code></td><td>1:1</td><td>on nurse-accept + payment</td></tr>
<tr><td><code>bookings</code><code>booking_sessions</code></td><td>1:N</td><td><strong>NEW</strong> — multi-visit engagements</td></tr>
<tr><td><code>booking_sessions</code><code>visit_verifications</code></td><td>1:1</td><td><strong>CHANGED</strong> — EVV per session</td></tr>
<tr><td><code>bookings</code><code>booking_care_instructions</code> / <code>reviews</code> / <code>invoices</code></td><td>1:1</td><td></td></tr>
<tr><td><code>bookings</code><code>payment_transactions</code></td><td>1:N</td><td>attempts</td></tr>
<tr><td><code>payment_transactions</code><code>bnpl_transactions</code></td><td>1:1</td><td>if BNPL (<strong>replaces</strong> installment_plans)</td></tr>
<tr><td><code>payment_transactions</code><code>refunds</code></td><td><strong>1:N</strong></td><td><strong>CHANGED</strong> — partials allowed</td></tr>
<tr><td><code>payment_gateways</code><code>payment_webhook_events</code></td><td>1:N</td><td><strong>NEW</strong> — idempotency</td></tr>
<tr><td><code>bookings</code> / nurses → <code>ledger_entries</code></td><td>1:N</td><td><strong>NEW</strong> — money source of truth</td></tr>
<tr><td><code>refunds</code><code>nurse_clawbacks</code></td><td>1:1 (opt)</td><td><strong>NEW</strong> — refund-after-payout</td></tr>
<tr><td><code>nurse_payout_batches</code><code>nurse_payouts</code><code>nurse_payout_booking_links</code></td><td>1:N → 1:N</td><td><code>booking_id</code> UNIQUE</td></tr>
<tr><td><code>nurse_payout_booking_links</code><code>bookings</code></td><td>1:1</td><td>exactly one payout per booking</td></tr>
<tr><td><code>patients</code><code>patient_care_records</code></td><td>1:N</td><td>longitudinal history</td></tr>
<tr><td><code>tickets</code><code>ticket_participants</code> / <code>ticket_messages</code></td><td>1:N</td><td></td></tr>
<tr><td>Sensitive entities → <code>audit_logs</code></td><td>*:N</td><td>append-only</td></tr>
</tbody></table></div>
<hr>
<h2 id="final-mvp-table-list">Final MVP table list <a class="anchor" href="#final-mvp-table-list" aria-hidden="true">#</a></h2>
<p><strong>Identity &amp; Access:</strong> <code>users</code> · <code>user_sessions</code> · <code>roles</code> · <code>user_roles</code> · <code>nurse_profiles</code> · <code>customer_profiles</code> · <code>patients</code> · <code>customer_addresses</code> · <code>nurse_bank_accounts</code> — all <strong>[CORE]</strong></p>
<p><strong>Geography:</strong> <code>provinces</code> · <code>cities</code> <strong>[CORE]</strong> · <code>districts</code> <strong>[MVP]</strong> · <code>nurse_service_areas</code> <strong>[CORE]</strong></p>
<p><strong>Services &amp; Pricing:</strong> <code>service_categories</code> · <code>service_option_groups</code> · <code>service_option_values</code> · <code>nurse_service_variants</code> · <code>nurse_service_variant_options</code> · <strong><code>nurse_search_index</code></strong> <em>(NEW)</em><strong>[CORE]</strong>; <code>nurse_availability_slots</code> · <code>nurse_availability_exceptions</code><strong>[MVP]</strong></p>
<p><strong>Verification:</strong> <code>nurse_verifications</code> · <code>verification_step_types</code> · <code>verification_steps</code> · <code>verification_documents</code> <strong>[CORE]</strong>; <strong><code>nurse_credentials</code></strong> <em>(NEW)</em> <strong>[MVP]</strong></p>
<p><strong>Booking &amp; Scheduling:</strong> <code>booking_requests</code> · <code>bookings</code> · <code>booking_care_instructions</code> · <code>visit_verifications</code> <strong>[CORE]</strong>; <strong><code>booking_sessions</code></strong> <em>(NEW)</em> · <strong><code>cancellation_policies</code></strong> <em>(NEW)</em> <strong>[MVP]</strong></p>
<p><strong>Payments &amp; Ledger:</strong> <code>payment_gateways</code> · <code>payment_transactions</code> · <strong><code>payment_webhook_events</code></strong> <em>(NEW)</em> · <code>refunds</code> · <strong><code>ledger_entries</code></strong> <em>(NEW)</em> · <strong><code>nurse_clawbacks</code></strong> <em>(NEW)</em> · <code>nurse_payout_batches</code> · <code>nurse_payouts</code> · <code>nurse_payout_booking_links</code> <strong>[CORE]</strong>; <strong><code>invoices</code></strong> <em>(NEW)</em> <strong>[MVP]</strong></p>
<p><strong>BNPL:</strong> <strong><code>bnpl_transactions</code></strong> <em>(NEW — replaces <code>installment_plans</code>)</em> <strong>[MVP]</strong>; <del><code>installment_plans</code></del> · <del><code>installment_entries</code></del> <strong>CUT</strong>; <code>bnpl_settlement_entries</code> <strong>[DEFERRED]</strong></p>
<p><strong>Messaging:</strong> <code>tickets</code> · <code>ticket_participants</code> · <code>ticket_messages</code> <strong>[CORE]</strong></p>
<p><strong>Reviews &amp; Records:</strong> <code>reviews</code> <strong>[CORE]</strong>; <code>review_tags_master</code> · <code>review_tag_links</code> · <code>patient_care_records</code> <strong>[MVP]</strong></p>
<p><strong>Notifications:</strong> <code>notifications</code> · <code>support_alerts</code> <strong>[CORE]</strong></p>
<p><strong>Audit &amp; Config:</strong> <code>audit_logs</code> <strong>[CORE]</strong> · <code>system_events</code> <strong>[MVP]</strong> · <code>platform_configs</code> <strong>[CORE]</strong> · <strong><code>iranian_holidays</code></strong> <em>(NEW)</em> <strong>[MVP]</strong></p>
<p><strong>Partner / Launch:</strong> <strong><code>partner_centers</code></strong> <em>(NEW)</em> <strong>[MVP]</strong></p>
<p><strong>Future (modeled, inactive):</strong> <code>organizations</code> · <code>organization_nurses</code> · <code>fraud_flags</code> · <code>recurring_booking_schedules</code> · <code>bnpl_settlement_entries</code> — all <strong>[DEFERRED]</strong></p>
<p><strong>Net change vs the original 45:</strong> 2 cut (<code>installment_plans</code> replaced, <code>installment_entries</code> removed), +10 added (<code>ledger_entries</code>, <code>nurse_clawbacks</code>, <code>payment_webhook_events</code>, <code>nurse_search_index</code>, <code>booking_sessions</code>, <code>cancellation_policies</code>, <code>invoices</code>, <code>partner_centers</code>, <code>nurse_credentials</code>, <code>iranian_holidays</code>), 1 replaced (<code>bnpl_transactions</code>). The financial core is now a single ledger, BNPL is one settlement row, and the clawback / dispute-window / idempotency / license / multi-session gaps are all closed.</p>
<hr>
<h2 id="key-design-decisions-the-reasoning-in-one-place">Key Design Decisions (the reasoning, in one place) <a class="anchor" href="#key-design-decisions-the-reasoning-in-one-place" aria-hidden="true">#</a></h2>
<ol>
<li><strong>Escrow as a ledger state, not platform cash</strong> — because an Iranian پرداخت‌یار legally cannot custody buyer funds. Everything else in the money domain follows from honestly representing "we don't hold the cash; we hold a claim/obligation tracked in the ledger over funds at a licensed provider." This is also why payouts are modeled as provider-side settlement to <strong>verified, ownership-checked</strong> IBANs.</li>
</ol>
<ol>
<li><strong>A BNPL order is a net-of-fee inbound payment, full stop</strong> — the verified full-upfront settlement model means there is no customer receivable, no default risk, and no installment schedule for Balinyaar to track. Deleting <code>installment_entries</code> removed an entire fragile subsystem and replaced it with one reconciliation row.</li>
</ol>
<ol>
<li><strong>Three separate money amounts</strong> so the platform's two fee deductions (its own commission, and the BNPL provider's discount) are never conflated, and the nurse is paid identically regardless of payment method.</li>
</ol>
<ol>
<li><strong>Double-entry over status flags</strong> — the previous model could not answer "how much do we owe nurses right now" without fragile joins, and had nowhere to record a refund-after-payout. One append-only ledger + a <code>nurse_clawbacks</code> receivable fixes both and makes bank/Shaparak reconciliation possible.</li>
</ol>
<ol>
<li><strong>Dispute window gates payout</strong> — preferring a <em>holding period</em> over a <em>clawback</em>, because clawback against an already-paid nurse IBAN is largely unenforceable. The clawback path exists for the cases that slip through.</li>
</ol>
<ol>
<li><strong>Idempotency before money</strong><code>payment_webhook_events</code> keyed on the provider event id, written first, is the cheapest insurance against the most damaging payments bug (double-confirm / double-settle on callback retries).</li>
</ol>
<ol>
<li><strong>Multi-session engagements are the norm, not an edge case</strong><code>booking_sessions</code> makes long elder-care arrangements representable, lets escrow release per completed visit instead of holding a month of money, and makes mid-engagement cancellation accounting clean.</li>
</ol>
<ol>
<li><strong>Partner center is launch-critical</strong> — it is the legal vehicle and likely merchant-of-record; without it the recommended go-to-market and the money flow are not representable.</li>
</ol>
<ol>
<li><strong>Verified-trust must be queryable</strong><code>nurse_credentials</code> turns the brand promise into renewal alerts, a real badge, and audit defensibility, surviving the future arrival of an INO/MoH API.</li>
</ol>
<ol>
<li><strong>Keep the configurable service EAV; cut the analytics scaffolding</strong> — the category/option model earns its complexity (admin-extensible pricing dimensions without migrations); <code>response_rate</code>/<code>profile_completion_score</code>/<code>system_events</code>-in-SQL do not, at launch.</li>
</ol>
<hr>
<h2 id="open-items-to-confirm-before-building-not-schema-blockers">Open items to confirm before building (not schema blockers) <a class="anchor" href="#open-items-to-confirm-before-building-not-schema-blockers" aria-hidden="true">#</a></h2>
<ul>
<li><strong>BNPL provider contract:</strong> does SnappPay/Digipay permit a multi-vendor marketplace re-disbursing to many nurses as a single merchant? (Publicly undocumented — confirm with sales.) The schema assumes <strong>one lump to Balinyaar/the center, internal allocation to nurses</strong>, so this is an ops confirmation, not a schema dependency.</li>
<li><strong>Commission %</strong> and <strong>settlement SLA</strong> per provider (and whether the provider returns its commission on a refund — full or pro-rata).</li>
<li><strong>PSP/تسهیم provider</strong> for MVP (ZarinPal Multiplexing vs Vandar vs Jibit) and whether it permits the hold-then-weekly-payout timing, or whether a bank-grade escrow (Vandar میندو) is needed.</li>
<li><strong>VAT exemption</strong> ruling on the nursing service itself (the commission line is taxable regardless) — <code>vat_rate</code> is config-driven so either ruling is a value change.</li>
<li><strong>مودیان</strong> enrollment thresholds for the platform and high-earning nurses.</li>
</ul>
<blockquote><p>Confirm decades-old regulations, provider fee/settlement specifics, and tax thresholds against current primary sources and the provider's compliance team before building the payment integration. See the <a href="../payments/index.html">payments deep-dive</a> for the full source-cited analysis.</p>
</blockquote>
<a class="back-to-top" href="#">↑ Back to top</a>
</div></main>
</div>
<script>
(function(){var k='balinyaar-docs-theme';var s=localStorage.getItem(k);
if(s)document.documentElement.setAttribute('data-theme',s);
else if(matchMedia('(prefers-color-scheme: dark)').matches)document.documentElement.setAttribute('data-theme','dark');})();
function __t(){var d=document.documentElement;var n=d.getAttribute('data-theme')==='dark'?'light':'dark';
d.setAttribute('data-theme',n);localStorage.setItem('balinyaar-docs-theme',n);}
</script>
</body>
</html>