f818dd0307
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>
123 lines
4.1 KiB
JavaScript
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);
|
|
});
|