@svton/cli 1.2.2 → 1.2.4
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/index.js +58 -27
- package/dist/index.mjs +58 -27
- package/package.json +1 -3
- package/templates/apps/admin/next-env.d.ts +0 -2
- package/templates/apps/admin/next.config.js +0 -15
- package/templates/apps/admin/package.json.tpl +0 -54
- package/templates/apps/admin/postcss.config.js +0 -6
- package/templates/apps/admin/src/app/globals.css +0 -37
- package/templates/apps/admin/src/app/layout.tsx +0 -19
- package/templates/apps/admin/src/app/login/page.tsx +0 -96
- package/templates/apps/admin/src/app/page.tsx +0 -8
- package/templates/apps/admin/src/app/users/page.tsx +0 -165
- package/templates/apps/admin/src/components/ui/switch.tsx +0 -29
- package/templates/apps/admin/src/hooks/useAPI.ts +0 -130
- package/templates/apps/admin/src/lib/api-client.ts +0 -112
- package/templates/apps/admin/src/lib/api-server.ts +0 -95
- package/templates/apps/admin/tailwind.config.js +0 -54
- package/templates/apps/admin/tsconfig.json +0 -22
- package/templates/apps/backend/.env.example +0 -29
- package/templates/apps/backend/nest-cli.json +0 -8
- package/templates/apps/backend/package.json.tpl +0 -57
- package/templates/apps/backend/prisma/schema.prisma +0 -72
- package/templates/apps/backend/prisma/seed.ts +0 -32
- package/templates/apps/backend/src/app.controller.ts +0 -15
- package/templates/apps/backend/src/app.module.ts +0 -85
- package/templates/apps/backend/src/app.service.ts +0 -12
- package/templates/apps/backend/src/auth/auth.controller.ts +0 -31
- package/templates/apps/backend/src/auth/auth.module.ts +0 -27
- package/templates/apps/backend/src/auth/auth.service.ts +0 -89
- package/templates/apps/backend/src/auth/jwt-auth.guard.ts +0 -5
- package/templates/apps/backend/src/auth/jwt.strategy.ts +0 -27
- package/templates/apps/backend/src/config/env.schema.ts +0 -35
- package/templates/apps/backend/src/main.ts +0 -51
- package/templates/apps/backend/src/object-storage/object-storage.controller.ts +0 -114
- package/templates/apps/backend/src/object-storage/object-storage.module.ts +0 -7
- package/templates/apps/backend/src/prisma/prisma.module.ts +0 -9
- package/templates/apps/backend/src/prisma/prisma.service.ts +0 -13
- package/templates/apps/backend/src/user/user.controller.ts +0 -50
- package/templates/apps/backend/src/user/user.module.ts +0 -12
- package/templates/apps/backend/src/user/user.service.ts +0 -117
- package/templates/apps/backend/tsconfig.json +0 -23
- package/templates/apps/mobile/babel.config.js +0 -8
- package/templates/apps/mobile/config/index.ts +0 -65
- package/templates/apps/mobile/package.json.tpl +0 -48
- package/templates/apps/mobile/project.config.json.tpl +0 -17
- package/templates/apps/mobile/src/app.config.ts +0 -9
- package/templates/apps/mobile/src/app.scss +0 -4
- package/templates/apps/mobile/src/app.ts +0 -8
- package/templates/apps/mobile/src/hooks/useAPI.ts +0 -285
- package/templates/apps/mobile/src/pages/index/index.scss +0 -7
- package/templates/apps/mobile/src/pages/index/index.tsx +0 -49
- package/templates/apps/mobile/src/services/api.ts +0 -155
- package/templates/apps/mobile/src/services/upload.service.ts +0 -41
- package/templates/apps/mobile/tsconfig.json +0 -21
- package/templates/configs/authz.config.ts +0 -10
- package/templates/configs/cache.config.ts +0 -14
- package/templates/configs/oauth.config.ts +0 -20
- package/templates/configs/payment.config.ts +0 -44
- package/templates/configs/queue.config.ts +0 -21
- package/templates/configs/rate-limit.config.ts +0 -16
- package/templates/configs/sms.config.ts +0 -11
- package/templates/configs/storage.config.ts +0 -14
- package/templates/examples/README.md +0 -258
- package/templates/examples/authz/README.md +0 -273
- package/templates/examples/authz/roles.guard.ts +0 -37
- package/templates/examples/authz/user.controller.ts +0 -116
- package/templates/examples/cache/README.md +0 -82
- package/templates/examples/cache/user.controller.ts +0 -42
- package/templates/examples/cache/user.service.ts +0 -78
- package/templates/examples/oauth/README.md +0 -192
- package/templates/examples/oauth/auth.controller.ts +0 -99
- package/templates/examples/oauth/auth.service.ts +0 -97
- package/templates/examples/payment/README.md +0 -151
- package/templates/examples/payment/order.controller.ts +0 -56
- package/templates/examples/payment/order.service.ts +0 -132
- package/templates/examples/payment/webhook.controller.ts +0 -73
- package/templates/examples/queue/README.md +0 -134
- package/templates/examples/queue/email.controller.ts +0 -34
- package/templates/examples/queue/email.processor.ts +0 -68
- package/templates/examples/queue/email.service.ts +0 -64
- package/templates/examples/rate-limit/README.md +0 -249
- package/templates/examples/rate-limit/api.controller.ts +0 -113
- package/templates/examples/sms/README.md +0 -121
- package/templates/examples/sms/sms.service.ts +0 -69
- package/templates/examples/sms/verification.controller.ts +0 -100
- package/templates/examples/storage/README.md +0 -224
- package/templates/examples/storage/upload.controller.ts +0 -117
- package/templates/examples/storage/upload.service.ts +0 -123
- package/templates/packages/types/package.json.tpl +0 -16
- package/templates/packages/types/src/api.ts +0 -88
- package/templates/packages/types/src/common.ts +0 -89
- package/templates/packages/types/src/index.ts +0 -3
- package/templates/packages/types/tsconfig.json +0 -16
- package/templates/skills/authz.skill.md +0 -42
- package/templates/skills/base.skill.md +0 -57
- package/templates/skills/cache.skill.md +0 -88
- package/templates/skills/oauth.skill.md +0 -41
- package/templates/skills/payment.skill.md +0 -129
- package/templates/skills/queue.skill.md +0 -140
- package/templates/skills/rate-limit.skill.md +0 -38
- package/templates/skills/sms.skill.md +0 -39
- package/templates/skills/storage.skill.md +0 -42
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
# OAuth 登录示例
|
|
2
|
-
|
|
3
|
-
本示例展示如何使用 `@svton/nestjs-oauth` 模块实现微信登录。
|
|
4
|
-
|
|
5
|
-
## 文件说明
|
|
6
|
-
|
|
7
|
-
- `auth.service.ts` - 认证服务,处理 OAuth 流程
|
|
8
|
-
- `auth.controller.ts` - 认证控制器,提供登录接口
|
|
9
|
-
|
|
10
|
-
## 支持的登录方式
|
|
11
|
-
|
|
12
|
-
### 1. 微信开放平台(PC 扫码登录)
|
|
13
|
-
|
|
14
|
-
适用于网站应用,用户扫码登录。
|
|
15
|
-
|
|
16
|
-
### 2. 微信公众号(网页授权)
|
|
17
|
-
|
|
18
|
-
适用于公众号内网页,静默授权或用户授权。
|
|
19
|
-
|
|
20
|
-
### 3. 微信小程序
|
|
21
|
-
|
|
22
|
-
适用于小程序,使用 wx.login() 获取 code。
|
|
23
|
-
|
|
24
|
-
## 使用方式
|
|
25
|
-
|
|
26
|
-
### PC 扫码登录
|
|
27
|
-
|
|
28
|
-
```typescript
|
|
29
|
-
// 1. 获取授权 URL
|
|
30
|
-
const url = this.authService.getWechatOpenAuthUrl('/dashboard');
|
|
31
|
-
|
|
32
|
-
// 2. 重定向到微信授权页面
|
|
33
|
-
// 用户扫码授权后,微信会回调到 callback 接口
|
|
34
|
-
|
|
35
|
-
// 3. 处理回调
|
|
36
|
-
const userInfo = await this.authService.handleWechatOpenCallback(code);
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### 公众号网页授权
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
// 1. 获取授权 URL
|
|
43
|
-
const url = this.authService.getWechatMpAuthUrl('/profile');
|
|
44
|
-
|
|
45
|
-
// 2. 重定向到微信授权页面
|
|
46
|
-
// 用户授权后,微信会回调到 callback 接口
|
|
47
|
-
|
|
48
|
-
// 3. 处理回调
|
|
49
|
-
const userInfo = await this.authService.handleWechatMpCallback(code);
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### 小程序登录
|
|
53
|
-
|
|
54
|
-
```typescript
|
|
55
|
-
// 小程序端
|
|
56
|
-
wx.login({
|
|
57
|
-
success: (res) => {
|
|
58
|
-
// 将 code 发送到后端
|
|
59
|
-
wx.request({
|
|
60
|
-
url: 'https://api.example.com/examples/auth/wechat/miniprogram/login',
|
|
61
|
-
method: 'POST',
|
|
62
|
-
data: { code: res.code },
|
|
63
|
-
});
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// 后端处理
|
|
68
|
-
const result = await this.authService.miniprogramLogin(code);
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### 小程序获取手机号
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
// 小程序端
|
|
75
|
-
<button open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
|
|
76
|
-
获取手机号
|
|
77
|
-
</button>
|
|
78
|
-
|
|
79
|
-
// 获取到 code 后发送到后端
|
|
80
|
-
const result = await this.authService.getMiniprogramPhoneNumber(code);
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## 测试接口
|
|
84
|
-
|
|
85
|
-
### PC 扫码登录
|
|
86
|
-
|
|
87
|
-
```bash
|
|
88
|
-
# 1. 访问登录页面(会重定向到微信)
|
|
89
|
-
curl http://localhost:3000/examples/auth/wechat/open/login
|
|
90
|
-
|
|
91
|
-
# 2. 扫码授权后,微信会回调到:
|
|
92
|
-
# http://localhost:3000/examples/auth/wechat/open/callback?code=xxx&state=/
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### 小程序登录
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
curl -X POST http://localhost:3000/examples/auth/wechat/miniprogram/login \
|
|
99
|
-
-H "Content-Type: application/json" \
|
|
100
|
-
-d '{"code":"081234567890abcdef"}'
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### 小程序获取手机号
|
|
104
|
-
|
|
105
|
-
```bash
|
|
106
|
-
curl -X POST http://localhost:3000/examples/auth/wechat/miniprogram/phone \
|
|
107
|
-
-H "Content-Type: application/json" \
|
|
108
|
-
-d '{"code":"081234567890abcdef"}'
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
## 环境变量配置
|
|
112
|
-
|
|
113
|
-
在 `.env` 文件中配置:
|
|
114
|
-
|
|
115
|
-
```env
|
|
116
|
-
# 微信开放平台
|
|
117
|
-
WECHAT_OPEN_APP_ID=wx1234567890abcdef
|
|
118
|
-
WECHAT_OPEN_APP_SECRET=1234567890abcdef1234567890abcdef
|
|
119
|
-
WECHAT_OPEN_CALLBACK_URL=https://yourdomain.com/examples/auth/wechat/open/callback
|
|
120
|
-
|
|
121
|
-
# 微信公众号
|
|
122
|
-
WECHAT_MP_APP_ID=wx1234567890abcdef
|
|
123
|
-
WECHAT_MP_APP_SECRET=1234567890abcdef1234567890abcdef
|
|
124
|
-
|
|
125
|
-
# 微信小程序
|
|
126
|
-
WECHAT_MINI_APP_ID=wx1234567890abcdef
|
|
127
|
-
WECHAT_MINI_APP_SECRET=1234567890abcdef1234567890abcdef
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## 配置回调地址
|
|
131
|
-
|
|
132
|
-
需要在微信后台配置回调地址:
|
|
133
|
-
|
|
134
|
-
### 开放平台
|
|
135
|
-
1. 登录微信开放平台
|
|
136
|
-
2. 进入网站应用详情
|
|
137
|
-
3. 配置授权回调域:`yourdomain.com`
|
|
138
|
-
|
|
139
|
-
### 公众号
|
|
140
|
-
1. 登录微信公众平台
|
|
141
|
-
2. 设置与开发 -> 接口权限 -> 网页授权
|
|
142
|
-
3. 配置授权回调域:`yourdomain.com`
|
|
143
|
-
|
|
144
|
-
### 小程序
|
|
145
|
-
1. 登录微信小程序后台
|
|
146
|
-
2. 开发 -> 开发管理 -> 开发设置
|
|
147
|
-
3. 配置服务器域名:`https://yourdomain.com`
|
|
148
|
-
|
|
149
|
-
## 最佳实践
|
|
150
|
-
|
|
151
|
-
1. **UnionID 机制**:使用 unionid 关联同一用户在不同应用的身份
|
|
152
|
-
2. **Token 管理**:使用 JWT 生成 token,设置合理的过期时间
|
|
153
|
-
3. **刷新机制**:实现 refresh_token 机制,避免频繁授权
|
|
154
|
-
4. **错误处理**:妥善处理授权失败、token 过期等异常情况
|
|
155
|
-
5. **安全性**:验证 state 参数,防止 CSRF 攻击
|
|
156
|
-
|
|
157
|
-
## 常见场景
|
|
158
|
-
|
|
159
|
-
### 网站登录
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
// 1. 用户点击"微信登录"
|
|
163
|
-
// 2. 重定向到微信授权页面
|
|
164
|
-
// 3. 用户扫码授权
|
|
165
|
-
// 4. 微信回调到后端
|
|
166
|
-
// 5. 后端获取用户信息,生成 token
|
|
167
|
-
// 6. 重定向到前端,携带 token
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
### 小程序登录
|
|
171
|
-
|
|
172
|
-
```typescript
|
|
173
|
-
// 1. 小程序调用 wx.login() 获取 code
|
|
174
|
-
// 2. 将 code 发送到后端
|
|
175
|
-
// 3. 后端调用 code2Session 获取 openid
|
|
176
|
-
// 4. 查询或创建用户
|
|
177
|
-
// 5. 返回 token 给小程序
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### 绑定手机号
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
|
-
// 1. 用户点击"获取手机号"按钮
|
|
184
|
-
// 2. 小程序获取到 code
|
|
185
|
-
// 3. 将 code 发送到后端
|
|
186
|
-
// 4. 后端调用接口获取手机号
|
|
187
|
-
// 5. 绑定手机号到用户账号
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
## 更多信息
|
|
191
|
-
|
|
192
|
-
查看官方文档:https://751848178.github.io/svton/packages/nestjs-oauth
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { Controller, Get, Query, Redirect, Post, Body } from '@nestjs/common';
|
|
2
|
-
import { AuthService } from './auth.service';
|
|
3
|
-
|
|
4
|
-
@Controller('examples/auth')
|
|
5
|
-
export class AuthController {
|
|
6
|
-
constructor(private readonly authService: AuthService) {}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 微信开放平台登录 - 获取授权 URL
|
|
10
|
-
*/
|
|
11
|
-
@Get('wechat/open/login')
|
|
12
|
-
@Redirect()
|
|
13
|
-
wechatOpenLogin(@Query('redirect') redirect?: string) {
|
|
14
|
-
const state = redirect || '/';
|
|
15
|
-
const url = this.authService.getWechatOpenAuthUrl(state);
|
|
16
|
-
|
|
17
|
-
return { url };
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* 微信开放平台登录 - 回调处理
|
|
22
|
-
*/
|
|
23
|
-
@Get('wechat/open/callback')
|
|
24
|
-
async wechatOpenCallback(
|
|
25
|
-
@Query('code') code: string,
|
|
26
|
-
@Query('state') state: string,
|
|
27
|
-
) {
|
|
28
|
-
const userInfo = await this.authService.handleWechatOpenCallback(code);
|
|
29
|
-
|
|
30
|
-
// TODO: 生成 JWT token
|
|
31
|
-
// const token = await this.jwtService.sign({ userId: user.id });
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
message: 'Login successful',
|
|
35
|
-
userInfo,
|
|
36
|
-
redirectUrl: state,
|
|
37
|
-
// token,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* 微信公众号登录 - 获取授权 URL
|
|
43
|
-
*/
|
|
44
|
-
@Get('wechat/mp/login')
|
|
45
|
-
@Redirect()
|
|
46
|
-
wechatMpLogin(@Query('redirect') redirect?: string) {
|
|
47
|
-
const state = redirect || '/';
|
|
48
|
-
const url = this.authService.getWechatMpAuthUrl(state);
|
|
49
|
-
|
|
50
|
-
return { url };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* 微信公众号登录 - 回调处理
|
|
55
|
-
*/
|
|
56
|
-
@Get('wechat/mp/callback')
|
|
57
|
-
async wechatMpCallback(
|
|
58
|
-
@Query('code') code: string,
|
|
59
|
-
@Query('state') state: string,
|
|
60
|
-
) {
|
|
61
|
-
const userInfo = await this.authService.handleWechatMpCallback(code);
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
message: 'Login successful',
|
|
65
|
-
userInfo,
|
|
66
|
-
redirectUrl: state,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* 小程序登录
|
|
72
|
-
*/
|
|
73
|
-
@Post('wechat/miniprogram/login')
|
|
74
|
-
async miniprogramLogin(@Body() body: { code: string }) {
|
|
75
|
-
const result = await this.authService.miniprogramLogin(body.code);
|
|
76
|
-
|
|
77
|
-
// TODO: 生成 JWT token
|
|
78
|
-
// const token = await this.jwtService.sign({ userId: user.id });
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
message: 'Login successful',
|
|
82
|
-
...result,
|
|
83
|
-
// token,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* 小程序获取手机号
|
|
89
|
-
*/
|
|
90
|
-
@Post('wechat/miniprogram/phone')
|
|
91
|
-
async getMiniprogramPhone(@Body() body: { code: string }) {
|
|
92
|
-
const result = await this.authService.getMiniprogramPhoneNumber(body.code);
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
message: 'Phone number retrieved successfully',
|
|
96
|
-
...result,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { Injectable } from '@nestjs/common';
|
|
2
|
-
import { OAuthService } from '@svton/nestjs-oauth';
|
|
3
|
-
|
|
4
|
-
@Injectable()
|
|
5
|
-
export class AuthService {
|
|
6
|
-
constructor(private readonly oauthService: OAuthService) {}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 获取微信开放平台授权 URL(PC 扫码登录)
|
|
10
|
-
*/
|
|
11
|
-
getWechatOpenAuthUrl(state: string): string {
|
|
12
|
-
return this.oauthService.wechat.getAuthorizationUrl('open', state);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* 获取微信公众号授权 URL(网页授权)
|
|
17
|
-
*/
|
|
18
|
-
getWechatMpAuthUrl(state: string): string {
|
|
19
|
-
return this.oauthService.wechat.getAuthorizationUrl('mp', state);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* 处理微信开放平台回调
|
|
24
|
-
*/
|
|
25
|
-
async handleWechatOpenCallback(code: string) {
|
|
26
|
-
// 获取 access_token
|
|
27
|
-
const tokenResult = await this.oauthService.wechat.getAccessToken('open', code);
|
|
28
|
-
|
|
29
|
-
// 获取用户信息
|
|
30
|
-
const userInfo = await this.oauthService.wechat.getUserInfo(
|
|
31
|
-
'open',
|
|
32
|
-
tokenResult.access_token,
|
|
33
|
-
tokenResult.openid,
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
// TODO: 根据 openid 查询或创建用户
|
|
37
|
-
// const user = await this.userService.findOrCreateByWechatOpenId(userInfo.unionid);
|
|
38
|
-
|
|
39
|
-
return {
|
|
40
|
-
openid: userInfo.openid,
|
|
41
|
-
unionid: userInfo.unionid,
|
|
42
|
-
nickname: userInfo.nickname,
|
|
43
|
-
avatar: userInfo.headimgurl,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* 处理微信公众号回调
|
|
49
|
-
*/
|
|
50
|
-
async handleWechatMpCallback(code: string) {
|
|
51
|
-
const tokenResult = await this.oauthService.wechat.getAccessToken('mp', code);
|
|
52
|
-
|
|
53
|
-
const userInfo = await this.oauthService.wechat.getUserInfo(
|
|
54
|
-
'mp',
|
|
55
|
-
tokenResult.access_token,
|
|
56
|
-
tokenResult.openid,
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
// TODO: 根据 openid 查询或创建用户
|
|
60
|
-
// const user = await this.userService.findOrCreateByWechatMpOpenId(userInfo.openid);
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
openid: userInfo.openid,
|
|
64
|
-
nickname: userInfo.nickname,
|
|
65
|
-
avatar: userInfo.headimgurl,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* 小程序登录
|
|
71
|
-
*/
|
|
72
|
-
async miniprogramLogin(code: string) {
|
|
73
|
-
const result = await this.oauthService.wechat.code2Session(code);
|
|
74
|
-
|
|
75
|
-
// TODO: 根据 openid 查询或创建用户
|
|
76
|
-
// const user = await this.userService.findOrCreateByWechatMiniOpenId(result.openid);
|
|
77
|
-
|
|
78
|
-
return {
|
|
79
|
-
openid: result.openid,
|
|
80
|
-
sessionKey: result.session_key,
|
|
81
|
-
unionid: result.unionid,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* 小程序获取手机号
|
|
87
|
-
*/
|
|
88
|
-
async getMiniprogramPhoneNumber(code: string) {
|
|
89
|
-
const result = await this.oauthService.wechat.getPhoneNumber(code);
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
phoneNumber: result.phone_info.phoneNumber,
|
|
93
|
-
purePhoneNumber: result.phone_info.purePhoneNumber,
|
|
94
|
-
countryCode: result.phone_info.countryCode,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
}
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
# 支付功能示例
|
|
2
|
-
|
|
3
|
-
本示例展示如何使用 `@svton/nestjs-payment` 模块集成微信支付和支付宝。
|
|
4
|
-
|
|
5
|
-
## 文件说明
|
|
6
|
-
|
|
7
|
-
- `order.service.ts` - 订单服务,创建支付订单
|
|
8
|
-
- `order.controller.ts` - 订单控制器,提供支付接口
|
|
9
|
-
- `webhook.controller.ts` - 支付回调处理
|
|
10
|
-
|
|
11
|
-
## 支持的支付方式
|
|
12
|
-
|
|
13
|
-
### 微信支付
|
|
14
|
-
- JSAPI 支付(公众号/小程序)
|
|
15
|
-
- Native 支付(扫码支付)
|
|
16
|
-
- APP 支付
|
|
17
|
-
- H5 支付
|
|
18
|
-
- 小程序支付
|
|
19
|
-
|
|
20
|
-
### 支付宝
|
|
21
|
-
- 电脑网站支付(PC)
|
|
22
|
-
- 手机网站支付(H5)
|
|
23
|
-
- APP 支付
|
|
24
|
-
|
|
25
|
-
## 使用方式
|
|
26
|
-
|
|
27
|
-
### 1. 创建支付订单
|
|
28
|
-
|
|
29
|
-
```typescript
|
|
30
|
-
// 微信 JSAPI 支付
|
|
31
|
-
const result = await this.paymentService.wechat.createOrder({
|
|
32
|
-
outTradeNo: orderId,
|
|
33
|
-
totalAmount: amount,
|
|
34
|
-
description: '商品购买',
|
|
35
|
-
userId: openid,
|
|
36
|
-
}, 'jsapi');
|
|
37
|
-
|
|
38
|
-
// 支付宝电脑网站支付
|
|
39
|
-
const result = await this.paymentService.alipay.createOrder({
|
|
40
|
-
outTradeNo: orderId,
|
|
41
|
-
totalAmount: amount,
|
|
42
|
-
description: '商品购买',
|
|
43
|
-
}, 'page');
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### 2. 查询订单状态
|
|
47
|
-
|
|
48
|
-
```typescript
|
|
49
|
-
const status = await this.paymentService.wechat.queryOrder(outTradeNo);
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### 3. 申请退款
|
|
53
|
-
|
|
54
|
-
```typescript
|
|
55
|
-
const refund = await this.paymentService.wechat.refund({
|
|
56
|
-
outTradeNo: orderId,
|
|
57
|
-
outRefundNo: refundId,
|
|
58
|
-
refundAmount: amount,
|
|
59
|
-
totalAmount: totalAmount,
|
|
60
|
-
});
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### 4. 处理支付回调
|
|
64
|
-
|
|
65
|
-
```typescript
|
|
66
|
-
@Post('webhook/wechat')
|
|
67
|
-
async wechatWebhook(@Req() req: Request) {
|
|
68
|
-
const result = await this.paymentService.wechat.handleNotify(req);
|
|
69
|
-
// 处理支付成功逻辑
|
|
70
|
-
return { code: 'SUCCESS', message: '成功' };
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## 测试接口
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
# 创建微信 JSAPI 支付订单
|
|
78
|
-
curl -X POST http://localhost:3000/examples/orders/wechat/jsapi \
|
|
79
|
-
-H "Content-Type: application/json" \
|
|
80
|
-
-d '{
|
|
81
|
-
"orderId": "ORDER_001",
|
|
82
|
-
"amount": 100,
|
|
83
|
-
"openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
|
|
84
|
-
}'
|
|
85
|
-
|
|
86
|
-
# 创建微信 Native 支付订单(扫码支付)
|
|
87
|
-
curl -X POST http://localhost:3000/examples/orders/wechat/native \
|
|
88
|
-
-H "Content-Type: application/json" \
|
|
89
|
-
-d '{
|
|
90
|
-
"orderId": "ORDER_002",
|
|
91
|
-
"amount": 100
|
|
92
|
-
}'
|
|
93
|
-
|
|
94
|
-
# 创建支付宝电脑网站支付订单
|
|
95
|
-
curl -X POST http://localhost:3000/examples/orders/alipay/page \
|
|
96
|
-
-H "Content-Type: application/json" \
|
|
97
|
-
-d '{
|
|
98
|
-
"orderId": "ORDER_003",
|
|
99
|
-
"amount": 100
|
|
100
|
-
}'
|
|
101
|
-
|
|
102
|
-
# 查询订单状态
|
|
103
|
-
curl http://localhost:3000/examples/orders/ORDER_001/status
|
|
104
|
-
|
|
105
|
-
# 申请退款
|
|
106
|
-
curl -X POST http://localhost:3000/examples/orders/ORDER_001/refund \
|
|
107
|
-
-H "Content-Type: application/json" \
|
|
108
|
-
-d '{
|
|
109
|
-
"refundId": "REFUND_001",
|
|
110
|
-
"amount": 100,
|
|
111
|
-
"reason": "用户申请退款"
|
|
112
|
-
}'
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## 环境变量配置
|
|
116
|
-
|
|
117
|
-
在 `.env` 文件中配置:
|
|
118
|
-
|
|
119
|
-
```env
|
|
120
|
-
# 微信支付
|
|
121
|
-
WECHAT_MCH_ID=商户号
|
|
122
|
-
WECHAT_PRIVATE_KEY=./certs/apiclient_key.pem
|
|
123
|
-
WECHAT_SERIAL_NO=证书序列号
|
|
124
|
-
WECHAT_API_V3_KEY=APIv3密钥
|
|
125
|
-
WECHAT_APP_ID=关联的AppID
|
|
126
|
-
|
|
127
|
-
# 支付宝
|
|
128
|
-
ALIPAY_APP_ID=应用ID
|
|
129
|
-
ALIPAY_PRIVATE_KEY=./certs/alipay_private_key.pem
|
|
130
|
-
ALIPAY_PUBLIC_KEY=./certs/alipay_public_key.pem
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
## 最佳实践
|
|
134
|
-
|
|
135
|
-
1. **订单号唯一性**:确保 outTradeNo 全局唯一
|
|
136
|
-
2. **金额单位**:统一使用分为单位
|
|
137
|
-
3. **回调幂等性**:支付回调可能重复,需要幂等处理
|
|
138
|
-
4. **异步通知**:优先使用异步通知,不依赖同步返回
|
|
139
|
-
5. **安全验证**:验证回调签名,防止伪造
|
|
140
|
-
6. **错误处理**:妥善处理支付失败、超时等异常情况
|
|
141
|
-
|
|
142
|
-
## 回调地址配置
|
|
143
|
-
|
|
144
|
-
需要在微信支付和支付宝后台配置回调地址:
|
|
145
|
-
|
|
146
|
-
- 微信支付:`https://yourdomain.com/examples/webhooks/wechat`
|
|
147
|
-
- 支付宝:`https://yourdomain.com/examples/webhooks/alipay`
|
|
148
|
-
|
|
149
|
-
## 更多信息
|
|
150
|
-
|
|
151
|
-
查看官方文档:https://751848178.github.io/svton/packages/nestjs-payment
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { Controller, Post, Get, Body, Param, Query } from '@nestjs/common';
|
|
2
|
-
import { OrderService } from './order.service';
|
|
3
|
-
|
|
4
|
-
@Controller('examples/orders')
|
|
5
|
-
export class OrderController {
|
|
6
|
-
constructor(private readonly orderService: OrderService) {}
|
|
7
|
-
|
|
8
|
-
@Post('wechat/jsapi')
|
|
9
|
-
async createWechatJsapiOrder(
|
|
10
|
-
@Body() data: { orderId: string; amount: number; openid: string },
|
|
11
|
-
) {
|
|
12
|
-
return this.orderService.createWechatJsapiOrder(
|
|
13
|
-
data.orderId,
|
|
14
|
-
data.amount,
|
|
15
|
-
data.openid,
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
@Post('wechat/native')
|
|
20
|
-
async createWechatNativeOrder(
|
|
21
|
-
@Body() data: { orderId: string; amount: number },
|
|
22
|
-
) {
|
|
23
|
-
return this.orderService.createWechatNativeOrder(
|
|
24
|
-
data.orderId,
|
|
25
|
-
data.amount,
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
@Post('alipay/page')
|
|
30
|
-
async createAlipayPageOrder(
|
|
31
|
-
@Body() data: { orderId: string; amount: number },
|
|
32
|
-
) {
|
|
33
|
-
return this.orderService.createAlipayPageOrder(
|
|
34
|
-
data.orderId,
|
|
35
|
-
data.amount,
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
@Get(':orderId/status')
|
|
40
|
-
async queryOrderStatus(@Param('orderId') orderId: string) {
|
|
41
|
-
return this.orderService.queryOrderStatus(orderId);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
@Post(':orderId/refund')
|
|
45
|
-
async refundOrder(
|
|
46
|
-
@Param('orderId') orderId: string,
|
|
47
|
-
@Body() data: { refundId: string; amount: number; reason?: string },
|
|
48
|
-
) {
|
|
49
|
-
return this.orderService.refundOrder(
|
|
50
|
-
orderId,
|
|
51
|
-
data.refundId,
|
|
52
|
-
data.amount,
|
|
53
|
-
data.reason,
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
}
|