Skip to content

Runbook — Manual payments (ADR-0132 Phase 1 / ADR-0134 D3)

How staff fulfil a paid-plan upgrade while self-serve checkout is deferred (ADR-0131). The customer-facing path is Contact us → sales@puccha.ai; this is the staff side.

Who: superadmin only. Where: /admin/payments (Staff console → Payments). Audit: every activation writes a plan_changed lifecycle event; the request keeps external_ref + timestamps.


  1. A request appears. When a customer clicks Contact us on their billing page, a payment_request is recorded (status = pending) and an email lands in sales@puccha.ai. It shows in /admin/payments, pending on top. Target SLA: respond within 1 business day. An Enterprise click arrives as an unquoted request (amount to quote, ADR-0191): agree the figure with the customer, then Quote on the row to set it — Mark paid is unavailable until then. Re-quote also discounts a list-price request from any tier. For deals with no customer click at all, open one yourself: New request on /admin/payments (or New request on the tenant’s hub page) — the amount prefills at list price and is editable; put the quote reference in the note (it lands in the tenant’s audit log). One open request per tenant (ADR-0189).
  2. Verify the request: tenant, plan, cycle, and amount (฿) shown in the row. Confirm it matches what the customer asked for.
  3. Collect payment off-platform. Send the customer payment instructions on any channel — PromptPay QR, bank transfer details, or a hosted link. Use the amount shown.
  4. On confirmed receipt, capture the proof reference (bank slip no. / transfer id) for the record, then click Mark paid on that row.
    • This calls applyPlanChange → sets the tenant’s plan + emits plan_changed.
    • The row flips to activated; the customer is now on the new plan.
  5. Tell the customer it’s active (reply to the original thread).

  • Wrong / partial amount. Do not Mark paid. Reply to the customer for the balance; the request stays pending. (No partial activation.)
  • Customer paid but the plan is wrong. Mark paid activates the plan recorded on the request. If the request itself is wrong, do not Mark paid — Cancel the wrong row and open a fresh one with the right plan (New request).
  • Mistaken activation (activated the wrong tenant/plan). Click Reverse on the activated row (ADR-0138). It rolls the tenant back to the plan it was on before activation (prior_plan), emits a plan_changed event (so it’s audited), and flips the request to reversed. It does not refund — settle any money on the original channel. Caveat: requests activated before ADR-0138 shipped have no prior_plan, so no Reverse button appears — for those, reverse manually: confirm the prior plan from the tenant’s plan_changed events at /admin/tenants/<id>/health, then have an engineer run the corrective UPDATE tenants SET plan=… WHERE id=… and record it in the ops log.
  • Duplicate requests for the same tenant/plan: activate one, Cancel the rest; don’t Mark paid twice — activation is idempotent, but two activated rows muddy the queue.
  • Stale requests (customer never paid): Cancel them. An open request is also the floor for comps (a comp can’t land below it, ADR-0161 / #544), so a stale row will block staff from comping the tenant to a lower tier until it is cancelled. Cancelling never changes entitlement.
  • Refunds / disputes / chargebacks: manual today — coordinate the refund on the original payment channel and downgrade the plan as in mistaken activation. A proper refund/credit system lands with a live processor (own ADR).

  • Amounts come from PLAN_META.thb (+ seat add-ons); annual = 12 months at the 20%-off rate (ADR-0038). For Enterprise / negotiated deals enter the agreed figure on the New-request form, and set the tenant’s negotiated MRR via quotasJson.mrrThb so the FinOps margin view (ADR-0130, /admin/finops) reflects reality.
  • Idempotent: Mark paid on an already-activated request is a safe no-op.
  • Future channels (PromptPay auto-verify, Stripe) plug in behind the same payment_request + applyPlanChange seam (ADR-0132) — this runbook’s “collect
    • Mark paid” steps become automatic per channel; the edge cases stay relevant.