xiongzhu 2 yıl önce
ebeveyn
işleme
2dd0309faa

+ 2 - 1
.env

@@ -64,4 +64,5 @@ MONGO_DB_DATABASE=chillgpt
 WX_APP_ID=wx55dfde4d982b43fb
 WX_APP_SECRET=33097584ed2af75d5fd04ce15ad1eda8
 WX_MCH_ID=1642294106
-WX_MCH_KEY=SRhVwVEHxx7oR87S8Ce2kQeBtnftjCte
+WX_MCH_KEY=SRhVwVEHxx7oR87S8Ce2kQeBtnftjCte
+WX_MCH_CERT_SERIAL=796F265CC39597AC14948CEA60CCDE603F31CB20

+ 2 - 1
.env.production

@@ -64,4 +64,5 @@ MONGO_DB_DATABASE=chillgpt
 WX_APP_ID=wx55dfde4d982b43fb
 WX_APP_SECRET=33097584ed2af75d5fd04ce15ad1eda8
 WX_MCH_ID=1642294106
-WX_MCH_KEY=SRhVwVEHxx7oR87S8Ce2kQeBtnftjCte
+WX_MCH_KEY=SRhVwVEHxx7oR87S8Ce2kQeBtnftjCte
+WX_MCH_CERT_SERIAL=796F265CC39597AC14948CEA60CCDE603F31CB20

Dosya farkı çok büyük olduğundan ihmal edildi
+ 396 - 95
graph.json


+ 1 - 0
package.json

@@ -64,6 +64,7 @@
     "rxjs": "^7.8.0",
     "tnwx": "^2.5.6",
     "typeorm": "^0.3.12",
