@ysk-kit/cli 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/add-capability.js +2 -2
- package/dist/add-capability.js.map +1 -1
- package/dist/add-module.js +7 -7
- package/dist/add-module.js.map +1 -1
- package/dist/apply-capability.d.ts +1 -1
- package/dist/apply-capability.d.ts.map +1 -1
- package/dist/apply-capability.js +3 -3
- package/dist/apply-capability.js.map +1 -1
- package/dist/capability-catalog.d.ts +1 -1
- package/dist/capability-catalog.d.ts.map +1 -1
- package/dist/capability-catalog.js +11 -3
- package/dist/capability-catalog.js.map +1 -1
- package/dist/capability-patches.d.ts.map +1 -1
- package/dist/capability-patches.js +29 -46
- package/dist/capability-patches.js.map +1 -1
- package/dist/check-agent-guidance.d.ts.map +1 -1
- package/dist/check-agent-guidance.js +2 -1
- package/dist/check-agent-guidance.js.map +1 -1
- package/dist/check-agent.d.ts.map +1 -1
- package/dist/check-agent.js +2 -1
- package/dist/check-agent.js.map +1 -1
- package/dist/doctor.js +3 -3
- package/dist/doctor.js.map +1 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +6 -1
- package/dist/help.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +114 -73
- package/dist/index.js.map +1 -1
- package/dist/plan.d.ts +18 -0
- package/dist/plan.d.ts.map +1 -1
- package/dist/plan.js +78 -0
- package/dist/plan.js.map +1 -1
- package/dist/upgrade.js +1 -1
- package/dist/upgrade.js.map +1 -1
- package/package.json +4 -4
- package/templates/agent/nested/api.md +1 -0
- package/templates/agent/nested/clients.md +1 -0
- package/templates/agent/nested/contracts.md +1 -0
- package/templates/agent/rules/api-modules.mdc +1 -1
- package/templates/agent/rules/desktop.mdc +9 -0
- package/templates/agent/rules/security.mdc +9 -0
- package/templates/agent/rules/tests.mdc +9 -0
- package/templates/agent/rules/ui.mdc +9 -0
- package/templates/agent/skills/db-migration/SKILL.md +17 -0
- package/templates/agent/skills/desktop-electron/SKILL.md +18 -0
- package/templates/agent/skills/security-review/SKILL.md +20 -0
- package/templates/agent/skills/test-plan/SKILL.md +10 -0
- package/templates/agent/skills/ui-design/SKILL.md +9 -0
- package/templates/agent/skills/ui-review/SKILL.md +9 -0
- package/templates/agent/skills/webhook-handling/SKILL.md +18 -0
- package/templates/agent/skills/write-tests/SKILL.md +9 -0
- package/templates/agent/ysk-kit.mdc +1 -1
- package/templates/capabilities/billing/apps/api/src/modules/billing/application/billing-service.test.ts +56 -0
- package/templates/capabilities/billing/apps/api/src/modules/billing/application/billing-service.ts +71 -18
- package/templates/capabilities/billing/apps/api/src/modules/billing/domain/stripe-webhook-event.ts +12 -0
- package/templates/capabilities/billing/apps/api/src/modules/billing/domain/webhook-event-repository.ts +19 -0
- package/templates/capabilities/billing/apps/api/src/modules/billing/infra/billing.app.test.ts +172 -0
- package/templates/capabilities/billing/apps/api/src/modules/billing/infra/memory-webhook-event-repository.ts +31 -0
- package/templates/capabilities/billing/apps/api/src/modules/billing/infra/prisma-webhook-event-repository.ts +39 -0
- package/templates/capabilities/billing/apps/api/src/modules/billing/infra/stripe-billing.test.ts +3 -0
- package/templates/capabilities/billing/apps/api/src/modules/billing/infra/stripe-billing.ts +34 -7
- package/templates/capabilities/billing/packages/contracts/src/api/billing.ts +2 -2
- package/templates/capabilities/billing/packages/contracts/src/dto/billing.ts +1 -1
- package/templates/capabilities/billing/packages/sdk/src/resources/billing.ts +1 -1
- package/templates/capabilities/llm/apps/api/src/modules/llm/application/llm-service.test.ts +58 -0
- package/templates/capabilities/llm/apps/api/src/modules/llm/application/llm-service.ts +63 -36
- package/templates/capabilities/llm/apps/api/src/modules/llm/domain/usage-repository.ts +1 -0
- package/templates/capabilities/llm/apps/api/src/modules/llm/infra/llm.app.test.ts +49 -0
- package/templates/capabilities/llm/apps/api/src/modules/llm/infra/memory-usage-repository.ts +6 -3
- package/templates/capabilities/llm/apps/api/src/modules/llm/infra/prisma-usage-repository.ts +5 -0
- package/templates/capabilities/llm/packages/contracts/src/api/llm.ts +2 -2
- package/templates/capabilities/llm/packages/contracts/src/dto/llm.ts +8 -2
- package/templates/capabilities/llm/packages/sdk/src/resources/llm.ts +2 -2
- package/templates/capabilities/push/packages/contracts/src/api/devices.ts +2 -2
- package/templates/capabilities/push/packages/sdk/src/resources/devices.ts +1 -1
- package/templates/capabilities/team/packages/contracts/src/api/organizations.ts +2 -2
- package/templates/capabilities/team/packages/contracts/src/dto/organization.ts +1 -1
- package/templates/capabilities/team/packages/sdk/src/resources/organizations.ts +1 -1
- package/templates/module/api.ts.tmpl +3 -3
- package/templates/module/dto.ts.tmpl +1 -1
- package/templates/module/sdk-resource.ts.tmpl +1 -1
package/dist/upgrade.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../src/upgrade.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,UAAU,EACV,SAAS,EACT,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../src/upgrade.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ysk-kit/cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"ysk-kit": "./dist/index.js",
|
|
7
7
|
"yskk": "./dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@ysk-kit/api-http": "1.2.
|
|
11
|
-
"@ysk-kit/contracts": "1.2.
|
|
10
|
+
"@ysk-kit/api-http": "1.2.2",
|
|
11
|
+
"@ysk-kit/contracts": "1.2.2",
|
|
12
12
|
"zod": "^4.6.5"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"start": "tsx src/index.ts",
|
|
38
38
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
39
39
|
"test": "vitest run --passWithNoTests",
|
|
40
|
-
"build": "tsc -p tsconfig.build.json"
|
|
40
|
+
"build": "tsc -p tsconfig.build.json && chmod +x dist/index.js"
|
|
41
41
|
},
|
|
42
42
|
"main": "./dist/index.js",
|
|
43
43
|
"types": "./dist/index.d.ts",
|
|
@@ -7,3 +7,4 @@ Local notes for `apps/api`. Root law: `AGENTS.md`.
|
|
|
7
7
|
- JSON routes return `{ ok: true, data }` / `{ ok: false, error }`.
|
|
8
8
|
- Envelope exceptions stay listed in the root guide. Do not add one silently.
|
|
9
9
|
- Tests use in-memory ports. Do not start Redis, Stripe, Twilio, FCM, Jaeger, or Grafana.
|
|
10
|
+
- Plan cases with `docs/skills/test-plan.md`. Write them with `docs/skills/write-tests.md`.
|
|
@@ -5,3 +5,4 @@ Local notes for this client app. Root law: `AGENTS.md`. The same rule applies to
|
|
|
5
5
|
- Call the API only through `@ysk-kit/sdk` (React Query via `@ysk-kit/web-sdk`).
|
|
6
6
|
- No raw `fetch` to kit paths. No `@prisma/client`, `@ysk-kit/db-prisma`, or `apps/api/src/generated`.
|
|
7
7
|
- Do not import `@ysk-kit/observability`, Express, Fastify, jobs, mail, push, or the AWS SDK.
|
|
8
|
+
- Screens: `docs/skills/ui-design.md`. Not done until `docs/skills/ui-review.md` is green.
|
|
@@ -7,3 +7,4 @@ Local notes for `@ysk-kit/contracts`. Root law: `AGENTS.md`.
|
|
|
7
7
|
- No TypeScript `enum`. Use `as const` + Zod.
|
|
8
8
|
- Do not import apps, Prisma, Express, Fastify, React, or BullMQ.
|
|
9
9
|
- After path changes run `pnpm gen:openapi`. Still call paths through `@ysk-kit/sdk`.
|
|
10
|
+
- Contract tests live in this package. Follow `docs/skills/write-tests.md`.
|
|
@@ -6,4 +6,4 @@ alwaysApply: false
|
|
|
6
6
|
|
|
7
7
|
Local reminder. Law: `AGENTS.md` (also `apps/api/AGENTS.md`).
|
|
8
8
|
|
|
9
|
-
Prisma stays in `src/modules/*/infra` and shared `src/infra`. Domain and application do not import Express, Fastify, Prisma, React, or BullMQ. JSON uses `{ ok: true, data }` / `{ ok: false, error }`. Tests use in-memory ports.
|
|
9
|
+
Prisma stays in `src/modules/*/infra` and shared `src/infra`. Domain and application do not import Express, Fastify, Prisma, React, or BullMQ. JSON uses `{ ok: true, data }` / `{ ok: false, error }`. Tests use in-memory ports. Schema changes: `docs/skills/db-migration.md`.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Electron desktop — isolation, CSP, IPC, safeStorage
|
|
3
|
+
globs: apps/desktop/**/*
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Local reminder. Law: `AGENTS.md`.
|
|
8
|
+
|
|
9
|
+
Procedure: `docs/skills/desktop-electron.md`. Keep `contextIsolation` and `nodeIntegration: false`. Add sandbox, CSP, navigation guards, IPC `senderFrame` checks. `safeStorage` with no plaintext fallback. Renderer still uses `@ysk-kit/sdk` only.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Security review — authz, tenancy, secrets, webhooks
|
|
3
|
+
globs: apps/api/**/*,packages/logger/**/*,packages/config/**/*
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Local reminder. Law: `AGENTS.md`.
|
|
8
|
+
|
|
9
|
+
Procedure: `docs/skills/security-review.md`. Org-scoped use cases check membership in `application/` (template: `requireBiller`). Never log OTP, JWT, Stripe `sk_`, or webhook secrets. Inbound webhooks: `docs/skills/webhook-handling.md`.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Tests — in-memory ports, envelope, given/when/then
|
|
3
|
+
globs: "**/*.test.ts,**/*.test.tsx,**/*.spec.ts,apps/web/e2e/**/*"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Local reminder. Law: `AGENTS.md`.
|
|
8
|
+
|
|
9
|
+
Plan with `docs/skills/test-plan.md`, then write with `docs/skills/write-tests.md`. In-memory ports only. Assert `{ ok, data }` / `{ ok, error }`. Do not start Redis, Stripe, Twilio, FCM, Jaeger, or Grafana.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: UI — kit tokens, @ysk-kit/ui, ui-review before done
|
|
3
|
+
globs: "packages/ui/**/*,packages/ui-logic/**/*,apps/web/src/**/*.tsx,apps/admin/src/**/*.tsx,apps/mobile/src/**/*.tsx,apps/desktop/src/renderer/**/*.tsx"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Local reminder. Law: `AGENTS.md`.
|
|
8
|
+
|
|
9
|
+
Reuse `@ysk-kit/ui` and the zinc theme (`docs/skills/ui-design.md`). Do not invent a parallel design system. A screen is not done until `docs/skills/ui-review.md` is green. No raw `fetch`.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: db-migration
|
|
3
|
+
description: >
|
|
4
|
+
Apply Prisma 7.10 migrations in YSK Kit: review generated SQL, expand/contract
|
|
5
|
+
for breaking changes, backfills, never migrate reset without explicit consent.
|
|
6
|
+
Use when schema.prisma changes, after add-module --prisma or add <capability>
|
|
7
|
+
with Prisma, or the user says migrate / rename column / backfill / reset.
|
|
8
|
+
中文:資料庫遷移、改欄位、改名、回填、migrate reset。
|
|
9
|
+
Do not use for Prisma 8 commands or for application rules with no schema change.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Read `docs/skills/db-migration.md`. Law: `AGENTS.md`.
|
|
13
|
+
|
|
14
|
+
1. Pin Prisma 7.10: `migrate dev --create-only`, `migrate deploy`. No Prisma 8 CLI.
|
|
15
|
+
2. Read generated SQL. Breaking changes use expand/contract + backfill.
|
|
16
|
+
3. Update Prisma **and** memory repositories. Never `migrate reset` without this-turn consent.
|
|
17
|
+
Full steps: `docs/skills/db-migration.md`.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: desktop-electron
|
|
3
|
+
description: >
|
|
4
|
+
Harden the YSK Kit Electron desktop app: contextIsolation, sandbox, CSP,
|
|
5
|
+
navigation and new-window restrictions, IPC sender validation, safeStorage
|
|
6
|
+
with no plaintext fallback, auto-update signing.
|
|
7
|
+
Use when changing apps/desktop/src/main or preload, adding an IPC channel,
|
|
8
|
+
opening external URLs, filesystem access, auto-update, or packaging.
|
|
9
|
+
中文:Electron 安全、CSP、IPC、safeStorage、自動更新。
|
|
10
|
+
Do not use for renderer-only UI or web/admin token storage with no main-process change.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
Read `docs/skills/desktop-electron.md`. Law: `AGENTS.md`.
|
|
14
|
+
|
|
15
|
+
1. Keep contextIsolation, nodeIntegration false, sandbox, webSecurity. Add CSP.
|
|
16
|
+
2. Deny navigation / new windows by default; allowlist `https:` for openExternal.
|
|
17
|
+
3. Validate IPC `senderFrame`. `safeStorage` only — no plaintext token fallback.
|
|
18
|
+
Full steps: `docs/skills/desktop-electron.md`.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security-review
|
|
3
|
+
description: >
|
|
4
|
+
Review YSK Kit authz, tenant isolation, secrets, logging redaction, token
|
|
5
|
+
storage, webhook signatures, and LLM input against hexagonal modules and
|
|
6
|
+
the {ok,data}/{ok,error} envelope. Optional lightweight threat-model mode.
|
|
7
|
+
Use when the user asks for a security review or audit; the diff touches
|
|
8
|
+
auth, apikey, organizations, billing, files, crypto, webhooks, env/secrets,
|
|
9
|
+
or a new capability; or the plan's security section is not "none".
|
|
10
|
+
中文:安全審查、權限、跨租戶、密鑰、日誌遮罩、webhook 簽名。
|
|
11
|
+
Do not use for pure UI styling, docs typos, or generic dependency bumps.
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
Read `docs/skills/security-review.md`. Law: `AGENTS.md`.
|
|
15
|
+
|
|
16
|
+
1. Diff is the report scope; the repo is research. HIGH confidence only.
|
|
17
|
+
2. AuthZ in `application/` (membership + role). Cross-tenant memory tests.
|
|
18
|
+
3. No secrets in logs. Webhook signatures. Client token stores.
|
|
19
|
+
4. Threat-model sub-mode only when the user asks or a new trust boundary lands.
|
|
20
|
+
Full steps: `docs/skills/security-review.md`.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test-plan
|
|
3
|
+
description: >
|
|
4
|
+
Produce a risk-ranked YSK Kit test plan (given/when/then, fixtures, out of scope)
|
|
5
|
+
mapped to contract, memory-port, API, SDK, client, and Playwright layers.
|
|
6
|
+
Use when the user asks how to test a feature, what cases to cover, or for a QA
|
|
7
|
+
checklist before writing tests or shipping.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
Read `docs/skills/test-plan.md`. Law: `AGENTS.md`.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ui-design
|
|
3
|
+
description: >
|
|
4
|
+
Design and implement YSK Kit UI with existing tokens, @ysk-kit/ui, WCAG 2.2 AA,
|
|
5
|
+
forms mapped from the API envelope, and no generic AI-slop chrome.
|
|
6
|
+
Use when adding or changing web, admin, mobile, or desktop screens.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Read `docs/skills/ui-design.md`. Law: `AGENTS.md`.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ui-review
|
|
3
|
+
description: >
|
|
4
|
+
QA checklist before declaring YSK Kit UI work done: visual, a11y, responsive,
|
|
5
|
+
states, copy, performance — with Playwright, keyboard, and axe verification.
|
|
6
|
+
Use when finishing a screen or when the user asks to review UI / UX / a11y.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Read `docs/skills/ui-review.md`. Law: `AGENTS.md`.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: webhook-handling
|
|
3
|
+
description: >
|
|
4
|
+
Handle inbound webhooks in YSK Kit: verify signature on the raw body, ack
|
|
5
|
+
fast, idempotency by event id, out-of-order events, retries, replay tests.
|
|
6
|
+
Stripe is the main example; the same rules apply to other providers.
|
|
7
|
+
Use when adding or changing /v1/*/webhook, adding billing, or debugging
|
|
8
|
+
"paid but not activated" / duplicate fulfilments.
|
|
9
|
+
中文:webhook、驗簽、冪等、Stripe 回調、重送。
|
|
10
|
+
Do not use for outbound HTTP or a general security audit with no webhook route.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
Read `docs/skills/webhook-handling.md`. Law: `AGENTS.md`.
|
|
14
|
+
|
|
15
|
+
1. Mount raw body before JSON parsers (Express + Fastify). Verify signature.
|
|
16
|
+
2. Persist event id, return `{ ok: true, data: { received: true } }`, fulfil via jobs.
|
|
17
|
+
3. Do not trust event order. Replay tests in memory; never call Stripe in CI.
|
|
18
|
+
Full steps: `docs/skills/webhook-handling.md`.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: write-tests
|
|
3
|
+
description: >
|
|
4
|
+
Write professional YSK Kit tests: trophy per layer, in-memory ports, given/when/then,
|
|
5
|
+
deterministic fixtures, envelope and authz cases, exact Vitest/Playwright commands.
|
|
6
|
+
Use when adding tests, fixing a bug, or after test-plan.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Read `docs/skills/write-tests.md`. Law: `AGENTS.md`.
|
|
@@ -3,4 +3,4 @@ description: YSK Kit agent law — follow root AGENTS.md
|
|
|
3
3
|
alwaysApply: true
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Follow `AGENTS.md` at the repo root (Chinese: `AGENTS.zh.md`). Do not copy that file here. Procedures: `docs/skills/`. Plan first when required: `pnpm ysk-kit plan <slug>` → `docs/plans/<yyyy-mm-dd>-<slug>.md`. New HTTP features start with `pnpm ysk-kit add module <kebab-name> --prisma --web`. Verify with `pnpm layers && pnpm typecheck && pnpm test && pnpm gen:openapi && pnpm ysk-kit check agent`.
|
|
6
|
+
Follow `AGENTS.md` at the repo root (Chinese: `AGENTS.zh.md`). Do not copy that file here. Procedures: `docs/skills/`. Plan first when required: `pnpm ysk-kit plan <slug>` → `docs/plans/<yyyy-mm-dd>-<slug>.md`. New HTTP features start with `pnpm ysk-kit add module <kebab-name> --prisma --web`. Verify with `pnpm layers && pnpm typecheck && pnpm test && pnpm gen:openapi && pnpm ysk-kit check agent`. Security / Prisma / webhooks / Electron: `docs/skills/security-review.md`, `db-migration.md`, `webhook-handling.md`, `desktop-electron.md`.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { createMemoryOrganizationRepository } from '../../organizations/infra/memory-organization-repository';
|
|
3
|
+
import { createLogBilling } from '../infra/log-billing';
|
|
4
|
+
import { createMemorySubscriptionRepository } from '../infra/memory-subscription-repository';
|
|
5
|
+
import { createMemoryWebhookEventRepository } from '../infra/memory-webhook-event-repository';
|
|
6
|
+
import { createBillingService } from './billing-service';
|
|
7
|
+
|
|
8
|
+
describe('createBillingService webhooks', () => {
|
|
9
|
+
it('activates once and skips a replay plus a stale event', async () => {
|
|
10
|
+
const orgs = createMemoryOrganizationRepository();
|
|
11
|
+
const subscriptions = createMemorySubscriptionRepository();
|
|
12
|
+
const service = createBillingService({
|
|
13
|
+
subscriptions,
|
|
14
|
+
billing: createLogBilling(),
|
|
15
|
+
orgs,
|
|
16
|
+
webhookEvents: createMemoryWebhookEventRepository(),
|
|
17
|
+
});
|
|
18
|
+
const owner = await orgs.createWithOwner({ name: 'Pay', ownerUserId: 'user-1' });
|
|
19
|
+
await service.handleStripeEvent({
|
|
20
|
+
id: 'evt_new',
|
|
21
|
+
type: 'checkout.session.completed',
|
|
22
|
+
created: 100,
|
|
23
|
+
data: {
|
|
24
|
+
object: {
|
|
25
|
+
customer: 'cus_1',
|
|
26
|
+
metadata: { organizationId: owner.id, planCode: 'pro', seatCount: '4' },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
await service.handleStripeEvent({
|
|
31
|
+
id: 'evt_new',
|
|
32
|
+
type: 'checkout.session.completed',
|
|
33
|
+
created: 100,
|
|
34
|
+
data: {
|
|
35
|
+
object: {
|
|
36
|
+
customer: 'cus_1',
|
|
37
|
+
metadata: { organizationId: owner.id, planCode: 'pro', seatCount: '4' },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
await service.handleStripeEvent({
|
|
42
|
+
id: 'evt_old',
|
|
43
|
+
type: 'checkout.session.completed',
|
|
44
|
+
created: 50,
|
|
45
|
+
data: {
|
|
46
|
+
object: {
|
|
47
|
+
customer: 'cus_1',
|
|
48
|
+
metadata: { organizationId: owner.id, planCode: 'pro', seatCount: '1' },
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
const row = await subscriptions.findByOrganizationId(owner.id);
|
|
53
|
+
expect(row?.seatCount).toBe(4);
|
|
54
|
+
expect(row?.status).toBe('active');
|
|
55
|
+
});
|
|
56
|
+
});
|
package/templates/capabilities/billing/apps/api/src/modules/billing/application/billing-service.ts
CHANGED
|
@@ -3,7 +3,9 @@ import { BILLING_PLANS, orgRoleCan } from '@ysk-kit/contracts';
|
|
|
3
3
|
import { AppError } from '@ysk-kit/domain-kernel';
|
|
4
4
|
import type { IOrganizationRepository } from '../../organizations/domain/organization-repository';
|
|
5
5
|
import type { IBillingPort } from '../domain/billing-port';
|
|
6
|
+
import type { StripeWebhookEvent } from '../domain/stripe-webhook-event';
|
|
6
7
|
import type { ISubscriptionRepository } from '../domain/subscription-repository';
|
|
8
|
+
import type { IProcessedWebhookRepository } from '../domain/webhook-event-repository';
|
|
7
9
|
|
|
8
10
|
const PERIOD_MS = 1000 * 60 * 60 * 24 * 30;
|
|
9
11
|
|
|
@@ -30,6 +32,7 @@ export const createBillingService = (opts: {
|
|
|
30
32
|
subscriptions: ISubscriptionRepository;
|
|
31
33
|
billing: IBillingPort;
|
|
32
34
|
orgs: IOrganizationRepository;
|
|
35
|
+
webhookEvents: IProcessedWebhookRepository;
|
|
33
36
|
now?: () => Date;
|
|
34
37
|
}) => {
|
|
35
38
|
const now = () => opts.now?.() ?? new Date();
|
|
@@ -44,6 +47,25 @@ export const createBillingService = (opts: {
|
|
|
44
47
|
return org;
|
|
45
48
|
};
|
|
46
49
|
|
|
50
|
+
const activate = async (
|
|
51
|
+
organizationId: string,
|
|
52
|
+
planCode: SubscriptionDto['planCode'],
|
|
53
|
+
customerId?: string,
|
|
54
|
+
seatCount = 1,
|
|
55
|
+
) => {
|
|
56
|
+
if (planCode === 'free') return;
|
|
57
|
+
const org = await opts.orgs.findById(organizationId);
|
|
58
|
+
if (!org) throw new AppError('NOT_FOUND');
|
|
59
|
+
await opts.subscriptions.upsert({
|
|
60
|
+
organizationId,
|
|
61
|
+
planCode,
|
|
62
|
+
status: 'active',
|
|
63
|
+
currentPeriodEnd: new Date(now().getTime() + PERIOD_MS),
|
|
64
|
+
seatCount,
|
|
65
|
+
});
|
|
66
|
+
if (customerId) await opts.orgs.setStripeCustomerId(organizationId, customerId);
|
|
67
|
+
};
|
|
68
|
+
|
|
47
69
|
return {
|
|
48
70
|
plans: () => BILLING_PLANS,
|
|
49
71
|
subscription: async (userId: string, organizationId: string): Promise<SubscriptionDto> => {
|
|
@@ -64,24 +86,7 @@ export const createBillingService = (opts: {
|
|
|
64
86
|
invoiceId,
|
|
65
87
|
});
|
|
66
88
|
},
|
|
67
|
-
activate
|
|
68
|
-
organizationId: string,
|
|
69
|
-
planCode: SubscriptionDto['planCode'],
|
|
70
|
-
customerId?: string,
|
|
71
|
-
seatCount = 1,
|
|
72
|
-
) => {
|
|
73
|
-
if (planCode === 'free') return;
|
|
74
|
-
const org = await opts.orgs.findById(organizationId);
|
|
75
|
-
if (!org) throw new AppError('NOT_FOUND');
|
|
76
|
-
await opts.subscriptions.upsert({
|
|
77
|
-
organizationId,
|
|
78
|
-
planCode,
|
|
79
|
-
status: 'active',
|
|
80
|
-
currentPeriodEnd: new Date(now().getTime() + PERIOD_MS),
|
|
81
|
-
seatCount,
|
|
82
|
-
});
|
|
83
|
-
if (customerId) await opts.orgs.setStripeCustomerId(organizationId, customerId);
|
|
84
|
-
},
|
|
89
|
+
activate,
|
|
85
90
|
portal: async (userId: string, organizationId: string, returnUrl: string) => {
|
|
86
91
|
const org = await requireBiller(userId, organizationId);
|
|
87
92
|
const customerId = org.stripeCustomerId;
|
|
@@ -120,6 +125,54 @@ export const createBillingService = (opts: {
|
|
|
120
125
|
}
|
|
121
126
|
return { url: result.url };
|
|
122
127
|
},
|
|
128
|
+
handleStripeEvent: async (event: StripeWebhookEvent) => {
|
|
129
|
+
if (!event.id) throw new AppError('VALIDATION_FAILED', 'Stripe event id required');
|
|
130
|
+
const organizationId = event.data.object.metadata?.organizationId;
|
|
131
|
+
const eventCreatedAt = new Date((Number.isFinite(event.created) ? event.created : 0) * 1000);
|
|
132
|
+
const claim = await opts.webhookEvents.tryClaim({
|
|
133
|
+
provider: 'stripe',
|
|
134
|
+
eventId: event.id,
|
|
135
|
+
eventType: event.type,
|
|
136
|
+
eventCreatedAt,
|
|
137
|
+
...(organizationId ? { organizationId } : {}),
|
|
138
|
+
});
|
|
139
|
+
if (claim === 'duplicate') return { received: true as const };
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
if (organizationId) {
|
|
143
|
+
const latest = await opts.webhookEvents.maxEventCreatedAt(
|
|
144
|
+
'stripe',
|
|
145
|
+
organizationId,
|
|
146
|
+
event.id,
|
|
147
|
+
);
|
|
148
|
+
if (latest && eventCreatedAt < latest) return { received: true as const };
|
|
149
|
+
}
|
|
150
|
+
if (event.type === 'checkout.session.completed') {
|
|
151
|
+
const planCode = event.data.object.metadata?.planCode;
|
|
152
|
+
const seatRaw = Number(event.data.object.metadata?.seatCount ?? '1');
|
|
153
|
+
const seatCount = Number.isFinite(seatRaw) && seatRaw >= 1 ? Math.floor(seatRaw) : 1;
|
|
154
|
+
if (organizationId && (planCode === 'pro' || planCode === 'free')) {
|
|
155
|
+
const customer = event.data.object.customer;
|
|
156
|
+
await activate(
|
|
157
|
+
organizationId,
|
|
158
|
+
planCode,
|
|
159
|
+
typeof customer === 'string' ? customer : undefined,
|
|
160
|
+
seatCount,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return { received: true as const };
|
|
165
|
+
} catch (error) {
|
|
166
|
+
if (
|
|
167
|
+
error instanceof AppError &&
|
|
168
|
+
(error.code === 'NOT_FOUND' || error.code === 'VALIDATION_FAILED')
|
|
169
|
+
) {
|
|
170
|
+
return { received: true as const };
|
|
171
|
+
}
|
|
172
|
+
await opts.webhookEvents.release('stripe', event.id);
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
},
|
|
123
176
|
cancel: async (userId: string, organizationId: string) => {
|
|
124
177
|
await requireBiller(userId, organizationId);
|
|
125
178
|
const row = await opts.subscriptions.findByOrganizationId(organizationId);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type ProcessedWebhookClaim = 'claimed' | 'duplicate';
|
|
2
|
+
|
|
3
|
+
export type ProcessedWebhookRecord = {
|
|
4
|
+
provider: string;
|
|
5
|
+
eventId: string;
|
|
6
|
+
eventType: string;
|
|
7
|
+
eventCreatedAt: Date;
|
|
8
|
+
organizationId?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export interface IProcessedWebhookRepository {
|
|
12
|
+
tryClaim(input: ProcessedWebhookRecord): Promise<ProcessedWebhookClaim>;
|
|
13
|
+
maxEventCreatedAt(
|
|
14
|
+
provider: string,
|
|
15
|
+
organizationId: string,
|
|
16
|
+
exceptEventId: string,
|
|
17
|
+
): Promise<Date | null>;
|
|
18
|
+
release(provider: string, eventId: string): Promise<void>;
|
|
19
|
+
}
|