Skip to content

Commit

Permalink
Merge pull request #10 from notadd/develop
Browse files Browse the repository at this point in the history
chore(release): publish v0.3.0
  • Loading branch information
dzzzzzy authored Nov 27, 2018
2 parents aded199 + 8e8ff24 commit edb2f07
Show file tree
Hide file tree
Showing 16 changed files with 178 additions and 39 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,5 @@ fabric.properties

# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
.sandbox_signkey.txt
certificate/
certificate/
dist/
49 changes: 48 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,54 @@

## 使用说明

TODO
### 安装

`npm install @notadd/addon-pay`

### 配置 PayAddon

```typescript
import { Module } from '@nestjs/common';
import { PayAddon } from '@notadd/addon-pay';

@Module({
imports: [
PayAddon.forRoot({
wechatConfig: {
appid: 'appid', // 公众号appi/应用appid/小程序appid
mch_id: 'mch_id', // 商户号
secretKey: 'secretKey', // 商户交易秘钥
pfx: fs.readFileSync('path_to_p12_file'), // p12文件
sandbox: true // 是否启用沙箱环境,默认不启用
}
})
]
})
export class ApplicationModule {}
```

### 使用 Wechat`XXX`PayService

```typescript
import { Injectable, Inject } from '@nestjs/common';
import { WechatNativePayService, WechatTradeType } from '@notadd/addon-pay';

@Injectable()
export class TestPay {
constructor(@Inject(WechatNativePayService) private readonly wechatNativePayService: WechatNativePayService) { }

async nativePay() {
const ressult = await this.wechatNativePayService.pay({
body: '支付一下',
out_trade_no: '201811271512000001',
total_fee: 301,
spbill_create_ip: '127.0.0.1',
notify_url: 'your.domain.com/wechat-pay/notify',
trade_type: WechatTradeType.JSAPI
});
}
}
```

## 贡献说明

