Skip to content

Commit

Permalink
Merge pull request #12 from notadd/develop
Browse files Browse the repository at this point in the history
chore(release): v0.3.2
  • Loading branch information
dzzzzzy authored Dec 7, 2018
2 parents 27afd65 + 2f4f229 commit 1ffa36f
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 100 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,10 @@ export class PaymentNotifyController {
- [x] **0.1.0** 微信-普通商户版-APP支付
- [x] **0.2.0** 微信-普通商户版-JSAPI支付、微信-普通商户版-Native支付、微信-普通商户版-H5支付、微信-普通商户版-小程序支付
- [x] **0.3.0** 微信-普通商户版-付款码支付
- [ ] **0.4.0** 支付宝-APP支付
- [ ] **0.5.0** 支付宝-当面付
- [ ] **0.6.0** 支付宝-手机网站支付
- [ ] **0.7.0** 支付宝-电脑网站支付
- [ ] **0.4.0** 微信-普通商户版-现金红包
- [ ] **0.5.0** 微信-普通商户版-企业付款
- [ ] **0.6.0** 支付宝-APP支付
- [ ] **0.7.0** 支付宝-当面付
- [ ] **0.8.0** 支付宝-手机网站支付
- [ ] **0.9.0** 支付宝-电脑网站支付
- [ ] **1.0.0** 完善使用说明、发布正式版v1.0.0
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nt-addon-pay",
"version": "0.3.1",
"version": "0.3.2",
"description": "The pay addon for notadd application",
"scripts": {
"start": "ts-node -r tsconfig-paths/register starter/main.ts",
Expand Down
8 changes: 4 additions & 4 deletions src/common/interfaces/addon.config.interface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** 微信支付初始化配置 */
interface PayAddonWeChatConfig {
export interface WeChatPayConfig {
/** 公众账号APPID或应用APPID */
appid: string;
/** 微信支付商户号 */
Expand All @@ -15,14 +15,14 @@ interface PayAddonWeChatConfig {
}

/** 支付宝支付初始化配置 */
interface PayAddonAliConfig {
export interface AliPayConfig {

}

/** 支付插件初始化配置 */
export interface PayAddonConfig {
/** 微信支付初始化配置 */
wechatConfig?: PayAddonWeChatConfig;
wechatConfig?: WeChatPayConfig;
/** 支付宝支付初始化配置 */
aliConfig?: PayAddonAliConfig;
aliConfig?: AliPayConfig;
}
3 changes: 2 additions & 1 deletion src/modules/wechat/constants/wechat.constant.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const WeChatCertificateAgentProvider = 'WeChatCertificateAgentProvider';
export const WeChatPayCertificateAgentProvider = 'WeChatPayCertificateAgentProvider';
export const WeChatPayConfigProvider = 'WeChatPayConfigProvider';
12 changes: 6 additions & 6 deletions src/modules/wechat/services/base.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Inject, Injectable } from '@nestjs/common';
import * as https from 'https';

import { PayAddonConfig, PayAddonConfigProvider } from '../../../common';
import { WeChatCertificateAgentProvider } from '../constants/wechat.constant';
import { WeChatPayConfig } from '../../../common';
import { WeChatPayCertificateAgentProvider, WeChatPayConfigProvider } from '../constants/wechat.constant';
import {
WeChatBaseCloseOrderReqParam,
WeChatBaseCloseOrderRes,
Expand All @@ -23,7 +23,7 @@ import { WeChatRequestUtil } from '../utils/request.util';
@Injectable()
export class WeChatPayBaseService {
/** API 接口域名 */
protected apiBase = 'https://api.mch.weixin.qq.com' + (this.payAddonConfig.wechatConfig.sandbox ? '/sandboxnew' : '');
protected apiBase = 'https://api.mch.weixin.qq.com' + (this.config.sandbox ? '/sandboxnew' : '');
/** 统一下单接口地址 */
protected readonly unifiedOrderUrl = `${this.apiBase}/pay/unifiedorder`;
/** 查询订单接口地址 */
Expand All @@ -40,8 +40,8 @@ export class WeChatPayBaseService {
protected readonly downloadFundFlowUrl = `${this.apiBase}/pay/downloadfundflow`;

constructor(
@Inject(PayAddonConfigProvider) protected readonly payAddonConfig: PayAddonConfig,
@Inject(WeChatCertificateAgentProvider) protected readonly certificateAgent: https.Agent,
@Inject(WeChatPayConfigProvider) private readonly config: WeChatPayConfig,
@Inject(WeChatPayCertificateAgentProvider) protected readonly certificateAgent: https.Agent,
@Inject(WeChatRequestUtil) protected readonly requestUtil: WeChatRequestUtil
) { }

Expand Down Expand Up @@ -93,7 +93,7 @@ export class WeChatPayBaseService {
* 检查是否覆盖默认的签名类型
*/
protected checkOverrideDefaultSignType(params: any) {
const signType = this.payAddonConfig.wechatConfig.sign_type;
const signType = this.config.sign_type;
if (signType) {
(params as any).sign_type = signType;
}
Expand Down
11 changes: 6 additions & 5 deletions src/modules/wechat/utils/notify-parser.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { Inject, Injectable } from '@nestjs/common';
import * as crypto from 'crypto';
import { IncomingMessage } from 'http';

import { PayAddonConfig, PayAddonConfigProvider } from '../../../common';
import { WeChatPayConfig } from '../../../common';
import { XmlUtil } from '../../../shared/utils/xml.util';
import { WeChatPayConfigProvider } from '../constants/wechat.constant';
import { WeChatPayNotifyRes, WeChatRefundNotifyRes } from '../interfaces/notify.interface';
import { WeChatSignUtil } from './sign.util';

Expand All @@ -13,7 +14,7 @@ import { WeChatSignUtil } from './sign.util';
@Injectable()
export class WeChatNotifyParserUtil {
constructor(
@Inject(PayAddonConfigProvider) protected readonly payAddonConfig: PayAddonConfig,
@Inject(WeChatPayConfigProvider) protected readonly config: WeChatPayConfig,
@Inject(XmlUtil) private readonly xmlUtil: XmlUtil,
@Inject(WeChatSignUtil) private readonly signUtil: WeChatSignUtil
) { }
Expand All @@ -29,8 +30,8 @@ export class WeChatNotifyParserUtil {
return undefined;
}

const secretKey = this.payAddonConfig.wechatConfig.secretKey;
const signType = this.payAddonConfig.wechatConfig.sign_type;
const secretKey = this.config.secretKey;
const signType = this.config.sign_type;
const result = await this.xmlUtil.parseObjFromXml<WeChatPayNotifyRes>(data);

if (result.return_code !== 'SUCCESS') {
Expand All @@ -55,7 +56,7 @@ export class WeChatNotifyParserUtil {
}
const result = await this.xmlUtil.parseObjFromXml<WeChatRefundNotifyRes>(data);

const secretKey = this.payAddonConfig.wechatConfig.secretKey;
const secretKey = this.config.secretKey;
const cryptedBase64Str = Buffer.from(result.req_info).toString('base64');
const secretKeyMD5 = crypto.createHash('md5').update(secretKey).digest('hex').toLocaleLowerCase();

Expand Down
7 changes: 4 additions & 3 deletions src/modules/wechat/utils/request.util.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { HttpService, Inject, Injectable } from '@nestjs/common';
import * as axios from 'axios';

import { PayAddonConfig, PayAddonConfigProvider } from '../../../common';
import { WeChatPayConfig } from '../../../common';
import { RandomUtil } from '../../../shared/utils/random.util';
import { XmlUtil } from '../../../shared/utils/xml.util';
import { WeChatPayConfigProvider } from '../constants/wechat.constant';
import { WeChatSignUtil } from './sign.util';

/**
Expand All @@ -13,7 +14,7 @@ import { WeChatSignUtil } from './sign.util';
export class WeChatRequestUtil {
constructor(
@Inject(HttpService) private readonly httpService: HttpService,
@Inject(PayAddonConfigProvider) private readonly payAddonConfig: PayAddonConfig,
@Inject(WeChatPayConfigProvider) private readonly config: WeChatPayConfig,
@Inject(XmlUtil) private readonly xmlUtil: XmlUtil,
@Inject(WeChatSignUtil) private readonly signUtil: WeChatSignUtil,
@Inject(RandomUtil) private readonly randomUtil: RandomUtil
Expand All @@ -27,7 +28,7 @@ export class WeChatRequestUtil {
* @param config AxiosRequestConfig
*/
async post<T>(url: string, params: any, config?: axios.AxiosRequestConfig): Promise<T> {
const wechatConfig = this.payAddonConfig.wechatConfig;
const wechatConfig = this.config;
params.appid = wechatConfig.appid;
params.mch_id = wechatConfig.mch_id;
params.nonce_str = this.randomUtil.genRandomStr();
Expand Down
84 changes: 54 additions & 30 deletions src/modules/wechat/wechat.pay.module.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Inject, Module, OnModuleInit } from '@nestjs/common';
import { DynamicModule, Inject, Module, OnModuleInit } from '@nestjs/common';
import * as fs from 'fs';
import * as https from 'https';
import * as path from 'path';

import { PayAddonConfig, PayAddonConfigProvider } from '../../common';
import { WeChatPayConfig } from '../../common';
import { SharedModule } from '../../shared/shared.module';
import { WeChatPayCertificateAgentProvider, WeChatPayConfigProvider } from './constants/wechat.constant';
import { WeChatSandboxResponse } from './interfaces/sandbox.interface';
import { WeChatAppPayService } from './services/app.pay.service';
import { WeChatAppletPayService } from './services/applet.pay.service';
Expand All @@ -15,47 +18,55 @@ import { WeChatNotifyParserUtil } from './utils/notify-parser.util';
import { WeChatRequestUtil } from './utils/request.util';
import { WeChatSignUtil } from './utils/sign.util';

@Module({
imports: [],
providers: [
WeChatPayBaseService,
WeChatAppPayService,
WeChatAppletPayService,
WeChatJSAPIPayService,
WeChatMicroPayService,
WeChatNativePayService,
WeChatWapPayService,
WeChatSignUtil,
WeChatRequestUtil,
WeChatNotifyParserUtil
],
exports: [
WeChatAppPayService,
WeChatAppletPayService,
WeChatJSAPIPayService,
WeChatMicroPayService,
WeChatNativePayService,
WeChatWapPayService,
WeChatNotifyParserUtil
]
})
@Module({})
export class WeChatPayModule implements OnModuleInit {
/** 沙箱环境获取验签秘钥接口地址 */
private readonly sandboxGetSignKeyUrl = 'https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey';
private readonly sandboxSignKeyFileName = '.sandbox_signkey.txt';

constructor(
@Inject(WeChatRequestUtil) private readonly wechatRequestUtil: WeChatRequestUtil,
@Inject(PayAddonConfigProvider) private readonly payAddonConfig: PayAddonConfig
@Inject(WeChatPayConfigProvider) private readonly config: WeChatPayConfig
) { }

static forRoot(config: WeChatPayConfig): DynamicModule {
const certificateAgent = this.createCertificateAgent(config.pfx, config.mch_id);
return {
module: WeChatPayModule,
imports: [SharedModule],
providers: [
WeChatPayBaseService,
WeChatAppPayService,
WeChatAppletPayService,
WeChatJSAPIPayService,
WeChatMicroPayService,
WeChatNativePayService,
WeChatWapPayService,
WeChatSignUtil,
WeChatRequestUtil,
WeChatNotifyParserUtil,
{ provide: WeChatPayCertificateAgentProvider, useValue: certificateAgent },
{ provide: WeChatPayConfigProvider, useValue: config },
],
exports: [
WeChatAppPayService,
WeChatAppletPayService,
WeChatJSAPIPayService,
WeChatMicroPayService,
WeChatNativePayService,
WeChatWapPayService,
WeChatNotifyParserUtil
]
};
}

async onModuleInit() {
if (!this.payAddonConfig.wechatConfig.sandbox) return;
if (!this.config.sandbox) return;

const sandboxSignKeyExipre = this.checkSandboxSignKeyExpire();
if (!sandboxSignKeyExipre) {
const fileContent = fs.readFileSync(path.join(__dirname, this.sandboxSignKeyFileName)).toString();
this.payAddonConfig.wechatConfig.secretKey = JSON.parse(fileContent).key;
this.config.secretKey = JSON.parse(fileContent).key;
} else {
const data = await this.getSandboxSignKey();
if (data.return_code === 'FAIL') {
Expand All @@ -64,7 +75,7 @@ export class WeChatPayModule implements OnModuleInit {
}
const fileContent = JSON.stringify({ key: data.sandbox_signkey, createdAt: +new Date() });
fs.writeFileSync(path.join(__dirname, this.sandboxSignKeyFileName), fileContent);
this.payAddonConfig.wechatConfig.secretKey = data.sandbox_signkey;
this.config.secretKey = data.sandbox_signkey;
}
}

Expand All @@ -84,4 +95,17 @@ export class WeChatPayModule implements OnModuleInit {
const fileContent = fs.readFileSync(path.join(__dirname, this.sandboxSignKeyFileName)).toString();
return (+new Date()) - JSON.parse(fileContent).createdAt > (3600 * 24 * 1000);
}

/**
* 创建请求证书代理
*
* 此 agent 仅用于微信支付的申请退款、撤销订单和下载资金账单接口
*/
private static createCertificateAgent(pfx: Buffer, mchId: string): https.Agent {
if (!pfx) throw Error('读取商户证书失败');
return new https.Agent({
pfx,
passphrase: mchId
});
}
}
19 changes: 8 additions & 11 deletions src/pay.addon.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
import { DynamicModule, Inject, Module, OnModuleInit } from '@nestjs/common';
import { DynamicModule, Module } from '@nestjs/common';

import { PayAddonConfig, PayAddonConfigProvider } from './common';
import { PayAddonConfig } from './common';
import { AliPayModule } from './modules/ali/ali.pay.module';
import { WeChatPayModule } from './modules/wechat/wechat.pay.module';
import { SharedModule } from './shared/shared.module';

@Module({})
export class PayAddon implements OnModuleInit {
constructor(
@Inject(PayAddonConfigProvider) private readonly payAddonConfig: PayAddonConfig
) { }

export class PayAddon {
static forRoot(config: PayAddonConfig): DynamicModule {
this.checkConfig(config);
return {
module: PayAddon,
imports: [SharedModule.forFeature(config), WeChatPayModule, AliPayModule],
imports: [SharedModule, WeChatPayModule.forRoot(config.wechatConfig), AliPayModule],
exports: [WeChatPayModule, AliPayModule]
};
}

async onModuleInit() {
const wechatConfig = this.payAddonConfig.wechatConfig;
const aliConfig = this.payAddonConfig.aliConfig;
private static checkConfig(config: PayAddonConfig) {
const wechatConfig = config.wechatConfig;
const aliConfig = config.aliConfig;
if (!wechatConfig && !aliConfig) {
throw Error('请至少指定一种支付方式的配置');
}
Expand Down
42 changes: 7 additions & 35 deletions src/shared/shared.module.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,11 @@
import { DynamicModule, Global, HttpModule, Module } from '@nestjs/common';
import * as https from 'https';
import { HttpModule, Module } from '@nestjs/common';

import { PayAddonConfig, PayAddonConfigProvider } from '../common';
import { WeChatCertificateAgentProvider } from '../modules/wechat/constants/wechat.constant';
import { RandomUtil } from './utils/random.util';
import { XmlUtil } from './utils/xml.util';

@Global()
@Module({})
export class SharedModule {
static forFeature(config: PayAddonConfig): DynamicModule {
const pfx = this.createCertificateAgent(config.wechatConfig.pfx, config.wechatConfig.mch_id);
return {
module: SharedModule,
imports: [HttpModule],
providers: [
XmlUtil,
RandomUtil,
{ provide: PayAddonConfigProvider, useValue: config },
{ provide: WeChatCertificateAgentProvider, useValue: pfx }
],
exports: [HttpModule, RandomUtil, XmlUtil, PayAddonConfigProvider, WeChatCertificateAgentProvider]
};
}

/**
* 创建请求证书代理
*
* 此 agent 仅用于微信支付的申请退款、撤销订单和下载资金账单接口
*/
private static createCertificateAgent(pfx: Buffer, mchId: string): https.Agent {
if (!pfx) throw Error('读取商户证书失败');
return new https.Agent({
pfx,
passphrase: mchId
});
}
}
@Module({
imports: [HttpModule],
providers: [XmlUtil, RandomUtil],
exports: [HttpModule, RandomUtil, XmlUtil]
})
export class SharedModule { }

0 comments on commit 1ffa36f

Please sign in to comment.