Files
memind/wechat-pay.test.mjs
john f818dd0307 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>
2026-06-16 16:20:04 +08:00

123 lines
4.1 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
buildV2JsapiParams,
buildXml,
createWechatPayClient,
loadWechatPayConfig,
parseXmlFields,
signV2Params,
} from './wechat-pay.mjs';
test('signV2Params is deterministic and uppercase MD5', () => {
const params = {
appid: 'wx8888888888888888',
mch_id: '1900000109',
body: 'test',
nonce_str: 'ibuaiVcKdpRxkhJA',
};
const apiKey = '8934e7d15453e97507ef794cf7b0519d';
const sign = signV2Params(params, apiKey);
assert.match(sign, /^[0-9A-F]{32}$/);
assert.equal(sign, signV2Params(params, apiKey));
assert.notEqual(sign, signV2Params({ ...params, body: 'other' }, apiKey));
});
test('parseXmlFields and buildXml round-trip', () => {
const xml = buildXml({
return_code: 'SUCCESS',
out_trade_no: 'TK123',
total_fee: '500',
});
const fields = parseXmlFields(xml);
assert.equal(fields.return_code, 'SUCCESS');
assert.equal(fields.out_trade_no, 'TK123');
assert.equal(fields.total_fee, '500');
});
test('loadWechatPayConfig selects v2 when APIv2 key is configured', () => {
const previous = {
enabled: process.env.H5_WECHAT_PAY_ENABLED,
appId: process.env.H5_WECHAT_APP_ID,
mchId: process.env.H5_WECHAT_MCH_ID,
apiV2Key: process.env.H5_WECHAT_API_V2_KEY,
apiVersion: process.env.H5_WECHAT_API_VERSION,
notifyUrl: process.env.H5_WECHAT_NOTIFY_URL,
v3Key: process.env.H5_WECHAT_API_V3_KEY,
serialNo: process.env.H5_WECHAT_SERIAL_NO,
privateKey: process.env.H5_WECHAT_PRIVATE_KEY,
};
process.env.H5_WECHAT_PAY_ENABLED = '1';
process.env.H5_WECHAT_APP_ID = 'wxtestappid';
process.env.H5_WECHAT_MCH_ID = '1234567890';
process.env.H5_WECHAT_API_V2_KEY = 'test-api-v2-key-32chars-long!!!!';
process.env.H5_WECHAT_NOTIFY_URL = 'https://example.com/webhooks/wechat-pay/notify';
delete process.env.H5_WECHAT_API_VERSION;
delete process.env.H5_WECHAT_API_V3_KEY;
delete process.env.H5_WECHAT_SERIAL_NO;
delete process.env.H5_WECHAT_PRIVATE_KEY;
try {
const config = loadWechatPayConfig();
assert.equal(config.apiVersion, 'v2');
assert.equal(config.enabled, true);
const client = createWechatPayClient(config);
assert.equal(client.apiVersion, 'v2');
assert.equal(client.enabled, true);
} finally {
for (const [key, value] of Object.entries(previous)) {
const envKey = {
enabled: 'H5_WECHAT_PAY_ENABLED',
appId: 'H5_WECHAT_APP_ID',
mchId: 'H5_WECHAT_MCH_ID',
apiV2Key: 'H5_WECHAT_API_V2_KEY',
apiVersion: 'H5_WECHAT_API_VERSION',
notifyUrl: 'H5_WECHAT_NOTIFY_URL',
v3Key: 'H5_WECHAT_API_V3_KEY',
serialNo: 'H5_WECHAT_SERIAL_NO',
privateKey: 'H5_WECHAT_PRIVATE_KEY',
}[key];
if (value === undefined) delete process.env[envKey];
else process.env[envKey] = value;
}
}
});
test('buildV2JsapiParams signs prepay package for WeixinJSBridge', () => {
const config = {
appId: 'wxtestappid',
apiKey: 'test-api-v2-key-32chars-long!!!!',
};
const params = buildV2JsapiParams(config, 'wx201410272009395522657a690389285100');
assert.equal(params.appId, 'wxtestappid');
assert.equal(params.package, 'prepay_id=wx201410272009395522657a690389285100');
assert.equal(params.signType, 'MD5');
assert.match(params.paySign, /^[0-9A-F]{32}$/);
});
test('verifyNotify validates v2 callback signature', () => {
const apiKey = 'test-api-v2-key-32chars-long!!!!';
const fields = {
return_code: 'SUCCESS',
result_code: 'SUCCESS',
out_trade_no: 'TKORDER001',
transaction_id: '4200001234',
total_fee: '1000',
nonce_str: 'abc123',
};
fields.sign = signV2Params(fields, apiKey);
const body = buildXml(fields);
const client = createWechatPayClient({
enabled: true,
apiVersion: 'v2',
appId: 'wxtest',
mchId: '123',
apiKey,
notifyUrl: 'https://example.com/notify',
skipNotifyVerify: false,
});
const { transaction } = client.verifyNotify({ headers: {}, body });
assert.equal(transaction.out_trade_no, 'TKORDER001');
assert.equal(transaction.trade_state, 'SUCCESS');
assert.equal(transaction.amount.total, 1000);
});