Expand Down
23 changes: 13 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "nt-addon-pay",
"version": "0.0.1",
"description": "The pay addon for notadd application.",
"version": "0.3.0",
"description": "The pay addon for notadd application",
"scripts": {
"start": "ts-node -r tsconfig-paths/register starter/main.ts",
"start:watch": "nodemon",
Expand All @@ -15,16 +15,16 @@
"author": "notadd",
"license": "Apache-2.0",
"dependencies": {
"@nestjs/common": "^5.4.0",
"@nestjs/core": "^5.4.0",
"chance": "^1.0.16",
"reflect-metadata": "^0.1.12",
"rxjs": "^6.3.3",
"typescript": "^3.1.3",
"xml2js": "^0.4.19"
},
"devDependencies": {
"@nestjs/common": "^5.4.0",
"@nestjs/core": "^5.4.0",
"@nestjs/testing": "^5.4.0",
"reflect-metadata": "^0.1.12",
"rxjs": "^6.3.3",
"typescript": "^3.1.3",
"@types/chance": "^1.0.1",
"@types/jest": "^23.3.9",
"@types/node": "^10.12.0",
Expand All @@ -39,15 +39,18 @@
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "test",
"testRegex": ".spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"coverageDirectory": "../coverage",
"collectCoverage": true,
"collectCoverageFrom": [
"src/**/*.service.ts",
"!src/modules/wechat/services/base.service.ts"
],
"coverageDirectory": "coverage",
"testEnvironment": "node"
}
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './pay.addon';
export * from './modules/wechat';
11 changes: 11 additions & 0 deletions src/modules/wechat/enums/trade-type.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/** 微信支付交易类型 */
export enum WechatTradeType {
/** JSAPI、小程序支付 */
JSAPI = 'JSAPI',
/** 扫码支付 */
NATIVE = 'NATIVE',
/** APP支付 */
APP = 'APP',
/** H5支付 */
MWEB = 'MWEB',
}
7 changes: 7 additions & 0 deletions src/modules/wechat/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export * from './services/app.pay.service';
export * from './services/applet.pay.service';
export * from './services/jsapi.pay.service';
export * from './services/micro.pay.service';
export * from './services/native.pay.service';
export * from './services/wap.pay.service';
export * from './enums/trade-type.enum';
13 changes: 1 addition & 12 deletions src/modules/wechat/interfaces/order.interface.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { WechatTradeType } from '../enums/trade-type.enum';
import { WechatBaseResponse } from './base.interface';

/** 微信支付下单接口基础请求参数 */
Expand Down Expand Up @@ -28,18 +29,6 @@ interface WechatBaseOrderRequestParam {
scene_info?: string;
}

/** 微信支付交易类型 */
export enum WechatTradeType {
/** JSAPI、小程序支付 */
JSAPI = 'JSAPI',
/** 扫码支付 */
NATIVE = 'NATIVE',
/** APP支付 */
APP = 'APP',
/** H5支付 */
MWEB = 'MWEB',
}

/** 微信付款码支付下单接口请求参数 */
export interface WechatMicroPayOrderReqParam extends WechatBaseOrderRequestParam {
/** 授权码 */
Expand Down
55 changes: 55 additions & 0 deletions src/modules/wechat/interfaces/pay-notify.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/** 微信支付结果通知返回值 */
export interface WechatPayBaseNotifyRes {
/** 返回状态码 */
return_code: string;
/** 返回信息 */
return_msg: string;
/** 业务结果 */
result_code: string;
/** 错误代码 */
err_code?: string;
/** 错误代码描述 */
err_code_des?: string;
/** 公众账号ID/小程序ID/应用ID */
appid: string;
/** 商户号 */
mch_id: string;
/** 微信支付订单号 */
transaction_id: string;
/** 商户订单号 */
out_trade_no: string;
/** 设备号 */
device_info?: string;
/** 随机字符串 */
nonce_str: string;
/** 签名 */
sign: string;
/** 签名类型 */
sign_type?: string;
/** 用户标识 */
openid: string;
/** 是否关注公众账号 */
is_subscribe: string;
/** 交易类型 */
trade_type: string;
/** 付款银行 */
bank_type: string;
/** 订单金额 */
total_fee: number;
/** 应结订单金额 */
settlement_total_fee?: number;
/** 货币种类 */
fee_type?: string;
/** 现金支付金额 */
cash_fee: number;
/** 现金支付货币类型 */
cash_fee_type?: string;
/** 总代金券金额 */
coupon_fee?: number;
/** 代金券使用数量 */
coupon_count?: number;
/** 商家数据包 */
attach?: string;
/** 支付完成时间 */
time_end: string;
}
4 changes: 3 additions & 1 deletion src/modules/wechat/interfaces/sandbox.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ export interface WechatSandboxResponse {
/** 返回状态码 */
return_code: string;
/** 返回信息 */
retmsg: string;
return_msg?: string;
/** 返回信息 */
retmsg?: string;
/** 沙箱秘钥 */
sandbox_signkey: string;
}
25 changes: 24 additions & 1 deletion src/modules/wechat/services/base.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@ import { Inject, Injectable } from '@nestjs/common';
import * as https from 'https';

import { PayAddonConfig, PayAddonConfigProvider } from '../../../common';
import { XmlUtil } from '../../../shared/utils/xml.util';
import { WechatCertificateAgentProvider } from '../constants/wechat.constant';
import {
WechatBaseCloseOrderReqParam,
WechatBaseCloseOrderRes,
WechatBaseQueryOrderReqParam,
WechatBaseQueryOrderRes,
} from '../interfaces/order.interface';
import { WechatPayBaseNotifyRes } from '../interfaces/pay-notify.interface';
import {
WechatBaseQueryRefundReqParam,
WechatBaseQueryRefundRes,
WechatBaseRefundReqParam,
WechatBaseRefundRes,
} from '../interfaces/refund.interface';
import { WechatRequestUtil } from '../utils/request.util';
import { WechatSignUtil } from '../utils/sign.util';

/**
* 微信支付
Expand All @@ -42,7 +45,9 @@ export class WechatPayBaseService {
constructor(
@Inject(PayAddonConfigProvider) protected readonly payAddonConfig: PayAddonConfig,
@Inject(WechatCertificateAgentProvider) protected readonly certificateAgent: https.Agent,
@Inject(WechatRequestUtil) protected readonly requestUtil: WechatRequestUtil
@Inject(WechatRequestUtil) protected readonly requestUtil: WechatRequestUtil,
@Inject(XmlUtil) private readonly xmlUtil: XmlUtil,
@Inject(WechatSignUtil) private readonly signUtil: WechatSignUtil
) { }

/**
Expand Down Expand Up @@ -89,6 +94,24 @@ export class WechatPayBaseService {
return await this.requestUtil.post<WechatBaseQueryRefundRes>(this.queryOrderUrl, params);
}

/**
* 解析支付结果通知的请求体
*
* 支付结果通知验签失败时,返回 undefined
*
* @param body 请求体
*/
public async parseWechatPayNotify(body: any): Promise<WechatPayBaseNotifyRes> {
const secretKey = this.payAddonConfig.wechatConfig.secretKey;
const signType = this.payAddonConfig.wechatConfig.sign_type;
const result = await this.xmlUtil.parseObjFromXml<WechatPayBaseNotifyRes>(body);
if (result.sign && result.sign !== this.signUtil.sign(result, secretKey, signType)) {
// 支付结果通知验签失败时,返回 undefined
return undefined;
}
return result;
}

/**
* 检查是否覆盖默认的签名类型
*/
Expand Down
6 changes: 3 additions & 3 deletions src/modules/wechat/utils/sign.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ export class WechatSignUtil {
*
* @param params 参数
* @param secretKey 秘钥
* @param hashType 签名方式(选填),默认MD5
* @param signType 签名方式(选填),默认MD5
*/
sign(params: {}, secretKey: string, hashType?: 'MD5' | 'HMAC-SHA256') {
sign(params: {}, secretKey: string, signType?: 'MD5' | 'HMAC-SHA256') {
const paramArr: string[] = [];
const sortedKeys = Object.keys(params).sort();
for (const key of sortedKeys) {
params[key] && paramArr.push(`${key}=${params[key]}`);
}
let signStr = paramArr.join('&');
if (hashType && hashType === 'HMAC-SHA256') {
if (signType && signType === 'HMAC-SHA256') {
return crypto.createHmac('sha256', secretKey).update(signStr).digest('hex').toUpperCase();
}
return crypto.createHash('md5').update(signStr += `&key=${secretKey}`).digest('hex').toUpperCase();
Expand Down
6 changes: 4 additions & 2 deletions src/modules/wechat/wechat.pay.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { WechatSignUtil } from './utils/sign.util';
WechatSignUtil,
WechatRequestUtil
],
exports: []
exports: [WechatAppPayService, WechatAppletPayService, WechatJSAPIPayService, WechatMicroPayService, WechatNativePayService, WechatWapPayService]
})
export class WechatPayModule implements OnModuleInit {
/** 沙箱环境获取验签秘钥接口地址 */
Expand All @@ -39,12 +39,14 @@ export class WechatPayModule implements OnModuleInit {
) { }

async onModuleInit() {
if (!this.payAddonConfig.wechatConfig.sandbox) return;
if (fs.existsSync(path.join(__dirname, '.sandbox_signkey.txt'))) {
this.payAddonConfig.wechatConfig.secretKey = fs.readFileSync(path.join(__dirname, '.sandbox_signkey.txt')).toString();
} else {
const data = await this.getSandboxSignKey();
if (data.return_code === 'FAIL') {
throw new Error('微信支付获取沙箱环境秘钥时出现异常:' + data.retmsg);
const errmsg = data.retmsg ? data.retmsg : data.return_msg;
throw new Error('微信支付获取沙箱环境秘钥时出现异常:' + errmsg);
}
fs.writeFileSync(path.join(__dirname, '.sandbox_signkey.txt'), data.sandbox_signkey);
this.payAddonConfig.wechatConfig.secretKey = data.sandbox_signkey;
Expand Down
3 changes: 2 additions & 1 deletion src/pay.addon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export class PayAddon implements OnModuleInit {
static forRoot(config: PayAddonConfig): DynamicModule {
return {
module: PayAddon,
imports: [SharedModule.forFeature(config), WechatPayModule, AliPayModule]
imports: [SharedModule.forFeature(config), WechatPayModule, AliPayModule],
exports: [WechatPayModule, AliPayModule]
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/shared/utils/xml.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class XmlUtil {
* @param obj 对象
*/
convertObjToXml(obj: {}) {
return new xml2js.Builder({ rootName: 'xml' }).buildObject(obj);
return new xml2js.Builder({ rootName: 'xml', cdata: true }).buildObject(obj);
}

/**
Expand Down
4 changes: 1 addition & 3 deletions test/wechat/app.pay.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { Test } from '@nestjs/testing';
import * as fs from 'fs';

import { WechatTradeType } from '../../src/modules/wechat/interfaces/order.interface';
import { WechatAppPayService } from '../../src/modules/wechat/services/app.pay.service';
import { PayAddon } from '../../src/pay.addon';
import { PayAddon, WechatAppPayService, WechatTradeType } from '../../src';

describe('WechatPayAppService', () => {
let wechatAppPayService: WechatAppPayService;
Expand Down
4 changes: 1 addition & 3 deletions test/wechat/applet.pay.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { Test } from '@nestjs/testing';
import * as fs from 'fs';

import { WechatTradeType } from '../../src/modules/wechat/interfaces/order.interface';
import { WechatAppletPayService } from '../../src/modules/wechat/services/applet.pay.service';
import { PayAddon } from '../../src/pay.addon';
import { PayAddon, WechatAppletPayService, WechatTradeType } from '../../src';

describe('WechatPayAppletService', () => {
let wechatAppletPayService: WechatAppletPayService;
Expand Down

0 comments on commit edb2f07

Please sign in to comment.