支付

注意

以下方式是旧版本的支付方式,不建议使用,新版本已经插件化了,可以直接到插件市场下载对应的插件,插件的使用方式请参考插件市场对应插件的文档

前往微信支付插件

前往支付宝支付插件

cool-admin 自带封装了微信和支付宝支付

微信支付

适用微信支付接口 v3

1、安装插件

npm install @cool-midway/pay

2、引入插件

src/configuration.ts

import { App, Configuration } from "@midwayjs/decorator";
import { ILifeCycle, IMidwayContainer } from "@midwayjs/core";
import { Application } from "egg";
import * as orm from "@midwayjs/orm";
import * as cool from "@cool-midway/core";
import * as pay from "@cool-midway/pay";

@Configuration({
	// 注意组件顺序 cool 有依赖orm组件, 所以必须放在,orm组件之后 cool的其他组件必须放在cool 核心组件之后
	imports: [pay]
})
export class ContainerLifeCycle {
	@App()
	app: Application;
	// 应用启动完成
	async onReady(container?: IMidwayContainer) {}
	// 应用停止
	async onStop() {}
}

3、配置

也可以在src/config/xxx.ts配置(两种配置都存在,此种方式优先)

config.cool = {
	pay: {
		wx: {
			appid: "公众号ID",
			mchid: "微信商户号",
			publicKey: require("fs").readFileSync("公钥证书文件路径"),
			privateKey: require("fs").readFileSync("私钥证书文件路径"),
			notify_url: "支付回调地址",
			key: "可选参数 APIv3密钥"
		}
	}
};

4、API

其他支付方式可以参考wechatpay-node-v3open in new window

支付宝支付

1、安装插件

@cool-midway/pay

2、引入插件

src/configuration.ts

import { App, Configuration } from "@midwayjs/decorator";
import { ILifeCycle, IMidwayContainer } from "@midwayjs/core";
import { Application } from "egg";
import * as orm from "@midwayjs/orm";
import * as cool from "@cool-midway/core";
import * as pay from "@cool-midway/pay";

@Configuration({
	// 注意组件顺序 cool 有依赖orm组件, 所以必须放在,orm组件之后 cool的其他组件必须放在cool 核心组件之后
	imports: [pay]
})
export class ContainerLifeCycle {
	@App()
	app: Application;
	// 应用启动完成
	async onReady(container?: IMidwayContainer) {}
	// 应用停止
	async onStop() {}
}

3、配置

也可以在src/config/xxx.ts配置(两种配置都存在,此种方式优先)

config.cool = {
	pay: {
		ali: {
			notifyUrl: "https://xxx/app/order/pay/aliNotify",
			appId: "xxxxxx",
			privateKey: fs.readFileSync(
				path.resolve("./src/modules/order/pem/ali/privateKey.pem"),
				"ascii"
			),
			keyType: "PKCS1",
			appCertPath: path.resolve("./src/modules/order/pem/ali/appCertPublicKey.crt"),
			alipayRootCertPath: path.resolve("./src/modules/order/pem/ali/alipayRootCert.crt"),
			alipayPublicCertPath: path.resolve(
				"./src/modules/order/pem/ali/alipayCertPublicKey_RSA2.crt"
			)
		}
	}
};

WARNING

注意私钥需要用支付宝密钥工具转成 PKCS1 格式

4、API

其他支付方式可以参考alipay-sdkopen in new window

DEMO 示例

Controller,需要注意开放回调接口,不要对其进行 token 校验

import { CoolController, BaseController, CoolUrlTag, TagTypes } from "@cool-midway/core";
import { Body, Inject, Post } from "@midwayjs/core";
import { OrderPayService } from "../../service/pay";

/**
 * 支付
 */
@CoolUrlTag({
	key: TagTypes.IGNORE_TOKEN,
	value: ["wxNotify", "aliNotify"]
})
@CoolController()
export class AppOrderPayController extends BaseController {
	@Inject()
	orderPayService: OrderPayService;

	@Post("/aliNotify", { summary: "支付宝支付回调通知" })
	async aliNotify(@Body() body) {
		this.orderPayService.aliNotify(body);
		return "success";
	}

	@Post("/aliQrcode", { summary: "支付宝扫码支付" })
	async aliQrcode(@Body("orderId") orderId: number, @Body("width") width: number) {
		return this.ok(await this.orderPayService.aliQrcode(orderId, width));
	}

	@Post("/aliApp", { summary: "支付宝APP支付" })
	async aliApp(@Body("orderId") orderId: number) {
		return this.ok(await this.orderPayService.aliApp(orderId));
	}

	@Post("/wxApp", { summary: "微信APP支付" })
	async wxApp(@Body("orderId") orderId: number) {
		return this.ok(await this.orderPayService.wxApp(orderId));
	}

	@Post("/wxNotify", { summary: "微信支付回调" })
	async wxNotify(@Body() body) {
		await this.orderPayService.wxNotify(body);
		return "success";
	}

	@Post("/wxQrcode", { summary: "微信扫码支付" })
	async wxQrcode(@Body() body) {
		await this.orderPayService.wxQrcode(body);
		return "success";
	}
}

Service

import { Config, Inject, Provide } from "@midwayjs/decorator";
import { BaseService, CoolCommException } from "@cool-midway/core";
import { InjectEntityModel } from "@midwayjs/typeorm";
import { Repository } from "typeorm";
import { OrderInfoEntity } from "../entity/info";
// @ts-ignore
import { sign } from "alipay-sdk/lib/util";
import { CoolWxPay, CoolAliPay } from "@cool-midway/pay";
// @ts-ignore
import AlipayFormData from "alipay-sdk/lib/form";
// @ts-ignore
import { Decimal } from "decimal.js";
import { UserVipService } from "../../user/service/vip";
import { SeesionSocketService } from "../../session/service/socket";

