123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- import { App, Config, Inject, Middleware } from '@midwayjs/decorator';
- import { CoolUrlTagData } from '@cool-midway/core';
- import { NextFunction, Context } from '@midwayjs/koa';
- import { IMiddleware, IMidwayApplication, InjectClient } from '@midwayjs/core';
- import { CachingFactory, MidwayCache } from '@midwayjs/cache-manager';
- 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';
- /**
- * 签名验证
- */
- @Middleware()
- export class BaseAuthorityMiddleware
- implements IMiddleware<Context, NextFunction>
- {
- @Config('koa.globalPrefix')
- prefix;
- @Config('module.base')
- jwtConfig;
- @InjectClient(CachingFactory, 'default')
- midwayCache: MidwayCache;
- @InjectEntityModel(CustomerEntity)
- customerEntity: Repository<CustomerEntity>;
- @InjectEntityModel(MerchantEntity)
- merchantEntity: Repository<MerchantEntity>;
- @Inject()
- coolUrlTagData: CoolUrlTagData;
- @App()
- app: IMidwayApplication;
- ignoreUrls: string[] = [];
- resolve() {
- return async (ctx: Context, next: NextFunction) => {
- if (ctx.url.includes('/api/open/sign') || ctx.url.includes('api/open/easypay-webhook')) {
- await next();
- return;
- }
- // 签名校验
- let { header } = ctx;
- const {
- 'fusionpay-key': vaKey = '',
- 'fusionpay-timestamp': vaTimestamp = '',
- 'fusionpay-nonce': vaNonce = '',
- 'fusionpay-sign': vaSign = '',
- } = header;
- if (!vaKey || !vaTimestamp || !vaNonce || !vaSign) {
- ctx.status = 401;
- ctx.body = {
- code: ctx.status,
- message: '当前认证信息不存在,请确认之后重新发送',
- };
- return;
- }
- const params =
- ctx?.req.method === 'GET' || ctx.url.includes('/v1/upload')
- ? /* ctx?.request.query */ ''
- : ctx?.request.body;
- const merchantInfo = await this.merchantEntity.findOne({
- where: [
- {
- apikey: `${vaKey}`,
- },
- {
- 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') {
- if (!merchantInfo || merchantInfo.status != 1 || !vaKey || !customer) {
- let message = '';
- if (!vaKey) {
- message = 'API Secret 不存在!';
- }
- if (!merchantInfo && !message) {
- message = '当前商户不存在';
- }
- if (merchantInfo?.status != 1 && !message) {
- message = '当前商户已锁定';
- }
- if (!customer && !message && !ctx.url.includes('/api/open/')) {
- message = '当前商户暂无认证';
- }
- /*if (customer?.status === 'PENDDING' && !message) {
- message = '当前商户认证还在审核中';
- }
- if (customer?.status === 'FAILED' && !message) {
- message = '当前商户认证还在审核被驳回';
- }
- if (customer?.status === 'SUSPENDED' && !message) {
- message = '当前商户认证资格被冻结';
- }
- if (customer?.status === 'CLOSED' && !message) {
- message = '当前商户认证资格被回收';
- }*/
- if (message) {
- ctx.status = 401;
- ctx.body = {
- code: ctx.status,
- message: message,
- };
- return;
- }
- }
- // 2. 生成签名
- const sign = this.generateSignature(
- `${vaTimestamp}`,
- `${vaNonce}`,
- JSON.stringify(params),
- `${merchantInfo.apiSecret}`
- );
- console.log('9999-=-=',`${sign}`.toLocaleUpperCase())
- console.log('1000-=-=',`${vaSign}`.toLocaleUpperCase())
- 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/') || ctx.url.includes('/api/open/')) {
- return true;
- }
- }
- static getName(): string {
- return 'openApi';
- }
- /**
- * 生成签名
- * @see https://docs-merchant.sunpay.pro/guide
- */
- generateSignature(timestamp, nonce, body = '', secret = '') {
- const payload = `${timestamp}${nonce}${body}`;
- const signature = crypto
- .createHmac('sha256', secret)
- .update(payload)
- .digest('hex')
- .toUpperCase();
- return signature;
- }
- }
|