feat: deferred payments & installment plans — pledge = promise to pay on a date

CORE PRODUCT SHIFT:
A pledge is now a promise to pay on a future date, not just 'pay now'.

NEW FLOW: Amount → Schedule → Payment/Identity → Confirmation

SCHEDULE STEP (/p/[token] step 1):
- 'Pay right now' — existing card/DD/bank flow
- 'Pay on a specific date' — calendar picker with smart suggestions
  (This Friday, End of month, Payday 1st, In 2 weeks, In 1 month)
- 'Split into monthly payments' — 2/3/4/6/12 month installment plans
  with per-installment breakdown and date schedule

SCHEMA CHANGES:
- Pledge.dueDate — when the donor promises to pay (null = now)
- Pledge.planId — groups installment pledges together
- Pledge.installmentNumber / installmentTotal — e.g. 2 of 4
- Pledge.reminderSentForDueDate — tracking flag
- New indexes on dueDate+status and planId

INSTALLMENT PLANS:
- Creates N linked Pledge records with shared planId
- Each installment gets its own reference, due date, reminders
- Reminders: 2 days before, on due date, 3 days after, 10 days after
- WhatsApp receipt shows full plan summary

DEFERRED SINGLE PLEDGES:
- Reminders anchored to due date, not creation date
- 'Pay on date' → reminders: 2 days before, on day, +3d nudge, +10d final
- WhatsApp preferred when phone number provided

DASHBOARD:
- API returns dueDate, planId, installment info for each pledge
- Confirmation step shows schedule details for deferred pledges
This commit is contained in:
2026-03-03 04:43:19 +08:00
parent c6e7e4f01e
commit 250221b530
8 changed files with 676 additions and 42 deletions

View File

@@ -12,6 +12,10 @@ interface PledgeRow {
donorEmail: string | null
donorPhone: string | null
giftAid: boolean
dueDate: Date | null
planId: string | null
installmentNumber: number | null
installmentTotal: number | null
createdAt: Date
paidAt: Date | null
event: { name: string }
@@ -139,6 +143,11 @@ export async function GET(request: NextRequest) {
source: p.qrSource?.label || null,
volunteerName: p.qrSource?.volunteerName || null,
giftAid: p.giftAid,
dueDate: p.dueDate,
planId: p.planId,
installmentNumber: p.installmentNumber,
installmentTotal: p.installmentTotal,
isDeferred: !!p.dueDate,
createdAt: p.createdAt,
paidAt: p.paidAt,
nextReminder: p.reminders