|
@@ -4,15 +4,14 @@ import { NextFunction, Context } from '@midwayjs/koa';
|
|
|
import {
|
|
|
IMiddleware,
|
|
|
IMidwayApplication,
|
|
|
- Init,
|
|
|
InjectClient,
|
|
|
} from '@midwayjs/core';
|
|
|
import { CachingFactory, MidwayCache } from '@midwayjs/cache-manager';
|
|
|
-import { SunPayAdapter } from '../../payment/adapter/sunpay.adapter';
|
|
|
import * as crypto from 'crypto';
|
|
|
import { InjectEntityModel } from '@midwayjs/typeorm';
|
|
|
import { MerchantEntity } from '../../payment/entity/merchant';
|
|
|
import { Repository } from 'typeorm';
|
|
|
+import {CustomerEntity} from "../../payment/entity/customer";
|
|
|
|
|
|
/**
|
|
|
* 签名验证
|
|
@@ -30,6 +29,9 @@ export class BaseAuthorityMiddleware
|
|
|
@InjectClient(CachingFactory, 'default')
|
|
|
midwayCache: MidwayCache;
|
|
|
|
|
|
+ @InjectEntityModel(CustomerEntity)
|
|
|
+ customerEntity: Repository<CustomerEntity>;
|
|
|
+
|
|
|
@InjectEntityModel(MerchantEntity)
|
|
|
merchantEntity: Repository<MerchantEntity>;
|
|
|
|
|
@@ -44,60 +46,121 @@ export class BaseAuthorityMiddleware
|
|
|
resolve() {
|
|
|
return async (ctx: Context, next: NextFunction) => {
|
|
|
// 签名校验
|
|
|
- let { header, url } = ctx;
|
|
|
- const adminUrl = '/api/v1/';
|
|
|
- // 路由地址为 api/v1 前缀的 需要权限校验
|
|
|
- if (url.includes(adminUrl)) {
|
|
|
- const {
|
|
|
- 'va-key': vaKey = '',
|
|
|
- 'va-timestamp': vaTimestamp = '',
|
|
|
- 'va-nonce': vaNonce = '',
|
|
|
- 'va-sign': vaSign = '',
|
|
|
- } = header;
|
|
|
- const params =
|
|
|
- ctx?.req.method === 'GET' ? ctx?.request.query : ctx?.request.body;
|
|
|
-
|
|
|
- const merchantInfo = await this.merchantEntity.findOne({
|
|
|
- where: {
|
|
|
- apiSecret: `${vaKey}`,
|
|
|
- },
|
|
|
- });
|
|
|
-
|
|
|
- // 商户不存在或者说商户被禁用都提示失败
|
|
|
- if (!merchantInfo || merchantInfo.status != 1) {
|
|
|
- ctx.status = 401;
|
|
|
- ctx.body = {
|
|
|
- code: ctx.status,
|
|
|
- message: '签名不匹配,认证失败',
|
|
|
- };
|
|
|
- return;
|
|
|
+ let { header } = ctx;
|
|
|
+ const {
|
|
|
+ 'fusionpay-key': vaKey = '',
|
|
|
+ 'fusionpay-timestamp': vaTimestamp = '',
|
|
|
+ 'fusionpay-nonce': vaNonce = '',
|
|
|
+ 'fusionpay-sign': vaSign = '',
|
|
|
+ } = header;
|
|
|
+ const params =
|
|
|
+ ctx?.req.method === 'GET' ? ctx?.request.query : ctx?.request.body;
|
|
|
+ const merchantInfo = await this.merchantEntity.findOne({
|
|
|
+ where: {
|
|
|
+ apiSecret: `${vaKey}`,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ let customer = await this.customerEntity.findOne({
|
|
|
+ where: {
|
|
|
+ merchantId: merchantInfo.mchId,
|
|
|
+ out_user_id: null
|
|
|
+ },
|
|
|
+ })
|
|
|
+ // TODO 后续在中间件加入对指定用户的权限验证
|
|
|
+ // let merchantCustomer = ''
|
|
|
+
|
|
|
+ // 商户不存在或者说商户被禁用都提示失败
|
|
|
+ if (!merchantInfo || merchantInfo.status != 1 || !vaKey || !customer || customer.status !== 'ACTIVE') {
|
|
|
+ let message = '';
|
|
|
+ if (!vaKey) {
|
|
|
+ message = 'API Secret 不存在!';
|
|
|
+ }
|
|
|
+ if (!merchantInfo && !message) {
|
|
|
+ message = '当前商户不存在';
|
|
|
+ }
|
|
|
+ if (merchantInfo?.status != 1 && !message) {
|
|
|
+ message = '当前商户已锁定';
|
|
|
+ }
|
|
|
+ if (!customer && !message) {
|
|
|
+ message = '当前商户暂无认证';
|
|
|
+ }
|
|
|
+ if (!customer && !message) {
|
|
|
+ message = '当前商户暂无认证';
|
|
|
}
|
|
|
|
|
|
- // 2. 生成签名
|
|
|
- const sign = this.generateSignature(
|
|
|
- `${vaTimestamp}`,
|
|
|
- `${vaNonce}`,
|
|
|
- JSON.stringify(params),
|
|
|
- `${vaKey}`
|
|
|
- );
|
|
|
- if (sign !== vaSign) {
|
|
|
- ctx.status = 401;
|
|
|
- ctx.body = {
|
|
|
- code: ctx.status,
|
|
|
- message: '签名不匹配,认证失败',
|
|
|
- };
|
|
|
- return;
|
|
|
+ if (customer?.status === 'PENDDING' && !message) {
|
|
|
+ message = '当前商户认证还在审核中';
|
|
|
}
|
|
|
- /*如果说商户没有认证,我们需要提示商户进行认证*/
|
|
|
- ctx.admin.merchant = merchantInfo
|
|
|
- ctx.admin.openApi = true
|
|
|
- await next();
|
|
|
+
|
|
|
+ if (customer?.status === 'FAILED' && !message) {
|
|
|
+ message = '当前商户认证还在审核被驳回';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (customer?.status === 'SUSPENDED' && !message) {
|
|
|
+ message = '当前商户认证资格被冻结';
|
|
|
+ }
|
|
|
+ if (customer?.status === 'CLOSED' && !message) {
|
|
|
+ message = '当前商户认证资格被回收';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!message) {
|
|
|
+ message = '签名不匹配,认证失败';
|
|
|
+ }
|
|
|
+
|
|
|
+ ctx.status = 401;
|
|
|
+ ctx.body = {
|
|
|
+ code: ctx.status,
|
|
|
+ message: message,
|
|
|
+ };
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 生成签名
|
|
|
+ const sign = this.generateSignature(
|
|
|
+ `${vaTimestamp}`,
|
|
|
+ `${vaNonce}`,
|
|
|
+ JSON.stringify(params),
|
|
|
+ `${vaKey}`
|
|
|
+ );
|
|
|
+
|
|
|
+ if (`${sign}`.toLocaleUpperCase() !== `${vaSign}`.toLocaleUpperCase()) {
|
|
|
+ ctx.status = 401;
|
|
|
+ ctx.body = {
|
|
|
+ code: ctx.status,
|
|
|
+ message: '签名不匹配,认证失败',
|
|
|
+ };
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
+ /*如果说商户没有认证,我们需要提示商户进行认证*/
|
|
|
+ // this.customerEntity
|
|
|
+ /*// TODO 本地测试用
|
|
|
+ const merchantInfo = await this.merchantEntity.findOne({
|
|
|
+ where: {
|
|
|
+ mchId: `ddddd`,
|
|
|
+ },
|
|
|
+ });*/
|
|
|
+
|
|
|
+ ctx.admin = {
|
|
|
+ ...ctx.admin,
|
|
|
+ merchant: merchantInfo,
|
|
|
+ openApi: true,
|
|
|
+ };
|
|
|
await next();
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+ match(ctx: Context): boolean {
|
|
|
+ // 下面的匹配到的路由会执行此中间件
|
|
|
+ if (ctx.url.includes('/api/v1/')) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ static getName(): string {
|
|
|
+ return 'openApi';
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 生成签名
|
|
|
* @see https://docs-merchant.sunpay.pro/guide
|
|
@@ -109,7 +172,6 @@ export class BaseAuthorityMiddleware
|
|
|
.update(payload)
|
|
|
.digest('hex')
|
|
|
.toUpperCase();
|
|
|
-
|
|
|
return signature;
|
|
|
}
|
|
|
}
|