返回 AiToEarn
wxPay.service.ts
根目录 / project / aitoearn-electron / server / src / lib / wx / wxPay.service.ts
1 /*
2 * @Author: nevin
3 * @Date: 2024-06-17 16:12:56
4 * @LastEditTime: 2024-07-30 17:50:07
5 * @LastEditors: nevin
6 * @Description: 微信服务
7 */
8 import {
9 HttpException,
10 HttpStatus,
11 Inject,
12 Injectable,
13 Logger,
14 } from '@nestjs/common';
15 import { ConfigService } from '@nestjs/config';
16 import WxPay from 'wechatpay-node-v3';
17 import { WECHAT_PAY_MANAGER } from 'nest-wechatpay-node-v3';
18 import { sleep } from '../../util';
19
20 @Injectable()
21 export class WxPayService {
22 appId = '';
23 mchId = '';
24 notifyUrl = '';
25 key = '';
26 constructor(
27 private readonly configService: ConfigService,
28 @Inject(WECHAT_PAY_MANAGER) private wxPay: WxPay,
29 ) {
30 this.appId = this.configService.get('WX_CONFIG.APP_ID');
31 this.mchId = this.configService.get('WX_CONFIG.MCH_ID');
32 this.notifyUrl = this.configService.get('WX_CONFIG.NOTIFY_URL');
33 this.key = this.configService.get('WX_CONFIG.KEY');
34 }
35
36 /**
37 * 小程序下单
38 * @param openid
39 * @param money
40 * @returns
41 */
42 async miniAppToPay(
43 openid: string,
44 money: number,
45 outTradeNo: string,
46 description: string,
47 ): Promise<any> {
48 if (process.env.NODE_ENV !== 'production') {
49 money = 0.01;
50 }
51 const params = {
52 appid: this.appId,
53 mchid: this.mchId,
54 description: description,
55 out_trade_no: outTradeNo,
56 notify_url: this.notifyUrl,
57 amount: {
58 total: money * 100,
59 },
60 payer: {
61 openid: openid,
62 },
63 };
64 const result = await this.wxPay.transactions_jsapi(params);
65 if (result.error) {
66 Logger.error(result.error);
67 return null;
68 }
69
70 return result.data;
71 }
72
73 async appToPay(money: number, outTradeNo: string, description: string) {
74 const params = {
75 appid: this.appId,
76 mchid: this.mchId,
77 description: description,
78 out_trade_no: outTradeNo,
79 notify_url: this.notifyUrl,
80 amount: {
81 total: money * 100,
82 // total: money, // TODO: 测试使用1分钱
83 },
84 payer: {},
85 };
86 const result = await this.wxPay.transactions_app(params);
87 if (result.error) {
88 Logger.error(result.error);
89 return null;
90 }
91
92 return result.data;
93 }
94
95 async decipherGcm(
96 ciphertext: string,
97 associated_data: string,
98 nonce: string,
99 key?: string,
100 ): Promise<{
101 out_trade_no: string;
102 trade_state: string; // 'SUCCESS'
103 amount: {
104 total: number;
105 };
106 }> {
107 try {
108 if (!key) key = this.key;
109
110 return this.wxPay.decipher_gcm(ciphertext, associated_data, nonce, key);
111 } catch (error) {
112 console.log('===decipherGcm==', error);
113 Logger.error(error);
114
115 return null;
116 }
117 }
118
119 /**
120 * 发送红包
121 * @param openId
122 * @param transAmount 金额 单位元
123 * @param outBizNo 业务单号
124 * @param retry
125 * @returns
126 */
127 async sendRedPacket(
128 openId: string,
129 transAmount: number,
130 outBizNo: string,
131 retry = 0,
132 ) {
133 const result = await this.wxPay.batches_transfer({
134 out_batch_no: outBizNo,
135 batch_name: '提现红包',
136 batch_remark: '提现红包',
137 total_amount: transAmount * 100,
138 total_num: 1,
139 transfer_detail_list: [
140 {
141 out_detail_no: outBizNo,
142 transfer_amount: transAmount * 100,
143 transfer_remark: '提现红包',
144 openid: openId,
145 },
146 ],
147 });
148
149 if (result.status === 200) {
150 return result;
151 } else if (result.error === 'SYSTEM_ERROR' && result.status === 500) {
152 Logger.error('系统错误', 'SYSTEM_ERROR', 'wxPayService', result);
153 if (retry > 5) {
154 throw new HttpException('系统错误', HttpStatus.INTERNAL_SERVER_ERROR);
155 }
156 await sleep((retry + 1) * 1000);
157 return await this.sendRedPacket(
158 openId,
159 transAmount,
160 outBizNo,
161 (retry = retry + 1),
162 );
163 } else {
164 Logger.error('系统错误', 'wxPayService', result);
165 return null;
166 }
167 }
168 }
169
169 lines TYPESCRIPT