@h-ai/cli 0.1.0-alpha.65 → 0.1.0-alpha.67
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/README.en.md +66 -251
- package/README.ja.md +67 -252
- package/README.md +66 -251
- package/dist/{chunk-47V5OT22.js → chunk-7DSURTJ4.js} +32 -115
- package/dist/chunk-7DSURTJ4.js.map +1 -0
- package/dist/{chunk-IPG3QNYK.js → chunk-AOQ4N657.js} +212 -8
- package/dist/chunk-AOQ4N657.js.map +1 -0
- package/dist/cli-config-templates-6AHNUGBN.js +5 -0
- package/dist/{cli-config-templates-SJ2MBIR7.js.map → cli-config-templates-6AHNUGBN.js.map} +1 -1
- package/dist/{cli-skill-templates-A5VIR5J6.js → cli-skill-templates-FC3QRKDO.js} +3 -3
- package/dist/{cli-skill-templates-A5VIR5J6.js.map → cli-skill-templates-FC3QRKDO.js.map} +1 -1
- package/dist/index.js +644 -391
- package/dist/index.js.map +1 -1
- package/dist/{src-6PPBKQ6Q.js → src-2V6VP4I5.js} +972 -520
- package/dist/src-2V6VP4I5.js.map +1 -0
- package/package.json +8 -8
- package/templates/README.md +48 -145
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +1 -1
- package/templates/apps/api/.dockerignore.hbs +18 -12
- package/templates/apps/api/.gitignore.hbs +9 -1
- package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +28 -7
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -3
- package/templates/apps/api/hai.build.json.hbs +1 -0
- package/templates/apps/api/images/app.Dockerfile.hbs +35 -0
- package/templates/apps/api/package.json.hbs +10 -8
- package/templates/apps/fullstack/.dockerignore.hbs +18 -14
- package/templates/apps/fullstack/.gitignore.hbs +9 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +23 -8
- package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +0 -2
- package/templates/apps/fullstack/apps/{{projectName}}-app/public/runtime-config.js.hbs +4 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +3 -3
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +25 -10
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +2 -3
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/lib.rs.hbs +73 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +2 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +2 -2
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +16 -6
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +24 -10
- package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +0 -2
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +3 -3
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +12 -1
- package/templates/apps/fullstack/hai.build.json.hbs +7 -0
- package/templates/apps/fullstack/images/app.Dockerfile.hbs +36 -0
- package/templates/apps/fullstack/images/web.Dockerfile.hbs +34 -0
- package/templates/apps/fullstack/package.json.hbs +7 -12
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +28 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +0 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +33 -12
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +2 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +2 -2
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +2 -2
- package/templates/apps/mobile-app/index.html.hbs +1 -0
- package/templates/apps/mobile-app/public/runtime-config.js +1 -0
- package/templates/apps/mobile-app/src/App.svelte.hbs +2 -2
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +2 -1
- package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +3 -2
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +2 -2
- package/templates/apps/website/src/routes/+layout.svelte.hbs +3 -3
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +1 -1
- package/templates/base/.dockerignore.hbs +18 -13
- package/templates/base/.gitignore.hbs +9 -1
- package/templates/base/e2e/app.spec.ts.hbs +10 -0
- package/templates/base/hai.build.json.hbs +1 -0
- package/templates/base/images/app.Dockerfile.hbs +64 -0
- package/templates/base/{docker → images/web}/nginx.conf.hbs +6 -0
- package/templates/base/package.json.hbs +20 -19
- package/templates/base/svelte.config.js.hbs +2 -0
- package/templates/base/vite.config.ts.hbs +9 -3
- package/templates/shared/.gitattributes +11 -0
- package/templates/shared/container-runtime-config.sh +30 -0
- package/templates/shared/run-e2e.mjs +65 -0
- package/templates/shared/vite-runtime-config.ts +54 -0
- package/templates/skills/AGENTS.md +3 -3
- package/templates/skills/bridges/admin/AGENTS.md +1 -1
- package/templates/skills/bridges/api/AGENTS.md +1 -1
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +1 -1
- package/templates/skills/bridges/h5/AGENTS.md +1 -1
- package/templates/skills/bridges/mobile-app/AGENTS.md +1 -1
- package/templates/skills/bridges/website/AGENTS.md +1 -1
- package/templates/skills/hai-api-client/SKILL.md +14 -161
- package/templates/skills/hai-api-client/reference.md +166 -0
- package/templates/skills/hai-api-contract/SKILL.md +15 -273
- package/templates/skills/hai-api-contract/reference.md +278 -0
- package/templates/skills/hai-app-create/SKILL.md +2 -2
- package/templates/skills/hai-app-create/development.md +6 -4
- package/templates/skills/hai-app-create/reference.md +5 -4
- package/templates/skills/hai-app-review/SKILL.md +1 -1
- package/templates/skills/hai-app-tests/SKILL.md +4 -3
- package/templates/skills/hai-app-tests/reference.md +2 -2
- package/templates/skills/hai-app-tests/validation.md +31 -0
- package/templates/skills/hai-audit/SKILL.md +17 -142
- package/templates/skills/hai-audit/reference.md +145 -0
- package/templates/skills/hai-build/SKILL.md +24 -3
- package/templates/skills/hai-build/reference.md +10 -9
- package/templates/skills/hai-cache/SKILL.md +15 -182
- package/templates/skills/hai-cache/reference.md +187 -0
- package/templates/skills/hai-capacitor/SKILL.md +4 -3
- package/templates/skills/hai-capacitor/reference.md +6 -4
- package/templates/skills/hai-ci/SKILL.md +2 -0
- package/templates/skills/hai-core/SKILL.md +3 -3
- package/templates/skills/hai-crypto/SKILL.md +16 -252
- package/templates/skills/hai-crypto/reference.md +256 -0
- package/templates/skills/hai-datapipe/SKILL.md +15 -196
- package/templates/skills/hai-datapipe/reference.md +202 -0
- package/templates/skills/hai-deploy/SKILL.md +28 -271
- package/templates/skills/hai-deploy/middleware.md +6 -18
- package/templates/skills/hai-framework-sync/SKILL.md +2 -2
- package/templates/skills/hai-framework-sync/sync.md +1 -1
- package/templates/skills/hai-iam/SKILL.md +1 -3
- package/templates/skills/hai-kit/SKILL.md +12 -167
- package/templates/skills/hai-kit/reference.md +172 -0
- package/templates/skills/hai-payment/SKILL.md +19 -251
- package/templates/skills/hai-payment/reference.md +256 -0
- package/templates/skills/hai-pr-review/SKILL.md +4 -4
- package/templates/skills/hai-reach/SKILL.md +2 -2
- package/templates/skills/hai-reldb/SKILL.md +1 -1
- package/templates/skills/hai-scheduler/SKILL.md +15 -195
- package/templates/skills/hai-scheduler/reference.md +202 -0
- package/templates/skills/hai-serv/SKILL.md +2 -2
- package/templates/skills/hai-spec-implement/SKILL.md +1 -1
- package/templates/skills/hai-spec-implement/workflow.md +3 -3
- package/templates/skills/hai-storage/SKILL.md +17 -238
- package/templates/skills/hai-storage/reference.md +243 -0
- package/templates/skills/hai-ui/SKILL.md +1 -1
- package/templates/skills/hai-ui/reference.md +11 -5
- package/templates/skills/hai-vecdb/SKILL.md +2 -2
- package/dist/chunk-47V5OT22.js.map +0 -1
- package/dist/chunk-IPG3QNYK.js.map +0 -1
- package/dist/cli-config-templates-SJ2MBIR7.js +0 -5
- package/dist/src-6PPBKQ6Q.js.map +0 -1
- package/templates/apps/api/Dockerfile.hbs +0 -48
- package/templates/apps/fullstack/Dockerfile.hbs +0 -69
- package/templates/base/Dockerfile.hbs +0 -79
- /package/templates/apps/fullstack/{docker → images/web}/19-runtime-config.envsh.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/nginx.conf.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/runtime-config.js.template.hbs +0 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# hai-kit 参考
|
|
2
|
+
|
|
3
|
+
> `@h-ai/kit` 只负责 SvelteKit 集成:`handle` hook、guard、response、validate、client、auth、crud、A2A。公共跨端 HTTP API 契约统一使用 `@h-ai/api-contract` + `@h-ai/serv`,不要在 kit 中定义业务 API contract。
|
|
4
|
+
|
|
5
|
+
## 使用步骤
|
|
6
|
+
|
|
7
|
+
### 1. 请求管道
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import type { Handle } from '@sveltejs/kit'
|
|
11
|
+
import { kit } from '@h-ai/kit'
|
|
12
|
+
|
|
13
|
+
const haiHandle = kit.createHandle({
|
|
14
|
+
auth: {
|
|
15
|
+
verifyToken: async token => token ? { userId: 'u_1', roles: ['admin'], permissions: ['user:read'] } : null,
|
|
16
|
+
loginUrl: '/auth/login',
|
|
17
|
+
protectedPaths: ['/admin/*'],
|
|
18
|
+
},
|
|
19
|
+
rateLimit: { windowMs: 60_000, maxRequests: 100 },
|
|
20
|
+
logging: true,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
export const handle: Handle = haiHandle
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 2. SvelteKit API endpoint
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { kit } from '@h-ai/kit'
|
|
30
|
+
import { z } from 'zod'
|
|
31
|
+
|
|
32
|
+
const CreateUserSchema = z.object({ name: z.string().min(1) })
|
|
33
|
+
|
|
34
|
+
export const POST = kit.handler(async ({ request, locals }) => {
|
|
35
|
+
kit.guard.require(locals.session, 'user:create')
|
|
36
|
+
const payload = await kit.validate.body(request, CreateUserSchema)
|
|
37
|
+
return kit.response.created(payload)
|
|
38
|
+
})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 3. 浏览器同源请求
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { kit } from '@h-ai/kit'
|
|
45
|
+
|
|
46
|
+
const client = kit.client.create()
|
|
47
|
+
export const { apiFetch } = client
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> 同源 endpoint 优先使用 httpOnly Cookie;`auth: true` 只读取 `kit.auth.setBrowserToken()` 写入的页面内存 Token,不会默认读写 localStorage。确需 Bearer Header 时,显式传入 `BrowserTokenStore`,禁止把敏感 Token 存入 localStorage。
|
|
51
|
+
|
|
52
|
+
### 4. 同源传输加密
|
|
53
|
+
|
|
54
|
+
kit 不再维护本地传输加密实现;服务端和客户端都统一委托 `@h-ai/crypto` 的 `crypto.transport`。
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
// hooks.server.ts
|
|
58
|
+
import { cache } from '@h-ai/cache'
|
|
59
|
+
import { createRedisTransportKeyStore, crypto } from '@h-ai/crypto'
|
|
60
|
+
import { kit } from '@h-ai/kit'
|
|
61
|
+
|
|
62
|
+
await crypto.init()
|
|
63
|
+
await cache.init({ type: 'redis', host: '127.0.0.1', port: 6379 })
|
|
64
|
+
|
|
65
|
+
export const handle = kit.createHandle({
|
|
66
|
+
auth: {
|
|
67
|
+
verifyToken,
|
|
68
|
+
protectedPaths: ['/api/*'],
|
|
69
|
+
publicPaths: ['/api/_hai/*'],
|
|
70
|
+
},
|
|
71
|
+
crypto: {
|
|
72
|
+
crypto,
|
|
73
|
+
transport: {
|
|
74
|
+
requireEncryption: true,
|
|
75
|
+
keyStore: createRedisTransportKeyStore({ cache, ttlSeconds: 3600 }),
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
})
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
需要保护会话 Cookie 时配置 `crypto.encryptedCookies`,并通过 `crypto.cookieEncryptionKey` 或 `HAI_KIT_COOKIE_KEY` 提供密钥。缺少密钥会在 `createHandle()` 时失败;加解密错误不会回退到明文 Cookie。
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
// routes/+layout.svelte
|
|
85
|
+
<script lang='ts'>
|
|
86
|
+
import { browser } from '$app/environment'
|
|
87
|
+
import { crypto } from '@h-ai/crypto'
|
|
88
|
+
import { kit } from '@h-ai/kit'
|
|
89
|
+
import { appKitConfig } from '$lib/config/kit-config'
|
|
90
|
+
|
|
91
|
+
// 一次性安装:内部按 appKitConfig.transport 是否启用决定行为,并预热 crypto
|
|
92
|
+
if (browser) {
|
|
93
|
+
kit.client.installBrowserTransport(appKitConfig, { crypto })
|
|
94
|
+
}
|
|
95
|
+
</script>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
// lib/utils/api.ts —— 业务层只看到 apiFetch
|
|
100
|
+
import { kit } from '@h-ai/kit'
|
|
101
|
+
|
|
102
|
+
export const { apiFetch } = kit.client.create()
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
默认协商端点:`/api/_hai/key-exchange`。如 `keyExchangePath` 自定义,浏览器端同步设置 `keyExchangeUrl`。
|
|
106
|
+
|
|
107
|
+
多节点部署时,可在 `kit.createHandle({ crypto: { transport } })` 的运行时对象里直接传 `keyStore`;推荐使用 `@h-ai/crypto` 根入口导出的 `createRedisTransportKeyStore()` / `createReldbTransportKeyStore()`。这类对象依赖不要写进 `_kit.yml`。
|
|
108
|
+
|
|
109
|
+
transport 默认保护同源 `/api/*` endpoint 与 SvelteKit `__data.json` 页面数据请求;页面文档、静态资源与 `multipart/form-data` 上传请求保持明文透传。
|
|
110
|
+
|
|
111
|
+
安全策略默认 fail-closed:`requireEncryption: true` 时,受保护路径缺少 `X-Client-Id` 必须返回 400;服务端 transport 管理器不可用、响应体无法加密或超过单次加密上限时返回错误,禁止明文业务响应回退。`requireEncryption: false` 只适合迁移期灰度。
|
|
112
|
+
|
|
113
|
+
### 5. 使用 `_kit.yml` 统一 transport 配置
|
|
114
|
+
|
|
115
|
+
`@h-ai/kit` 现在提供 `KitConfigSchema` / `resolveKitConfig()`,适合同一份 `_kit.yml`
|
|
116
|
+
同时驱动 `hooks.server.ts` 与浏览器端 `kit.client.installBrowserTransport()`:
|
|
117
|
+
|
|
118
|
+
```yml
|
|
119
|
+
server:
|
|
120
|
+
host: 127.0.0.1
|
|
121
|
+
port: 3000
|
|
122
|
+
|
|
123
|
+
transport:
|
|
124
|
+
keyExchangePath: /api/_hai/key-exchange
|
|
125
|
+
requireEncryption: true
|
|
126
|
+
encryptResponse: true
|
|
127
|
+
excludePaths:
|
|
128
|
+
- /api/storage
|
|
129
|
+
- /api/public
|
|
130
|
+
- /api/auth/profile/avatar
|
|
131
|
+
maxClients: 10000
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
import { resolveKitConfig } from '@h-ai/kit'
|
|
136
|
+
import { parse } from 'yaml'
|
|
137
|
+
import rawKitConfig from '../config/_kit.yml?raw'
|
|
138
|
+
|
|
139
|
+
export const appKitConfig = resolveKitConfig(parse(rawKitConfig) ?? {})
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`resolveKitConfig()` 同时解析 `server`(host/port)与 `transport`;host/port 优先级为 `HOST` / `PORT` 环境变量 > `_kit.yml` 的 `server` > 默认 `127.0.0.1:3000`(与 `@h-ai/serv` 同构)。
|
|
143
|
+
`_kit.yml` 只放公开的 server/transport 路径/开关,不放密钥;如自定义 `keyExchangePath`,浏览器端必须同步映射为 `keyExchangeUrl`。
|
|
144
|
+
|
|
145
|
+
## 核心 API
|
|
146
|
+
|
|
147
|
+
| API | 用途 |
|
|
148
|
+
| --- | --- |
|
|
149
|
+
| `kit.createHandle()` | 创建 SvelteKit Handle |
|
|
150
|
+
| `kit.sequence()` | 组合多个 Handle |
|
|
151
|
+
| `kit.handler()` | API endpoint 错误边界 |
|
|
152
|
+
| `kit.guard.require()` | 权限守卫 |
|
|
153
|
+
| `kit.response.*` | 统一响应工厂 |
|
|
154
|
+
| `kit.validate.*` | Zod 请求校验 |
|
|
155
|
+
| `kit.auth.*` | Cookie / Token 辅助 |
|
|
156
|
+
| `kit.client.create()` | 浏览器端同源 apiFetch |
|
|
157
|
+
| `kit.client.installBrowserTransport(config, { crypto })` | 推荐入口:按解析后的 `_kit.yml` 一键安装浏览器端传输加密 |
|
|
158
|
+
| `kit.client.installBrowserTransportFetch()` | 底层入口:直接传 transport 配置安装浏览器全局 fetch 包装 |
|
|
159
|
+
| `kit.crud.define()` | 声明式 CRUD 资源 |
|
|
160
|
+
|
|
161
|
+
## 常见模式
|
|
162
|
+
|
|
163
|
+
- SvelteKit 应用内同源请求用 `kit.client.create().apiFetch`。
|
|
164
|
+
- SvelteKit 应用启用 transport 时,在 `+layout.svelte` 的 `if (browser)` 分支调用一次 `kit.client.installBrowserTransport(appKitConfig, { crypto })`,无需重复连线 `keyExchangeUrl` / `excludePaths`。
|
|
165
|
+
- Web/App/小程序跨域访问公共 API 用 `@h-ai/api-client` typed client。
|
|
166
|
+
- 传输加密只通过 `crypto.transport` 间接装配,不在 kit 中新增本地加密工厂或鸭子类型。
|
|
167
|
+
- 服务端业务模块直接调用模块 API,不通过 HTTP 自环。
|
|
168
|
+
- API endpoint 必须用 Zod 校验用户输入,并设置认证/权限边界。
|
|
169
|
+
|
|
170
|
+
## 声明式 CRUD 表单字段
|
|
171
|
+
|
|
172
|
+
`kit.crud.define` 的字段支持 `colSpan: 'auto' | 1 | 2`,以及 `validation.message: string | (() => string)`。布局通过 UI 的 `CrudPage.form` 配置;客户端校验用于交互提示,服务端仍使用 Zod 验证和现有权限守卫。UI 公共契约见相邻 [hai-ui skill](../hai-ui/SKILL.md)。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hai-payment
|
|
3
|
-
description: "
|
|
3
|
+
description: "接入微信、支付宝或 Stripe 的订单、回调验签、查单与退款,并处理客户端调起和幂等履约。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hai-payment
|
|
@@ -15,257 +15,25 @@ description: "使用 @h-ai/payment 创建支付订单、验证回调、查单和
|
|
|
15
15
|
| 输出 | 服务端 HaiResult;客户端 invokePayment 按 client 契约使用 |
|
|
16
16
|
| 限制 | 服务端保存凭据并验签;客户端完成不代表付款成功,以可信回调/查单为准。订单履约与重试需幂等。 |
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
## 使用路径
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
1. 服务端校验 Provider 配置后 init,真实凭据经私有配置注入;浏览器只导入 `/client`。
|
|
21
|
+
2. 订单号、金额单位、商品与用户由服务端可信数据决定,先保存业务订单再创建支付。
|
|
22
|
+
3. 回调使用原始 body 与 headers 验签;核对订单、金额及状态,幂等更新。客户端调起成功不代表付款完成。
|
|
23
|
+
4. 查单、退款与回调重试覆盖成功、失败和重复路径;真实支付仅在用户授权范围内,最后 close。
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
## 按需参考
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
按本次操作读取对应小节;以已安装版本的公开类型核验签名,示例中的业务变量由应用提供。
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
- 客户端调起支付(Web / H5 / App)
|
|
38
|
-
|
|
39
|
-
## 使用步骤
|
|
40
|
-
|
|
41
|
-
### 1. 配置
|
|
42
|
-
|
|
43
|
-
```yaml
|
|
44
|
-
# config/_payment.yml(自行创建)
|
|
45
|
-
wechat:
|
|
46
|
-
appId: ''
|
|
47
|
-
mchId: ''
|
|
48
|
-
apiV3Key: ''
|
|
49
|
-
privateKey: ''
|
|
50
|
-
serialNo: ''
|
|
51
|
-
platformCert: '' # 可选,回调验签需要
|
|
52
|
-
|
|
53
|
-
alipay:
|
|
54
|
-
appId: ''
|
|
55
|
-
privateKey: ''
|
|
56
|
-
alipayPublicKey: ''
|
|
57
|
-
signType: RSA2 # 可选,默认 RSA2
|
|
58
|
-
sandbox: false # 可选,默认 false
|
|
59
|
-
|
|
60
|
-
stripe:
|
|
61
|
-
secretKey: ''
|
|
62
|
-
webhookSecret: ''
|
|
63
|
-
webhookToleranceSeconds: 300 # 可选,默认 300 秒
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
约定环境变量优先于 YAML,例如 `HAI_PAYMENT_WECHAT_APIV3KEY`、
|
|
67
|
-
`HAI_PAYMENT_STRIPE_WEBHOOKSECRET`。生产密钥只放环境或密钥管理器。
|
|
68
|
-
|
|
69
|
-
### 2. 初始化(服务端)
|
|
70
|
-
|
|
71
|
-
```typescript
|
|
72
|
-
import { payment } from '@h-ai/payment'
|
|
73
|
-
|
|
74
|
-
const initResult = await payment.init({
|
|
75
|
-
wechat: {
|
|
76
|
-
appId: 'wx1234567890',
|
|
77
|
-
mchId: '1600000000',
|
|
78
|
-
apiV3Key: 'your-api-key-v3',
|
|
79
|
-
privateKey: '-----BEGIN RSA PRIVATE KEY-----...',
|
|
80
|
-
serialNo: 'CERT_SERIAL_NO',
|
|
81
|
-
},
|
|
82
|
-
alipay: {
|
|
83
|
-
appId: '2021000000000000',
|
|
84
|
-
privateKey: '-----BEGIN RSA PRIVATE KEY-----...',
|
|
85
|
-
alipayPublicKey: '-----BEGIN PUBLIC KEY-----...',
|
|
86
|
-
},
|
|
87
|
-
})
|
|
88
|
-
if (!initResult.success) {
|
|
89
|
-
// 处理初始化错误
|
|
90
|
-
}
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### 3. 创建订单
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
const result = await payment.createOrder('wechat', {
|
|
97
|
-
orderNo: 'ORDER_20250101_001',
|
|
98
|
-
amount: 9900, // 单位:分
|
|
99
|
-
description: '商品名称',
|
|
100
|
-
tradeType: 'jsapi',
|
|
101
|
-
userId: 'user_openid',
|
|
102
|
-
notifyUrl: 'https://example.com/payment/notify/wechat',
|
|
103
|
-
metadata: { source: 'web' },
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
if (result.success) {
|
|
107
|
-
// result.data: PaymentOrder { provider, tradeType, clientParams, prepayId? }
|
|
108
|
-
// clientParams 包含调起支付所需参数
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// amount / totalAmount 必须是正整数(单位:分)
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### 4. 处理支付回调
|
|
115
|
-
|
|
116
|
-
```typescript
|
|
117
|
-
// API 路由:POST /payment/notify/:provider
|
|
118
|
-
const result = await payment.handleNotify('wechat', {
|
|
119
|
-
body: await request.text(),
|
|
120
|
-
headers: Object.fromEntries(request.headers),
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
if (result.success) {
|
|
124
|
-
// result.data: { orderNo, transactionId, amount, status, paidAt? }
|
|
125
|
-
// 更新订单状态...
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### 5. 查询订单
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
const result = await payment.queryOrder('wechat', 'ORDER_20250101_001')
|
|
133
|
-
|
|
134
|
-
if (result.success) {
|
|
135
|
-
// result.data.status: 'pending' | 'paid' | 'closed' | 'refunded' | 'failed'
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### 6. 退款
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
const result = await payment.refund('wechat', {
|
|
143
|
-
orderNo: 'ORDER_20250101_001',
|
|
144
|
-
refundNo: 'REFUND_20250101_001',
|
|
145
|
-
amount: 9900,
|
|
146
|
-
totalAmount: 9900, // 微信退款必填
|
|
147
|
-
reason: '用户退款',
|
|
148
|
-
})
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### 7. 客户端调起支付(浏览器)
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
import { invokePayment } from '@h-ai/payment/client'
|
|
155
|
-
|
|
156
|
-
// 前端拿到服务端返回的 PaymentOrder 后调起支付
|
|
157
|
-
const payResult = await invokePayment(orderResult.data)
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## 核心 API(服务端)
|
|
161
|
-
|
|
162
|
-
| API | 用途 | 返回值 |
|
|
163
|
-
| ------------------------------------------------ | -------- | ----------------------------- |
|
|
164
|
-
| `payment.init(config)` | 初始化 | `HaiResult<void>` |
|
|
165
|
-
| `payment.close()` | 关闭 | `void` |
|
|
166
|
-
| `payment.createOrder(providerName, input)` | 创建订单 | `HaiResult<PaymentOrder>` |
|
|
167
|
-
| `payment.handleNotify(providerName, request)` | 处理回调 | `HaiResult<PaymentNotifyResult>` |
|
|
168
|
-
| `payment.queryOrder(providerName, orderNo)` | 查询订单 | `HaiResult<OrderStatus>` |
|
|
169
|
-
| `payment.refund(providerName, input)` | 退款 | `HaiResult<RefundResult>` |
|
|
170
|
-
| `payment.closeOrder(providerName, orderNo)` | 关闭订单 | `HaiResult<void>` |
|
|
171
|
-
| `payment.getProvider(name)` | 获取 Provider | `PaymentProvider \| undefined` |
|
|
172
|
-
| `payment.registerProvider(provider)` | 注册自定义 Provider | `void` |
|
|
173
|
-
|
|
174
|
-
## 客户端 API
|
|
175
|
-
|
|
176
|
-
| API | 用途 | 返回值 |
|
|
177
|
-
| ------------------------ | -------- | ----------------------------- |
|
|
178
|
-
| `invokePayment(options)` | 调起支付 | `HaiResult<InvokePaymentResult>` |
|
|
179
|
-
|
|
180
|
-
## 错误码 — `HaiPaymentError`
|
|
181
|
-
|
|
182
|
-
| 错误码 | code | 说明 |
|
|
183
|
-
|--------|------|------|
|
|
184
|
-
| `HaiPaymentError.CREATE_ORDER_FAILED` | `hai:payment:001` | 创建订单失败 |
|
|
185
|
-
| `HaiPaymentError.QUERY_ORDER_FAILED` | `hai:payment:002` | 查询订单失败 |
|
|
186
|
-
| `HaiPaymentError.REFUND_FAILED` | `hai:payment:003` | 退款失败 |
|
|
187
|
-
| `HaiPaymentError.CLOSE_ORDER_FAILED` | `hai:payment:004` | 关闭订单失败 |
|
|
188
|
-
| `HaiPaymentError.NOT_INITIALIZED` | `hai:payment:010` | 模块未初始化 |
|
|
189
|
-
| `HaiPaymentError.SIGN_FAILED` | `hai:payment:020` | 签名失败 |
|
|
190
|
-
| `HaiPaymentError.PROVIDER_NOT_FOUND` | `hai:payment:030` | Provider 未注册 |
|
|
191
|
-
| `HaiPaymentError.INVALID_AMOUNT` | `hai:payment:040` | 金额无效 |
|
|
192
|
-
| `HaiPaymentError.NOTIFY_VERIFY_FAILED` | `hai:payment:050` | 回调验签失败 |
|
|
193
|
-
| `HaiPaymentError.NOTIFY_PARSE_FAILED` | `hai:payment:051` | 回调解析失败 |
|
|
194
|
-
| `HaiPaymentError.INVOKE_WEB_FAILED` | `hai:payment:060` | Web 端调起支付失败 |
|
|
195
|
-
| `HaiPaymentError.INVOKE_APP_FAILED` | `hai:payment:061` | App 端调起支付失败 |
|
|
196
|
-
| `HaiPaymentError.CONFIG_ERROR` | `hai:payment:070` | 配置无效 |
|
|
197
|
-
|
|
198
|
-
## Provider 模式
|
|
199
|
-
|
|
200
|
-
内置 Provider:
|
|
201
|
-
|
|
202
|
-
- **wechat** — 微信支付 API v3
|
|
203
|
-
- **alipay** — 支付宝开放平台
|
|
204
|
-
- **stripe** — Stripe Checkout Session 模式
|
|
205
|
-
|
|
206
|
-
自定义 Provider 通过 `payment.registerProvider(provider)` 注册。
|
|
207
|
-
|
|
208
|
-
### HTTP API 契约
|
|
209
|
-
|
|
210
|
-
```typescript
|
|
211
|
-
import { apiContract } from '@h-ai/api-contract'
|
|
212
|
-
|
|
213
|
-
// apiContract.payment.orders.create — POST /payment/orders
|
|
214
|
-
// apiContract.payment.orders.get — GET /payment/orders/{orderNo}
|
|
215
|
-
// apiContract.payment.orders.refund — POST /payment/orders/{orderNo}/refunds
|
|
216
|
-
// apiContract.payment.notifications.wechat — POST /payment/notifications/wechat
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
## 常见模式
|
|
220
|
-
|
|
221
|
-
### 服务端路由(SvelteKit)
|
|
222
|
-
|
|
223
|
-
```typescript
|
|
224
|
-
import { kit } from '@h-ai/kit'
|
|
225
|
-
// src/routes/api/v1/payment/create/+server.ts
|
|
226
|
-
import { payment } from '@h-ai/payment'
|
|
227
|
-
|
|
228
|
-
export const POST = kit.handler(async ({ request, locals }) => {
|
|
229
|
-
const body = await request.json()
|
|
230
|
-
const result = await payment.createOrder(body.provider, {
|
|
231
|
-
orderNo: body.orderNo,
|
|
232
|
-
amount: body.amount,
|
|
233
|
-
description: body.description,
|
|
234
|
-
tradeType: body.tradeType,
|
|
235
|
-
userId: body.userId,
|
|
236
|
-
notifyUrl: body.notifyUrl,
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
if (!result.success) {
|
|
240
|
-
return kit.response.internalError(result.error.message)
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
return kit.response.ok(result.data, locals.requestId)
|
|
244
|
-
})
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
### 完整支付流程
|
|
248
|
-
|
|
249
|
-
```
|
|
250
|
-
1. 客户端 → 服务端:创建订单(api.payment.orders.create({...}))
|
|
251
|
-
2. 服务端 → 支付平台:调用 Provider 创建预支付
|
|
252
|
-
3. 服务端 → 客户端:返回 providerData(含调起参数)
|
|
253
|
-
4. 客户端:invokePayment(providerData) 调起支付
|
|
254
|
-
5. 支付平台 → 服务端:异步回调通知
|
|
255
|
-
6. 服务端:handleNotify() 验签 + 更新订单状态
|
|
256
|
-
7. 客户端:轮询或回调查询订单状态
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
## 审计日志
|
|
260
|
-
|
|
261
|
-
关键支付操作(创建订单、回调、退款、关闭订单)成功后自动写入审计日志(依赖 `@h-ai/audit`),无需额外配置。`queryOrder` 为只读操作,不写审计日志。
|
|
262
|
-
|
|
263
|
-
内置 Provider 的 HTTP 请求默认带 15 秒超时;Stripe 可通过 `webhookToleranceSeconds` 调整 webhook 时间戳容忍窗口。
|
|
264
|
-
|
|
265
|
-
## 相关 Skills
|
|
266
|
-
|
|
267
|
-
- `hai-kit`:服务端 API 路由与契约处理
|
|
268
|
-
- `hai-api-client`:客户端契约调用
|
|
269
|
-
- `hai-reldb`:订单持久化存储
|
|
270
|
-
- `hai-audit`:审计日志(payment 内部自动调用)
|
|
271
|
-
- `hai-core`:HaiResult 类型、日志、配置
|
|
29
|
+
- [运行环境](reference.md#运行环境)
|
|
30
|
+
- [依赖](reference.md#依赖)
|
|
31
|
+
- [适用场景](reference.md#适用场景)
|
|
32
|
+
- [使用步骤](reference.md#使用步骤)
|
|
33
|
+
- [核心 API(服务端)](reference.md#核心-api服务端)
|
|
34
|
+
- [客户端 API](reference.md#客户端-api)
|
|
35
|
+
- [错误码 — `HaiPaymentError`](reference.md#错误码--haipaymenterror)
|
|
36
|
+
- [Provider 模式](reference.md#provider-模式)
|
|
37
|
+
- [常见模式](reference.md#常见模式)
|
|
38
|
+
- [审计日志](reference.md#审计日志)
|
|
39
|
+
- [相关 Skills](reference.md#相关-skills)
|