Add WeChat in-app JSAPI recharge for g2.tkmind.cn.
Use JSAPI with bound openid inside WeChat instead of QR long-press, and include related auth redirect and admin UX fixes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -215,6 +215,33 @@ async function wechatV3Request(config, method, path, bodyObj) {
|
||||
return data;
|
||||
}
|
||||
|
||||
export function buildV2JsapiParams(config, prepayId) {
|
||||
const timeStamp = String(Math.floor(Date.now() / 1000));
|
||||
const nonceStr = randomNonce(16);
|
||||
const pkg = `prepay_id=${prepayId}`;
|
||||
const signType = 'MD5';
|
||||
const paySign = signV2Params(
|
||||
{
|
||||
appId: config.appId,
|
||||
timeStamp,
|
||||
nonceStr,
|
||||
package: pkg,
|
||||
signType,
|
||||
},
|
||||
config.apiKey,
|
||||
);
|
||||
return { appId: config.appId, timeStamp, nonceStr, package: pkg, signType, paySign };
|
||||
}
|
||||
|
||||
export function buildV3JsapiParams(config, prepayId) {
|
||||
const timeStamp = String(Math.floor(Date.now() / 1000));
|
||||
const nonceStr = randomNonce(16);
|
||||
const pkg = `prepay_id=${prepayId}`;
|
||||
const message = `${config.appId}\n${timeStamp}\n${nonceStr}\n${pkg}\n`;
|
||||
const paySign = signMessage(message, config.privateKey);
|
||||
return { appId: config.appId, timeStamp, nonceStr, package: pkg, signType: 'RSA', paySign };
|
||||
}
|
||||
|
||||
function createV2Client(config) {
|
||||
const createNativeOrder = async ({ outTradeNo, description, amountCents, clientIp }) => {
|
||||
const data = await wechatV2Request(config, '/pay/unifiedorder', {
|
||||
@@ -254,6 +281,25 @@ function createV2Client(config) {
|
||||
return { h5Url: data.mweb_url };
|
||||
};
|
||||
|
||||
const createJsapiOrder = async ({ outTradeNo, description, amountCents, clientIp, openid }) => {
|
||||
if (!openid) {
|
||||
throw new Error('JSAPI 支付缺少 openid');
|
||||
}
|
||||
const data = await wechatV2Request(config, '/pay/unifiedorder', {
|
||||
body: description,
|
||||
out_trade_no: outTradeNo,
|
||||
total_fee: String(amountCents),
|
||||
spbill_create_ip: clientIp || '127.0.0.1',
|
||||
notify_url: config.notifyUrl,
|
||||
trade_type: 'JSAPI',
|
||||
openid,
|
||||
});
|
||||
if (!data.prepay_id) {
|
||||
throw new Error('微信支付未返回 prepay_id');
|
||||
}
|
||||
return { jsapiParams: buildV2JsapiParams(config, data.prepay_id) };
|
||||
};
|
||||
|
||||
const verifyNotify = ({ headers: _headers, body }) => {
|
||||
const bodyText = typeof body === 'string' ? body : String(body ?? '');
|
||||
const fields = parseXmlFields(bodyText);
|
||||
@@ -279,8 +325,10 @@ function createV2Client(config) {
|
||||
return {
|
||||
enabled: true,
|
||||
apiVersion: 'v2',
|
||||
appId: config.appId,
|
||||
createNativeOrder,
|
||||
createH5Order,
|
||||
createJsapiOrder,
|
||||
verifyNotify,
|
||||
};
|
||||
}
|
||||
@@ -319,6 +367,26 @@ function createV3Client(config) {
|
||||
return { h5Url: data.h5_url };
|
||||
};
|
||||
|
||||
const createJsapiOrder = async ({ outTradeNo, description, amountCents, clientIp, openid }) => {
|
||||
if (!openid) {
|
||||
throw new Error('JSAPI 支付缺少 openid');
|
||||
}
|
||||
const data = await wechatV3Request(config, 'POST', '/v3/pay/transactions/jsapi', {
|
||||
appid: config.appId,
|
||||
mchid: config.mchId,
|
||||
description,
|
||||
out_trade_no: outTradeNo,
|
||||
notify_url: config.notifyUrl,
|
||||
amount: { total: amountCents, currency: 'CNY' },
|
||||
payer: { openid },
|
||||
scene_info: clientIp ? { payer_client_ip: clientIp } : undefined,
|
||||
});
|
||||
if (!data.prepay_id) {
|
||||
throw new Error('微信支付未返回 prepay_id');
|
||||
}
|
||||
return { jsapiParams: buildV3JsapiParams(config, data.prepay_id) };
|
||||
};
|
||||
|
||||
const verifyNotify = ({ headers, body }) => {
|
||||
const timestamp = headers['wechatpay-timestamp'] ?? headers['Wechatpay-Timestamp'];
|
||||
const nonce = headers['wechatpay-nonce'] ?? headers['Wechatpay-Nonce'];
|
||||
@@ -371,8 +439,10 @@ function createV3Client(config) {
|
||||
return {
|
||||
enabled: true,
|
||||
apiVersion: 'v3',
|
||||
appId: config.appId,
|
||||
createNativeOrder,
|
||||
createH5Order,
|
||||
createJsapiOrder,
|
||||
verifyNotify,
|
||||
};
|
||||
}
|
||||
@@ -382,12 +452,16 @@ export function createWechatPayClient(config) {
|
||||
return {
|
||||
enabled: false,
|
||||
apiVersion: config?.apiVersion ?? null,
|
||||
appId: config?.appId ?? null,
|
||||
async createNativeOrder() {
|
||||
throw new Error('微信支付未配置');
|
||||
},
|
||||
async createH5Order() {
|
||||
throw new Error('微信支付未配置');
|
||||
},
|
||||
async createJsapiOrder() {
|
||||
throw new Error('微信支付未配置');
|
||||
},
|
||||
verifyNotify() {
|
||||
throw new Error('微信支付未配置');
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user