/**
 * 支付
 */
@Provide()
export class OrderPayService extends BaseService {
	@InjectEntityModel(OrderInfoEntity)
	orderInfoEntity: Repository<OrderInfoEntity>;

	@Config("appName")
	appName: string;

	// 微信支付
	@Inject()
	wxPay: CoolWxPay;

	// 支付宝支付
	@Inject()
	aliPay: CoolAliPay;

	@Inject()
	userVipService: UserVipService;

	@Inject()
	seesionSocketService: SeesionSocketService;

	@Inject()
	ctx;

	/**
	 * 微信APP支付
	 * @param orderId
	 * @returns
	 */
	async wxApp(orderId: number) {
		const info = await this.orderInfo(orderId);
		const params = {
			description: `${this.appName}-订单`,
			out_trade_no: info?.orderNum,
			notify_url: this.wxPay.coolWxPay.notify_url,
			amount: {
				total: new Decimal(info.price).times(100).toNumber()
			}
		};
		const result = await this.wxPay.getInstance().transactions_app(params);
		return result;
	}

	/**
	 * 微信二维码支付
	 * @param orderId
	 */
	async wxQrcode(orderId: number) {
		const info = await this.orderInfo(orderId);
		const params = {
			description: `${this.appName}-订单`,
			out_trade_no: info?.orderNum,
			notify_url: this.wxPay.coolWxPay.notify_url,
			amount: {
				total: new Decimal(info.price).times(100).toNumber()
			}
		};
		const result = await this.wxPay.getInstance().transactions_native(params);
		return result;
	}

	/**
	 * 微信退款
	 * @param order
	 */
	async wxRefund(order: OrderInfoEntity) {
		const params = {
			description: `${this.appName}-订单`,
			out_trade_no: order.orderNum,
			notify_url: this.wxPay.coolWxPay.notify_url,
			amount: {
				refund: new Decimal(order.refundAmount).times(100).toNumber(),
				total: new Decimal(order.price).times(100).toNumber()
			}
		};
		const result = await this.wxPay.getInstance().transactions_app(params);
		return result.status == "SUCCESS";
	}

	/**
	 * 订单信息
	 * @param orderId
	 */
	async orderInfo(orderId: number) {
		const info = await this.orderInfoEntity.findOneBy({ id: orderId });
		if (!info && info?.payStatus != 0) {
			throw new CoolCommException("订单不存在或不是可支付的状态");
		}
		return info;
	}

	/**
	 * 支付宝App支付
	 * @param orderId
	 */
	async aliApp(orderId: number) {
		const info = await this.orderInfo(orderId);
		// 返回支付链接
		const data = sign(
			"alipay.trade.app.pay",
			{
				notifyUrl: this.aliPay.coolAlipay.notifyUrl,
				bizContent: {
					subject: `${this.appName}-订单`,
					totalAmount: info.price,
					outTradeNo: info.orderNum,
					productCode: "QUICK_MSECURITY_PAY",
					body: {}
				}
			},
			this.aliPay.getInstance().config
		);
		const payInfo = new URLSearchParams(data).toString();
		return payInfo;
	}

	/**
	 * 支付宝扫码支付
	 * @param orderId
	 */
	async aliQrcode(orderId: number, width = 400): Promise<any> {
		const info = await this.orderInfo(orderId);
		const formData = new AlipayFormData();
		// 调用 setMethod 并传入 get,会返回可以跳转到支付页面的 url
		formData.setMethod("get");
		formData.addField("notifyUrl", this.aliPay.coolAlipay.notifyUrl);
		formData.addField("bizContent", {
			outTradeNo: info.orderNum,
			productCode: "FAST_INSTANT_TRADE_PAY",
			totalAmount: info.price,
			subject: `${this.appName}-订单`,
			qrPayMode: 4,
			qrcodeWidth: width,
			body: JSON.stringify({
				orderId
			})
		});
		// 返回支付链接
		const result = await this.aliPay
			.getInstance()
			.exec("alipay.trade.page.pay", {}, { formData });
		return result;
	}

	/**
	 * 支付宝支付回调通知
	 * @param data
	 */
	async aliNotify(data: any) {
		// 检查签名
		const check = await this.aliPay.signVerify(data);
		if (check && data.trade_status == "TRADE_SUCCESS") {
			await this.paySuccess(data.out_trade_no, 1);
		}
	}

	/**
	 * 微信支付回调通知
	 */
	async wxNotify(body: any) {
		const { ciphertext, associated_data, nonce } = body.resource;
		const data: any = this.wxPay.getInstance().decipher_gcm(ciphertext, associated_data, nonce);
		const check = await this.wxPay.signVerify(this.ctx);
		// 验签通过,处理业务逻辑
		if (check && data.trade_state == "SUCCESS") {
			await this.paySuccess(data.out_trade_no, 0);
		}
	}

	/**
	 * 支付成功
	 * @param orderNum
	 * @param payWay
	 */
	async paySuccess(orderNum: string, payWay: number) {
		await this.orderInfoEntity.update(
			{ orderNum },
			{ payStatus: 1, payWay, payTime: new Date() }
		);
		await this.userVipService.paySuccess(orderNum);

		const order = await this.orderInfoEntity.findOneBy({ orderNum });

		this.seesionSocketService.sendByUserId(order.userId, "paySuccess", order);
	}

	/**
	 * 订单号
	 * @param order
	 */
	async aliRefund(order: OrderInfoEntity) {
		const result = await this.aliPay.getInstance().exec("alipay.trade.refund", {
			bizContent: {
				out_trade_no: order.orderNum,
				refund_amount: order.price,
				refund_reason: order.refundReason
			}
		});
		return result.code == "10000";
	}
}
Last Updated: