Netherworld's Pretend Minibar

    @jianghohwason/nt-addon-pay

    1.0.1 • Public • Published

    Notadd 支付插件

    整合微信支付 API、支付宝支付 API,基于 Nest.js 框架构建的一个支付插件。

    使用说明

    使用前,请确保了解微信支付、支付宝支付的各类支付流程,且具备基本的 debug 能力。

    安装

    npm install @huazai5m/nt-addon-pay

    配置 PayAddon

    import { Module } from '@nestjs/common';
    import { PayAddon } from '@huazai5m/nt-addon-pay';
    
    @Module({
        imports: [
            PayAddon.forRoot({
                wechatConfig: {
                    appid: 'appid', // 公众号appi/应用appid/小程序appid
                    mch_id: 'mch_id', // 商户号
                    secretKey: 'secretKey', // 商户交易秘钥
                    sign_type: 'MD5', // 微信支付签名类型('MD5' | 'HMAC-SHA256'),默认MD5,配置后,所有接口参数均会使用这个签名类型
                    pfx: fs.readFileSync('path_to_p12_file'), // p12文件
                    sandbox: true, // 是否启用沙箱环境,默认不启用,用于商户支付验收测试
                },
                aliConfig: {
                    appId: '2021001145608062',
                    // alipayPublicKey: 'a',
                    privateKey: readFileSync('./hs_alipk.txt', 'ascii'),
                },
            }),
        ],
    })
    export class ApplicationModule {}

    微信支付

    接口使用前的必要声明:

    1. 所有的接口请求参数和接口返回结果中的属性名全部使用的是 snake_case,其中属性名结尾带 ? 的代表这个属性是可选的(非必填)。
    2. 所有与金额相关的数据全部是 number 类型且单位为 ,需自行转换。
    3. 所有接口参数中的 mch_idappid/wxappidnonce_strsign_typesign 数据均由插件自动填入,无需手动传入。
    4. 所有请求的返回结果若有 sign,插件会自动验签。
    5. 支付通知结果插件会自动验签,退款通知结果插件会自动解密 req_info 数据。
    6. 微信 接口 调用的返回结果、回调通知 的返回结果中的 金额数量 的类型会因 xml2js 工具解析后全部转换为字符串,需开发者手动转换类型。

    使用 WeChatXXXPayService 调用 API

    WeChatXXXPayService 类包含当前支付方式的支付、订单、退款相关 API,各支付类说明:

    • WeChatAppPayService —— APP 支付
    • WeChatAppletPayService —— 小程序支付
    • WeChatJSAPIPayService —— JSAPI 支付(用户通过微信扫码、关注公众号等方式进入商家 H5 页面,并在微信内调用 JSSDK 完成支付)
    • WeChatMicroPayService —— 付款码支付(用户打开微信钱包-付款码的界面,商户扫码后提交完成支付)
    • WeChatNativePayService —— Native 支付(扫码支付)
    • WeChatWapPayService —— H5 支付(用户在微信以外的手机浏览器请求微信支付的场景唤起微信支付)
    • WeChatRedpackService —— 现金红包
    • WeChatTransferService —— 企业付款

    例子:Native 支付(扫码支付)调用方式如下

    import { Injectable, Inject } from '@nestjs/common';
    import { WeChatNativePayService, WeChatTradeType } from '@huazai5m/nt-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', // 支付请求方IP
                notify_url: 'your.domain.com/payment/wechat_order_notify', // 服务端支付通知地址
                trade_type: WeChatTradeType.JSAPI,
            });
        }
    }

    使用 WeChatNotifyParserUtil 解析支付/退款通知

    支付/退款完成后,微信会把相关支付/退款结果和用户信息发送给商户,商户需要接收处理,并返回应答。

    对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。(通知频率为 15/15/30/180/1800/1800/1800/1800/3600,单位:秒)

    注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。

    推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处 理前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。

    import { Controller, Inject, Post, Req, Res } from '@nestjs/common';
    
    import { WeChatNotifyParserUtil } from '@huazai5m/nt-addon-pay';
    
    @Controller('payment')
    export class PaymentNotifyController {
        constructor(@Inject(WeChatNotifyParserUtil) private readonly weChatNotifyParserUtil: WeChatNotifyParserUtil) {}
    
        /**
         * 微信支付统一下单通知路由
         *
         * 特别提醒:商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。
         *
         * 技术人员可登进微信商户后台扫描加入接口报警群。
         *
         * @param req 通知请求
         */
        @Post('wechat_order_notify')
        async weChatOrderNotify(@Req() req, @Res() res) {
            // 必须使用 @Res() 注解向微信返回结果
            // 当 data 为 undefined 时,表示通知请求中的 sign 验签失败
            const data = await this.weChatNotifyParserUtil.parsePayNotify(req);
    
            // 验签失败时
            if (!data) {
                // 微信会重新发起通知
                res.end(this.weChatNotifyParserUtil.generateFailMessage('验签失败')); // 必须使用 res.end() 向微信返回当前业务结果;
            }
    
            // 判断返回状态,失败时
            if (data.return_code === 'FAIL') {
                // 处理业务数据
                // ......
                // 根据情况返回成功或失败消息
            }
    
            // 判断业务状态,失败时
            if (data.result_code === 'FAIL') {
                // 处理业务数据
                // ......
                // 根据情况返回成功或失败消息
            }
    
            // 成功时返回
            res.end(this.weChatNotifyParserUtil.generateSuccessMessage()); // 必须使用 res.end() 向微信返回当前业务结果;
        }
    
        /**
         * 微信支付退款通知路由
         *
         * 特别说明:退款结果对重要的数据进行了加密,商户需要用商户秘钥进行解密后才能获得结果通知的内容。
         *
         * @param req 通知请求
         */
        @Post('wechat_refund_notify')
        async weChatRefundNotify(@Req() req) {
            // 当 data 为 undefined 时,表示通知请求中的 req_info 解密失败
            const data = await this.weChatNotifyParserUtil.parseRefundNotify(req);
    
            // 解密失败时
            if (!data) {
                // 微信会重新发起通知
                return this.weChatNotifyParserUtil.generateFailMessage('解密失败');
            }
    
            // 判断返回状态,失败时
            if (data.return_code === 'FAIL') {
                // 处理业务数据
                // ......
                // 根据情况返回成功或失败消息
            }
    
            // 成功时返回
            return this.weChatNotifyParserUtil.generateSuccessMessage();
        }
    }

    使用 WeChatSignUtil 计算签名

    在个别支付方式中,需要服务端计算签名并返回给客户端时,使用插件提供的 WeChatSignUtil 工具类即可计算签名:

    import { Injectable, Inject } from '@nestjs/common';
    import { WeChatSignUtil } from '@huazai5m/nt-addon-pay';
    
    @Injectable()
    export class TestSign {
        constructor(@Inject(WeChatSignUtil) private readonly weChatSignUtil: WeChatSignUtil) {}
    
        async testWechatSign() {
            const params = { appid: 'xxxxx', openid: 'xxxxx' };
            const sign = await this.weChatSignUtil.sign(params);
        }
    }

    如何使用支付验收 case ?

    两种方式,如下:

    1. 关注微信公众号:微信支付商户接入验收助手,并查阅相应支付验收 case 示例。
    2. 下载支付验收 case 示例 pdf 文档。地址:免充值产品测试验收用例

    支付宝支付

    支付宝 service 使用

    import { AliPayService } from '@huazai5m/nt-addon-pay';
    
    const result = await this.aliPayService.exec('alipay.system.oauth.token', {
        grantType: 'authorization_code',
        code: 'code',
        refreshToken: 'token',
    });

    随机字符生成工具

    支付插件内使用了 nanoid 作为随机字符生成工具,开发者可以通过注入 RandomUtil,即可使用该工具。

    import { Injectable, Inject } from '@nestjs/common';
    import { RandomUtil } from '@huazai5m/nt-addon-pay';
    
    @Injectable()
    export class TestSign {
        constructor(@Inject(RandomUtil) private readonly randomUtil: RandomUtil) {}
    
        async testRandomStr() {
            /**
             * genRandomStr(pool?: string, length?: number) 接受两个参数:
             *
             * pool 随机字符池,默认:ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
             *
             * length 随机字符长度,默认:32
             */
            const randomStr = await this.randomUtil.genRandomStr();
        }
    }

    贡献说明

    我们欢迎 Nest.js 使用者来参与这个插件的开发,作为一个贡献者,请您遵循以下原则:

    • 代码提交规范,参考 Git Commit Message Conventions
    • 始终从 develop checkout 一个新分支,命名规范为 feature/xxx,xxx 必须具有可读性,如:微信-普通商户版-扫码支付 => feature/wechat-native-pay
    • 在 checkout 新分支前,先在本地 develop 分支拉取远程 develop 分支的最新代码
    • 文件命名规则请参考项目目前的命名规则,如:微信支付中,order.interface.ts 代表所有订单相关的请求参数和返回结果的定义,micro.pay.service.ts 代表付款码支付的业务逻辑

    功能开发

    请先查阅 Roadmap,确保你想贡献的功能没有正在被实现。然后在 issue 里提交一个贡献请求,注明想要贡献的功能。

    发现 Bug ?

    如果你在源码中发现 bug,请你先在本仓库的 issue 提交一个 bug 问题。在你提交完 bug 问题后,我们很乐意接受你提交一个 PR 来帮助我们修复这个 bug。

    QQ 交流群

    322247106,请注明加群目的!

    Roadmap

    • [x] 0.0.1 贡献说明
    • [x] 0.1.0 微信-普通商户版-APP 支付
    • [x] 0.2.0 微信-普通商户版-JSAPI 支付、微信-普通商户版-Native 支付、微信-普通商户版-H5 支付、微信-普通商户版-小程序支付
    • [x] 0.3.0 微信-普通商户版-付款码支付
    • [x] 0.4.0 微信-普通商户版-现金红包
    • [x] 0.5.0 微信-普通商户版-企业付款
    • [x] 0.6.0 支付宝-APP 支付
    • [x] 0.7.0 支付宝-当面付
    • [x] 0.8.0 支付宝-手机网站支付
    • [x] 0.9.0 支付宝-电脑网站支付
    • [x] 1.0.0 完善使用说明、发布正式版 v1.0.0

    已经集成支付宝 sdk

    Install

    npm i @jianghohwason/nt-addon-pay

    DownloadsWeekly Downloads

    4

    Version

    1.0.1

    License

    Apache-2.0

    Unpacked Size

    313 kB

    Total Files

    62

    Last publish

    Collaborators

    • jianghohwason