> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useduro.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quill

> A Substack-style creator-subscription product built on top of Duro. Real reader billing, recovery, payouts, and bills, with Duro as the managed money layer.

Quill is a Substack-for-Nigeria: writers publish paid publications, readers subscribe monthly in naira, and creators get paid to a bank account. It is a complete, standalone product with its own accounts, editor, feed, and video. What makes it interesting here is the part it does **not** build: none of the money. Every subscription, every recurring charge, every failed-payment recovery, every payout, and every airtime top-up is delegated to **Duro** over its public API.

That makes Quill the reference integration for Duro. It is not a smoke test that calls one endpoint. It is a second, independent codebase (NestJS + Drizzle + Next.js) that consumes Duro the way a real merchant would: create a customer, ensure a plan, open a hosted checkout, verify by polling, receive signed webhooks, route payouts through transfers, and buy bills through VAS. If Duro's managed-subscriptions thesis holds, Quill is the proof that a team can ship a monetised product on top of it without writing a billing engine.

<Note>
  Quill is a hackathon build that **uses** Duro; it is not part of the Duro monorepo. The backend lives in `quill-backend`, the frontend in `quill-frontend`. For the demo, `DURO_MODE` selects between a `MockDuroAdapter` (deterministic, offline) and an `HttpDuroAdapter` that hits a real Duro instance in sandbox with an `sk_test` key. See [Duro Integration](/quill/duro-integration).
</Note>

## What Quill owns vs. what Duro owns

The boundary is the whole point. Quill keeps product state; Duro keeps money state. Quill mirrors just enough of Duro's state (ids and status) to render its own UI and gate content.

<CardGroup cols={2}>
  <Card title="Quill owns" icon="feather">
    Users and roles (`reader` / `creator` / `admin`), publications, posts (TipTap/Markdown), premium-gated video (HLS), comments, the creator dashboard, and an append-only `ledger_entries` table that records recognised revenue for its own earnings/recovery views.
  </Card>

  <Card title="Duro owns" icon="building-columns">
    The customer, the plan, the checkout session, the subscription state machine, the renewal + dunning engine, the recovery decisions, bank transfers (payouts), and VAS/bill vending. Quill never charges a card or moves money itself.
  </Card>
</CardGroup>

## Why it is a strong reference integration

<CardGroup cols={2}>
  <Card title="Exercises the managed-subscriptions path end to end" icon="rotate">
    `subscribe → checkout → verify → webhook activation → renewal → past_due → recover` all run through Duro's public REST surface (`/customers`, `/plans`, `/checkout/sessions`, `/subscriptions/*`), with `Idempotency-Key` on every `POST`.
  </Card>

  <Card title="Consumes the recovery-first product, not just billing" icon="heart-pulse">
    Quill surfaces a creator **recovery** view (at-risk MRR, recovered revenue, recovery rate) driven by Duro's `subscription_past_due` / `subscription_payment_recovered` webhooks. Recovery is a feature of the host product, so it becomes a feature of Quill for free.
  </Card>

  <Card title="Uses Duro for money-out and bills too" icon="arrow-right-arrow-left">
    Creator payouts route through Duro **transfers**; airtime, data, betting, cable TV, and electricity route through Duro **VAS**; bank lists and name resolution come from Duro. One processor, four money flows.
  </Card>

  <Card title="Same-origin BFF, clean trust boundary" icon="shield-halved">
    The Next.js app never talks to the API directly from the browser. A catch-all proxy (`app/api/[...path]/route.ts`) forwards to the NestJS backend and relays the httpOnly session cookie, mirroring Duro's own BFF pattern.
  </Card>
</CardGroup>

## The lifecycle in one glance

```mermaid theme={null}
flowchart LR
    R["Reader clicks Subscribe"] --> Q["Quill backend<br/>subscribe()"]
    Q -->|"ensurePlan · ensureCustomer ·<br/>createCheckout (Idempotency-Key)"| D["Duro public API"]
    D -->|"checkoutUrl"| PAY["Hosted checkout<br/>(Duro)"]
    PAY -->|"success redirect"| VER["Quill /verify<br/>polls GET checkout status"]
    D -->|"signed webhook<br/>subscription_activated"| WH["POST /webhooks/duro"]
    VER --> ACT["subscription → active,<br/>ledger charge recorded"]
    WH --> ACT
    ACT --> UNLOCK["premium posts + video unlocked"]
```

Two independent activation paths converge on the same result: the synchronous `verify` poll (for the reader who is still on the page) and the asynchronous signed webhook (the source of truth). Both are idempotent on the ledger, so activation happens exactly once. This dual-path design is detailed in [Subscriptions & Recovery](/quill/subscriptions-recovery).

## How to read this section

<Steps>
  <Step title="Architecture">
    [Architecture](/quill/architecture) is the whole system at altitude: NestJS modules, the Drizzle schema, the same-origin BFF, BullMQ workers, and R2 media.
  </Step>

  <Step title="Duro Integration">
    [Duro Integration](/quill/duro-integration) is the core: the `DuroClient` interface, the HTTP adapter's exact calls, idempotency, and the signed webhook receiver.
  </Step>

  <Step title="Subscriptions & Recovery">
    [Subscriptions & Recovery](/quill/subscriptions-recovery) walks the status machine and the recovery-first creator metrics.
  </Step>

  <Step title="Payouts & VAS">
    [Payouts & VAS](/quill/payouts-vas) covers money-out (bank accounts, OTP, T+1 settlement) and bill vending, both via Duro.
  </Step>
</Steps>