+    "wechatpay-axios-plugin": "^0.8.7",
     "yup": "^1.0.0"
   },
   "devDependencies": {

+ 0 - 18
src/auth/auth.controller.spec.ts

@@ -1,18 +0,0 @@
-import { Test, TestingModule } from '@nestjs/testing';
-import { AuthController } from './auth.controller';
-
-describe('AuthController', () => {
-  let controller: AuthController;
-
-  beforeEach(async () => {
-    const module: TestingModule = await Test.createTestingModule({
-      controllers: [AuthController],
-    }).compile();
-
-    controller = module.get<AuthController>(AuthController);
-  });
-
-  it('should be defined', () => {
-    expect(controller).toBeDefined();
-  });
-});

+ 1 - 1
src/auth/auth.module.ts

@@ -4,7 +4,7 @@ import { ConfigModule } from '@nestjs/config'
 import jwtConfig from './jwt.config'
 import { JwtModule } from '@nestjs/jwt'
 import { JwtStrategy } from './jwt.strategy'
-import { UsersModule } from 'src/users/users.module'
+import { UsersModule } from '../users/users.module'
 import { AuthController } from './auth.controller'
 import { APP_GUARD } from '@nestjs/core'
 import { JwtAuthGuard } from './jwt-auth.guard'

+ 0 - 18
src/auth/auth.service.spec.ts

@@ -1,18 +0,0 @@
-import { Test, TestingModule } from '@nestjs/testing';
-import { AuthService } from './auth.service';
-
-describe('AuthService', () => {
-  let service: AuthService;
-
-  beforeEach(async () => {
-    const module: TestingModule = await Test.createTestingModule({
-      providers: [AuthService],
-    }).compile();
-
-    service = module.get<AuthService>(AuthService);
-  });
-
-  it('should be defined', () => {
-    expect(service).toBeDefined();
-  });
-});

+ 1 - 1
src/auth/auth.service.ts

@@ -1,7 +1,7 @@
 import { PhoneLoginDto } from './dto/login.dto'
 import { Injectable } from '@nestjs/common'
 import { JwtService } from '@nestjs/jwt'
-import { UsersService } from 'src/users/users.service'
+import { UsersService } from '../users/users.service'
 
 @Injectable()
 export class AuthService {

+ 1 - 1
src/auth/roles.guard.ts

@@ -1,7 +1,7 @@
 import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'
 import { Reflector } from '@nestjs/core'
 import { HAS_ANY_ROLES_KEY, HAS_ROLES_KEY } from './roles.decorator'
-import { Role } from 'src/model/role.enum'
+import { Role } from '../model/role.enum'
 
 @Injectable()
 export class RolesGuard implements CanActivate {

+ 1 - 1
src/chat/chat.controller.ts

@@ -2,7 +2,7 @@ import { Controller, Get, Post, Req, Res, Sse, HttpCode } from '@nestjs/common'
 import { ApiTags } from '@nestjs/swagger'
 import { ChatService } from './chat.service'
 import { Observable } from 'rxjs'
-import { Public } from 'src/auth/public.decorator'
+import { Public } from '../auth/public.decorator'
 
 @ApiTags('chat')
 @Controller('chat')

+ 0 - 18
src/file/file.controller.spec.ts

@@ -1,18 +0,0 @@
-import { Test, TestingModule } from '@nestjs/testing';
-import { FileController } from './file.controller';
-
-describe('FileController', () => {
-  let controller: FileController;
-
-  beforeEach(async () => {
-    const module: TestingModule = await Test.createTestingModule({
-      controllers: [FileController],
-    }).compile();
-
-    controller = module.get<FileController>(FileController);
-  });
-
-  it('should be defined', () => {
-    expect(controller).toBeDefined();
-  });
-});

+ 1 - 1
src/file/file.controller.ts

@@ -1,7 +1,7 @@
 import { FileService } from './file.service'
 import { Controller, UploadedFile, UseInterceptors } from '@nestjs/common'
 import { ApiTags } from '@nestjs/swagger'
-import { Public } from 'src/auth/public.decorator'
+import { Public } from '../auth/public.decorator'
 import { Post } from '@nestjs/common'
 import { FileInterceptor } from '@nestjs/platform-express'
 

+ 1 - 1
src/file/file.module.ts

@@ -1,7 +1,7 @@
 import { Module } from '@nestjs/common'
 import { FileService } from './file.service'
 import { FileController } from './file.controller'
-import { AliyunModule } from 'src/aliyun/aliyun.module'
+import { AliyunModule } from '../aliyun/aliyun.module'
 
 @Module({
     imports: [AliyunModule],

+ 0 - 18
src/file/file.service.spec.ts

@@ -1,18 +0,0 @@
-import { Test, TestingModule } from '@nestjs/testing';
-import { FileService } from './file.service';
-
-describe('FileService', () => {
-  let service: FileService;
-
-  beforeEach(async () => {
-    const module: TestingModule = await Test.createTestingModule({
-      providers: [FileService],
-    }).compile();
-
-    service = module.get<FileService>(FileService);
-  });
-
-  it('should be defined', () => {
-    expect(service).toBeDefined();
-  });
-});

+ 1 - 1
src/file/file.service.ts

@@ -1,5 +1,5 @@
 import { Injectable, Logger } from '@nestjs/common'
-import { AliyunService } from 'src/aliyun/aliyun.service'
+import { AliyunService } from '../aliyun/aliyun.service'
 import * as randomstring from 'randomstring'
 import { format } from 'date-fns'
 

+ 3 - 3
src/membership/membership.admin.controller.ts

@@ -1,9 +1,9 @@
-import { Public } from 'src/auth/public.decorator'
+import { Public } from '../auth/public.decorator'
 import { MembershipService } from './membership.service'
 import { Body, Controller, Get, Post, Req } from '@nestjs/common'
 import { MemberPlanDto } from './dto/memberPlan.dto'
-import { HasRoles } from 'src/auth/roles.decorator'
-import { Role } from 'src/model/role.enum'
+import { HasRoles } from '../auth/roles.decorator'
+import { Role } from '../model/role.enum'
 
 @Controller('/admin/membership')
 @HasRoles(Role.Admin)

+ 1 - 1
src/membership/membership.controller.ts

@@ -1,4 +1,4 @@
-import { Public } from 'src/auth/public.decorator'
+import { Public } from '../auth/public.decorator'
 import { MembershipService } from './membership.service'
 import { Body, Controller, Get, Post, Req } from '@nestjs/common'
 

+ 1 - 1
src/sms/sms.controller.ts

@@ -2,7 +2,7 @@ import { Body, Controller, Post } from '@nestjs/common'
 import { ApiTags } from '@nestjs/swagger'
 import { SendCodeDto, VerifyCodeDto } from './dto/sms.dto'
 import { SmsService } from './sms.service'
-import { Public } from 'src/auth/public.decorator'
+import { Public } from '../auth/public.decorator'
 
 @ApiTags('sms')
 @Controller('/sms')

+ 1 - 1
src/sms/sms.service.ts

@@ -1,6 +1,6 @@
 import { Injectable } from '@nestjs/common'
 import { InjectRepository } from '@nestjs/typeorm'
-import { AliyunService } from 'src/aliyun/aliyun.service'
+import { AliyunService } from '../aliyun/aliyun.service'
 import { SmsRecord } from './entities/sms.entity'
 import { Repository } from 'typeorm'
 import { SendCodeDto } from './dto/sms.dto'

+ 2 - 2
src/users/users.controller.ts

@@ -16,8 +16,8 @@ import { UserProfileDto } from './dto/user-profile.dto'
 import { UserUpdateDto } from './dto/user-update.dto'
 import { IUsers } from './interfaces/users.interface'
 import { ApiTags } from '@nestjs/swagger'
-import { HasRoles } from 'src/auth/roles.decorator'
-import { Role } from 'src/model/role.enum'
+import { HasRoles } from '../auth/roles.decorator'
+import { Role } from '../model/role.enum'
 
 @ApiTags('users')
 @Controller('users')

+ 1 - 1
src/users/users.service.ts

@@ -14,7 +14,7 @@ import { UserDto } from './dto/user.dto'
 import { UserProfileDto } from './dto/user-profile.dto'
 import { UserUpdateDto } from './dto/user-update.dto'
 import { HashingService } from '../shared/hashing/hashing.service'
-import { SmsService } from 'src/sms/sms.service'
+import { SmsService } from '../sms/sms.service'
 import * as randomstring from 'randomstring'
 
 @Injectable()

+ 2 - 1
src/weixin/weixin.config.ts

@@ -10,6 +10,7 @@ export default registerAs('weixin', () => {
         appId: configService.get<string>('WX_APP_ID'),
         appSecret: configService.get<string>('WX_APP_SECRET'),
         mchId: configService.get<string>('WX_MCH_ID'),
-        mchKey: configService.get<string>('WX_MCH_KEY')
+        mchKey: configService.get<string>('WX_MCH_KEY'),
+        certSerial: configService.get<string>('WX_MCH_CERT_SERIAL'),
     }
 })

+ 15 - 0
src/weixin/weixin.controller.ts

@@ -9,4 +9,19 @@ export class WeixinController {
     public async getAccessToken() {
         return await this.weixinService.getAccessToken()
     }
+
+    @Get('/redirectUrl')
+    public getRedirectUrl(url: string) {
+        return this.weixinService.getRedirectUrl()
+    }
+
+    @Get('/code2openid')
+    public async code2openid(code: string) {
+        return await this.weixinService.code2oenId(code)
+    }
+
+    @Get('/pay')    
+    public async pay() {
+        return await this.weixinService.pay()
+    }
 }

+ 29 - 0
src/weixin/weixin.service.spec.ts

@@ -0,0 +1,29 @@
+import { Test } from '@nestjs/testing'
+import { WeixinService } from './weixin.service'
+import { WeixinModule } from './weixin.module'
+import { AppModule } from '../app.module'
+import { ConfigModule } from '@nestjs/config'
+import { TypeOrmModule } from '@nestjs/typeorm'
+import weixinConfig from './weixin.config'
+import { AccessToken } from './entities/accessToken.entity'
+
+describe('WeixinService', () => {
+    let weixinService: WeixinService
+
+    beforeEach(async () => {
+        const moduleRef = await Test.createTestingModule({
+            imports: [AppModule, ConfigModule.forFeature(weixinConfig), TypeOrmModule.forFeature([AccessToken])],
+            controllers: [],
+            providers: [WeixinService]
+        }).compile()
+
+        weixinService = moduleRef.get<WeixinService>(WeixinService)
+    })
+
+    describe('getRedirectUrl', () => {
+        it('getRedirectUrl', async () => {
+            expect(weixinService).toBeDefined()
+            console.log(weixinService.getRedirectUrl())
+        })
+    })
+})

+ 125 - 1
src/weixin/weixin.service.ts

@@ -1,11 +1,28 @@
 import { Inject, Injectable } from '@nestjs/common'
 import weixinConfig from './weixin.config'
 import { ConfigType } from '@nestjs/config'
-import { ApiConfig, ApiConfigKit, AccessTokenApi } from 'tnwx'
+import {
+    ApiConfig,
+    ApiConfigKit,
+    AccessTokenApi,
+    SnsAccessTokenApi,
+    ScopeEnum,
+    PayKit,
+    WX_DOMAIN,
+    WX_API_TYPE,
+    Kits,
+    WX_TRADE_TYPE,
+    SIGN_TYPE,
+    HttpKit,
+    WxPayApiConifgKit,
+    WxPayApiConfig
+} from 'tnwx'
 import { InjectRepository } from '@nestjs/typeorm'
 import { AccessToken } from './entities/accessToken.entity'
 import { LessThan, Not, Repository } from 'typeorm'
 import { addSeconds } from 'date-fns'
+import * as fs from 'node:fs'
+const { Wechatpay } = require('wechatpay-axios-plugin');
 
 @Injectable()
 export class WeixinService {
@@ -21,6 +38,26 @@ export class WeixinService {
         ApiConfigKit.devMode = true
         // 设置当前应用
         ApiConfigKit.setCurrentAppId(apiConfig.getAppId)
+
+
+
+        const wxpay = new Wechatpay({
+            mchid: weixinConfiguration.mchId,
+            serial: merchantCertificateSerial,
+            privateKey: merchantPrivateKeyInstance,
+            certs: { [platformCertificateSerial]: platformCertificateInstance, },
+            // 使用APIv2时,需要至少设置 `secret`字段,示例代码未开启
+            // APIv2密钥(32字节)
+            // secret: 'your_merchant_secret_key_string',
+            // // 接口不要求证书情形,例如仅收款merchant对象参数可选
+            // merchant: {
+            //   cert: readFileSync('/path/to/merchant/apiclient_cert.pem'),
+            //   key: merchantPrivateKeyInstance,
+            //   // or
+            //   // passphrase: 'your_merchant_id',
+            //   // pfx: fs.readFileSync('/your/merchant/cert/apiclient_cert.p12'),
+            // },
+          });
     }
 
     async getAccessToken() {
@@ -44,4 +81,91 @@ export class WeixinService {
         await this.accessTokenRepository.delete({ id: Not(newToken.id) })
         return newToken.accessToken
     }
+
+    getRedirectUrl() {
+        return SnsAccessTokenApi.getAuthorizeUrl(
+            'https://chillgpt.raexmeta.com/ui/#/home',
+            ScopeEnum.SNSAPI_BASE,
+            'pay'
+        )
+    }
+
+    async code2oenId(code: string) {
+        const res = await SnsAccessTokenApi.getSnsAccessToken(code)
+        return res.getOpenid
+    }
+
+    async pay() {
+        let reqObj = {
+            appid: this.weixinConfiguration.appId,
+            mch_id: this.weixinConfiguration.mchId,
+            nonce_str: Kits.generateStr(), //生成随机字符串
+            body: 'IJPay 让支付触手可及',
+            attach: 'TNWX 微信系开发脚手架',
+            out_trade_no: Kits.generateStr(),
+            total_fee: 666,
+            spbill_create_ip: '110.172.10.1',
+            notify_url: 'https://chillgpt.raexmeta.com/api/weixin/notify',
+            trade_type: WX_TRADE_TYPE.JSAPI,
+            sign_type: SIGN_TYPE.SIGN_TYPE_MD5
+        }
+        let xml = await Kits.generateSignedXml(reqObj, this.weixinConfiguration.mchKey, SIGN_TYPE.SIGN_TYPE_MD5)
+        PayKit.buildRepSignMessage
+        let result = await PayKit.exePost(
+            WX_DOMAIN.CHINA, //
+            WX_API_TYPE.UNIFIED_ORDER,
+            this.weixinConfiguration.mchId,
+            this.weixinConfiguration.certSerial,
+            fs.readFileSync('/Users/drew/Downloads/1642294106_20230418_cert 2/apiclient_key.pem'),
+            xml
+        )
+        console.log(result)
+    }
+
+    async pay1() {
+        try {
+            let result = await PayKit.exeGet(
+                WX_DOMAIN.CHINA, //
+                WX_API_TYPE.GET_CERTIFICATES,
+                this.weixinConfiguration.mchId,
+                this.weixinConfiguration.certSerial,
+                fs.readFileSync('/Users/drew/Downloads/1642294106_20230418_cert 2/apiclient_key.pem')
+            )
+            console.log(`result.data:${result.data}`)
+
+            // 应答报文主体
+            let data = JSON.stringify(result.data)
+            // 应答状态码
+            console.log(`status:${result.status}`)
+            console.log(`data:${data}`)
+            // http 请求头
+            let headers = result.headers
+            // 证书序列号
+            let serial = headers['wechatpay-serial']
+            // 应答时间戳
+            let timestamp = headers['wechatpay-timestamp']
+            // 应答随机串
+            let nonce = headers['wechatpay-nonce']
+            // 应答签名
+            let signature = headers['wechatpay-signature']
+
+            console.log(`serial:\n${serial}`)
+            console.log(`timestamp:\n${timestamp}`)
+            console.log(`nonce:\n${nonce}`)
+            console.log(`signature:\n${signature}`)
+
+            // 根据序列号查证书  验证签名
+            // let verifySignature: boolean = PayKit.verifySignature(signature, data, nonce, timestamp, wxPublicKey)
+            let verifySignature: boolean = PayKit.verifySign(
+                headers,
+                data,
+                fs.readFileSync('/Users/drew/Downloads/1642294106_20230418_cert 2/apiclient_cert.pem')
+            )
+            console.log(`verifySignature:${verifySignature}`)
+
+            return data
+        } catch (error) {
+            console.log(error)
+        }
+    }
 }

+ 1 - 4
test/app.e2e-spec.ts

@@ -1,8 +1,7 @@
 import { Test, TestingModule } from '@nestjs/testing';
 import * as request from 'supertest';
-import { AppModule } from './../src/app.module';
+import { AppModule } from '../src/app.module';
 import { HttpStatus, ValidationPipe } from '@nestjs/common';
-import { AccessTokenGuard } from '../src/iam/login/guards/access-token/access-token.guard';
 
 describe('App (e2e)', () => {
   let app;
@@ -12,8 +11,6 @@ describe('App (e2e)', () => {
     const moduleFixture: TestingModule = await Test.createTestingModule({
       imports: [AppModule],
     })
-      .overrideGuard(AccessTokenGuard)
-      .useValue({ canActivate: () => true })
       .compile();
 
     app = moduleFixture.createNestApplication();

+ 1 - 4
test/change-password/change-password.e2e-spec.ts

@@ -1,13 +1,12 @@
 import { Test, TestingModule } from '@nestjs/testing';
 import * as request from 'supertest';
-import { AppModule } from './../../src/app.module';
+import { AppModule } from '../../src/app.module';
 import { MailerService } from '../../src/shared/mailer/mailer.service';
 import {
   BadRequestException,
   HttpStatus,
   ValidationPipe,
 } from '@nestjs/common';
-import { AccessTokenGuard } from '../../src/iam/login/guards/access-token/access-token.guard';
 
 const user = {
   email: 'test@example.com',
@@ -26,8 +25,6 @@ describe('App (e2e)', () => {
       .useValue({
         sendMail: jest.fn(() => true),
       })
-      .overrideGuard(AccessTokenGuard)
-      .useValue({ canActivate: () => true })
       .compile();
 
     app = moduleFixture.createNestApplication();

+ 0 - 12
test/forgot-password/forgot-password.e2e-spec.ts

@@ -7,7 +7,6 @@ import {
   HttpStatus,
   ValidationPipe,
 } from '@nestjs/common';
-import { ForgotPasswordDto } from 'src/iam/forgot-password/dto/forgot-password.dto';
 import { UserDto } from '../../src/users/dto/user.dto';
 
 const user = {
@@ -64,17 +63,6 @@ describe('App (e2e)', () => {
         });
     });
 
-    it('should generate a new password per user if they have forgotten their password.', () => {
-      return request(app.getHttpServer())
-        .post('/api/auth/forgot-password')
-        .send(user as ForgotPasswordDto)
-        .then(({ body }) => {
-          expect(body).toEqual({
-            message: 'Request Reset Password Successfully!',
-            status: 200,
-          });
-        });
-    });
   });
 
   it('should throw an error for a bad email', () => {

+ 24 - 1
yarn.lock

@@ -1830,6 +1830,13 @@ axios@^0.19.0:
   dependencies:
     follow-redirects "1.5.10"
 
+axios@^0.21.2:
+  version "0.21.4"
+  resolved "https://registry.npmmirror.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
+  integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==
+  dependencies:
+    follow-redirects "^1.14.0"
+
 axios@^1.3.5:
   version "1.3.5"
   resolved "https://registry.npmmirror.com/axios/-/axios-1.3.5.tgz#e07209b39a0d11848e3e341fa087acd71dadc542"
@@ -3107,7 +3114,7 @@ follow-redirects@1.5.10:
   dependencies:
     debug "=3.1.0"
 
-follow-redirects@^1.15.0:
+follow-redirects@^1.14.0, follow-redirects@^1.15.0:
   version "1.15.2"
   resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
   integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
@@ -6558,6 +6565,14 @@ webpack@5.76.2:
     watchpack "^2.4.0"
     webpack-sources "^3.2.3"
 
+wechatpay-axios-plugin@^0.8.7:
+  version "0.8.7"
+  resolved "https://registry.npmmirror.com/wechatpay-axios-plugin/-/wechatpay-axios-plugin-0.8.7.tgz#3d0a051fe5cac98571222153ecfaf08158e12d50"
+  integrity sha512-Xdr/i5z8/AWjZguFgNcnz/6AKZMayMouOMlwLOgP2nqPm4c6e6Vkl6niwNiH500ixvZHjeVwhjvgd43/eADgig==
+  dependencies:
+    axios "^0.21.2"
+    xml2js "^0.5.0"
+
 whatwg-fetch@^3.4.1:
   version "3.6.2"
   resolved "https://registry.npmmirror.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c"
@@ -6647,6 +6662,14 @@ xml2js@^0.4.16, xml2js@^0.4.22:
     sax ">=0.6.0"
     xmlbuilder "~11.0.0"
 
+xml2js@^0.5.0:
+  version "0.5.0"
+  resolved "https://registry.npmmirror.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7"
+  integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==
+  dependencies:
+    sax ">=0.6.0"
+    xmlbuilder "~11.0.0"
+
 xmlbuilder@~11.0.0:
   version "11.0.1"
   resolved "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor