Stripe 구독: 결제, 고객 포털, 멱등 웹훅(Drizzle)
코드파일 9개컨텍스트약 633토큰MCP stripe검사 통과
설치
$
genpm add @core/billing포함 내용
- src/lib/billing/에 소스 코드, 파일 9개. (15.7kB)
- src/lib/billing/AGENTS.md에 AI 규칙, 그리고 IDE 규칙 파일.
- .env.example에 추가되는 환경 변수: STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET.
- @core/auth, @core/db을(를) 자동으로 해결합니다.
README
이 패키지에는 README가 없습니다.
약 633토큰→ src/lib/billing/AGENTS.md→ .cursor/rules/genpm-core-billing.mdc
이것이 AI가 src/lib/billing에서 작업할 때 읽는 내용 그대로입니다. 그 외에는 컨텍스트에 아무것도 추가되지 않습니다.
@core/billing — rules for AI agents
Purpose
Stripe subscriptions: checkout, customer portal, an idempotent webhook handler that mirrors subscriptions into the
database, and hasEntitlement(user, feature) for gating. Stripe is the source of truth. No usage-based billing,
invoices UI or taxes logic (configure those in Stripe).
Map
index.ts— public API:createCheckoutSession,createPortalSession,handleStripeWebhook,hasEntitlement,activeSubscriptions.plans.ts— your config: Stripe price id → features. Edit this file.schema.ts—billing_customers,subscriptions,stripe_events. Depends on../authand../db.adapters/hono.ts,adapters/next.ts—POST /billing/checkout,/billing/portal,/billing/webhook.
Integration
- Env:
STRIPE_SECRET_KEY,STRIPE_WEBHOOK_SECRET. Use test-mode keys in development. - Use the Stripe MCP server (if the user enabled it) to list real products and prices; otherwise ask the user for price ids.
Put them in
plans.ts:FEATURES_BY_PRICE['price_123'] = ['pro']. - Generate and apply migrations (see
src/lib/db/AGENTS.md). - Mount routes after
@core/auth'ssessionMiddleware:- Hono:
app.route('/billing', billingRoutes())from./lib/billing/adapters/hono.js. - Next.js:
app/billing/{checkout,portal,webhook}/route.tsexportingcheckoutRoute/portalRoute/webhookRouteasPOST. Delete the adapter of the framework you don't use.
- Hono:
- Webhook endpoint in Stripe:
<origin>/billing/webhookwith eventscheckout.session.completedandcustomer.subscription.created|updated|deleted. Locally:stripe listen --forward-to localhost:3000/billing/webhook. - Gate features:
if (!(await hasEntitlement(user, 'pro'))) return c.json({ error: 'upgrade' }, 402). - Frontend:
POST /billing/checkout {priceId}returns{url}; redirect the browser there.
Conventions
- Read subscription state from the database (
hasEntitlement,activeSubscriptions), never from Stripe on each request. - All Stripe writes go through this module; keep amounts and prices in Stripe, not in code.
- The webhook must receive the raw body; do not put a JSON body parser in front of it.
Don't
- Don't skip webhook signature verification or process events outside
handleStripeWebhook. - Don't grant access from the checkout success page: wait for the webhook.
- Don't log card data, full webhook payloads or
STRIPE_SECRET_KEY. - Don't use live keys in tests or with the MCP server unless the user asks.
# @core/billing — rules for AI agents
## Purpose
Stripe subscriptions: checkout, customer portal, an idempotent webhook handler that mirrors subscriptions into the
database, and `hasEntitlement(user, feature)` for gating. Stripe is the source of truth. No usage-based billing,
invoices UI or taxes logic (configure those in Stripe).
## Map
- `index.ts` — public API: `createCheckoutSession`, `createPortalSession`, `handleStripeWebhook`, `hasEntitlement`, `activeSubscriptions`.
- `plans.ts` — **your config**: Stripe price id → features. Edit this file.
- `schema.ts` — `billing_customers`, `subscriptions`, `stripe_events`. Depends on `../auth` and `../db`.
- `adapters/hono.ts`, `adapters/next.ts` — `POST /billing/checkout`, `/billing/portal`, `/billing/webhook`.
## Integration
1. Env: `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`. Use test-mode keys in development.
2. Use the Stripe MCP server (if the user enabled it) to list real products and prices; otherwise ask the user for price ids.
Put them in `plans.ts`: `FEATURES_BY_PRICE['price_123'] = ['pro']`.
3. Generate and apply migrations (see `src/lib/db/AGENTS.md`).
4. Mount routes after `@core/auth`'s `sessionMiddleware`:
- Hono: `app.route('/billing', billingRoutes())` from `./lib/billing/adapters/hono.js`.
- Next.js: `app/billing/{checkout,portal,webhook}/route.ts` exporting `checkoutRoute`/`portalRoute`/`webhookRoute` as `POST`.
Delete the adapter of the framework you don't use.
5. Webhook endpoint in Stripe: `<origin>/billing/webhook` with events `checkout.session.completed` and
`customer.subscription.created|updated|deleted`. Locally: `stripe listen --forward-to localhost:3000/billing/webhook`.
6. Gate features: `if (!(await hasEntitlement(user, 'pro'))) return c.json({ error: 'upgrade' }, 402)`.
7. Frontend: `POST /billing/checkout {priceId}` returns `{url}`; redirect the browser there.
## Conventions
- Read subscription state from the database (`hasEntitlement`, `activeSubscriptions`), never from Stripe on each request.
- All Stripe writes go through this module; keep amounts and prices in Stripe, not in code.
- The webhook must receive the raw body; do not put a JSON body parser in front of it.
## Don't
- Don't skip webhook signature verification or process events outside `handleStripeWebhook`.
- Don't grant access from the checkout success page: wait for the webhook.
- Don't log card data, full webhook payloads or `STRIPE_SECRET_KEY`.
- Don't use live keys in tests or with the MCP server unless the user asks.
.genpmignore 적용 후 주입될 정확한 트리입니다. 고정 대상:
- 서버
- stripe
- 명령
- npx -y @stripe/mcp
- env
- STRIPE_SECRET_KEY
| 버전 | 커밋 | 게시일 | 검사 |
|---|---|---|---|
| 1.0.0 | a8a5ac1 | 3시간 전 | ✔ 검사 통과 |
- 제안됨
- GenPM은 npm 명령을 제안하고, 동의한 경우에만 실행합니다.
- 검사
- 검사 통과 · 문제 0건
- 커밋
- v1.0.0 → a8a5ac11d1027ce8ae3bad366f625d135bdf6246 · 가져온 뒤 검증됨
- 스크립트
- 없음. GenPM은 패키지 코드를 절대 실행하지 않습니다.
- 라이선스
- MIT
- 신고
- 문제가 있나요?