ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
Student dashboard and subscription billing engine: Stripe (test mode), failed-payment grace and recovery, idempotent webhooks, and an outbox to n8n.
翻译准备中
正在加载完整 README。
查看完整 README ↗A student platform for an online Islamic-studies academy: a premium student dashboard on top of a payment, access and messaging engine that stays correct when payments fail, webhooks repeat, and services go down.
Built as proof of work for a Full-Stack Developer & Automation Engineer role at an online Islamic academy. It runs under a neutral brand, "Demo Academy", styled to fit that academy's design language. It's a concept and isn't affiliated with any academy. Everything runs in Stripe test mode.
Live demo: https://academy-access-engine.vercel.app (Stripe test mode: pay with card 4242 4242 4242 4242, any future date, any CVC)
An academy that sells monthly subscriptions and lifetime access has to get a few unglamorous things exactly right:
| Area | Behaviour |
|---|---|
| Plans | Arabic $30/month, Precision in Knowledge $15/month, Lifetime $999 one-time. Foundations of Knowledge is free with any paid plan. Regional coupon REGION40. |
| Access | Decided only on the server from an entitlements table. A locked lesson never sends its video URL to the browser. The first lesson of each programme is a free preview. |
| Checkout | Stripe Checkout. Double purchases are blocked, and a double click can't create two Stripe customers. Access is granted only by the webhook, never by the success page. |
| Failed payment | A 7-day grace period counted from the first failure (retries don't extend it), a banner with "Update your card" (Stripe Billing Portal), automatic restore when paid, and revocation by a daily job when grace runs out. |
| Cancellation | Reason → one tailored offer (30% off the next payment, or a 30-day billing pause) → or cancel at period end. Every answer is recorded, each offer is usable once, and "Keep my plan" undoes it. |
| Refunds | A full refund of a lifetime purchase revokes exactly what it paid for. Partial refunds change nothing. |
| Messages | Every change writes an outbox event in the same transaction. A dispatcher sends it, signed, to n8n, which verifies it, drops duplicates and sends the email through Resend. It also nudges students who enrolled 3+ days ago and haven't opened a lesson. |
| Admin | An overview with MRR over time, payments by week, signups and active learners, plus a Needs attention list (failed payments, pending cancellations, paying students who've gone quiet). Also paying students, students in grace, revenue at risk, revenue recovered (30 days), failed payments, a webhook log, and the outbox with Retry. |
| Pricing | Locked lesson (no video URL sent) |
|---|---|
| Cancel flow | Admin overview |
flowchart TB
student([Student]) --> app
subgraph app [Next.js app on Vercel]
pages[Dashboard, pricing,<br/>lessons, billing, admin]
hooks[Stripe webhook route]
lib[Billing handlers,<br/>access rules]
dispatch[Outbox dispatcher]
cron[Daily crons:<br/>expire grace, retry outbox]
end
pages -- email + password,<br/>session cookies --> auth[(Supabase Auth)]
stripe[(Stripe<br/>test mode)] -- signed events --> hooks
pages -- Checkout / Portal --> stripe
hooks --> lib
lib -- "one transaction:<br/>state + processed_events + outbox" --> db[(Supabase Postgres<br/>RLS on)]
pages --> db
cron --> db
dispatch -- reads due events --> db
dispatch -- "HMAC-signed POST,<br/>retries with backoff" --> n8n[n8n workflows]
n8n -- email --> resend[(Resend)]
n8n -. on failure .-> alert[Error-alert workflow]
stateDiagram-v2
[*] --> Active: checkout.session.completed / invoice.paid
Active --> Grace: invoice.payment_failed (7 days from first failure)
Grace --> Active: invoice.paid (card updated, retry succeeds)
Grace --> Revoked: grace ends (daily job) or Stripe cancels
Active --> CancelScheduled: student cancels (keeps access)
CancelScheduled --> Active: student clicks Keep my plan
CancelScheduled --> Revoked: period ends (customer.subscription.deleted)
Revoked --> [*]
以上为 README 的开头部分。其余内容请阅读原文。 查看完整 README ↗