36a8719d0f
在虚拟支付商品入账后调用微信发货确认接口 允许已入账商品订单只重试发货而不重复发放权益 遇到失效 access token 时强制刷新并最多重放一次 要求使用微信 paid_time 并完善历史补单文件清理和回归测试
143 lines
3.4 KiB
JavaScript
143 lines
3.4 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import assert from 'node:assert/strict';
|
|
|
|
import {
|
|
buildReconcileFingerprint,
|
|
calcPaySig,
|
|
normalizeLocalOrderSnapshot,
|
|
notifyWechatGoodsDelivered,
|
|
paidAtMicrosFromWechatOrder,
|
|
parseEnvText,
|
|
validateQueryResult,
|
|
} from './reconcile-wechat-virtual-payment-order.mjs';
|
|
|
|
assert.equal(
|
|
calcPaySig(
|
|
'12345',
|
|
'/xpay/query_user_balance',
|
|
'{"openid": "xxx", "user_ip": "127.0.0.1", "env": 0}',
|
|
),
|
|
'c37809f27c6d7fd1837ad2500a04512b66b34fd793a39a385fade56dca89a4b5',
|
|
);
|
|
|
|
assert.deepEqual(
|
|
parseEnvText(
|
|
'WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_ENV=0 # 现网\nWECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_APP_KEY="app-key"\n',
|
|
),
|
|
{
|
|
WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_APP_KEY: 'app-key',
|
|
WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_ENV: '0',
|
|
},
|
|
);
|
|
|
|
const localOrder = { amountCents: 600, orderId: 'order-001' };
|
|
const paidOrder = {
|
|
order_fee: 600,
|
|
order_id: 'order-001',
|
|
order_type: 0,
|
|
status: 2,
|
|
};
|
|
assert.deepEqual(validateQueryResult(localOrder, paidOrder), {
|
|
eligibleForCredit: true,
|
|
status: 2,
|
|
});
|
|
assert.deepEqual(validateQueryResult(localOrder, { ...paidOrder, status: 1 }), {
|
|
eligibleForCredit: false,
|
|
status: 1,
|
|
});
|
|
assert.deepEqual(
|
|
validateQueryResult(localOrder, { ...paidOrder, order_type: 7 }),
|
|
{ eligibleForCredit: true, status: 2 },
|
|
);
|
|
assert.equal(
|
|
paidAtMicrosFromWechatOrder({ paid_time: 1_777_111_200 }),
|
|
1_777_111_200_000_000,
|
|
);
|
|
assert.throws(() => paidAtMicrosFromWechatOrder({}), /paid_time/u);
|
|
assert.throws(
|
|
() => paidAtMicrosFromWechatOrder({ paid_time: Number.MAX_SAFE_INTEGER }),
|
|
/安全整数范围/u,
|
|
);
|
|
assert.throws(
|
|
() => validateQueryResult(localOrder, { ...paidOrder, order_fee: 601 }),
|
|
/金额/u,
|
|
);
|
|
assert.throws(
|
|
() => validateQueryResult(localOrder, { ...paidOrder, order_type: 1 }),
|
|
/可入账虚拟支付单/u,
|
|
);
|
|
assert.throws(
|
|
() => validateQueryResult(localOrder, { ...paidOrder, order_type: 8 }),
|
|
/可入账虚拟支付单/u,
|
|
);
|
|
assert.equal(
|
|
buildReconcileFingerprint({ orderId: 'order-001', status: 2 }),
|
|
buildReconcileFingerprint({ orderId: 'order-001', status: 2 }),
|
|
);
|
|
assert.notEqual(
|
|
buildReconcileFingerprint({ orderId: 'order-001', status: 2 }),
|
|
buildReconcileFingerprint({ orderId: 'order-001', status: 1 }),
|
|
);
|
|
assert.deepEqual(
|
|
normalizeLocalOrderSnapshot([
|
|
'order-001',
|
|
'user-001',
|
|
'points_60',
|
|
'60 泥点',
|
|
[0],
|
|
600,
|
|
[0],
|
|
'wechat_mp_virtual',
|
|
[1],
|
|
[1],
|
|
1_777_111_200_000_000,
|
|
0,
|
|
[1],
|
|
[1],
|
|
[1],
|
|
[1],
|
|
[1],
|
|
]),
|
|
{
|
|
amount_cents: 600,
|
|
kind: [0],
|
|
order_id: 'order-001',
|
|
payment_channel: 'wechat_mp_virtual',
|
|
status: [0],
|
|
user_id: 'user-001',
|
|
},
|
|
);
|
|
|
|
const originalFetch = globalThis.fetch;
|
|
let notifyRequest;
|
|
globalThis.fetch = async (url, init) => {
|
|
notifyRequest = { url: String(url), init };
|
|
return {
|
|
ok: true,
|
|
status: 200,
|
|
text: async () => '',
|
|
};
|
|
};
|
|
try {
|
|
await notifyWechatGoodsDelivered(
|
|
{
|
|
accessToken: 'access-token-001',
|
|
notifyProvideGoodsEndpoint:
|
|
'https://api.weixin.qq.com/xpay/notify_provide_goods',
|
|
paymentEnv: 0,
|
|
},
|
|
'order-001',
|
|
);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
assert.equal(
|
|
notifyRequest.url,
|
|
'https://api.weixin.qq.com/xpay/notify_provide_goods?access_token=access-token-001',
|
|
);
|
|
assert.equal(notifyRequest.init.method, 'POST');
|
|
assert.equal(notifyRequest.init.body, '{"order_id":"order-001","env":0}');
|
|
|
|
console.log('wechat virtual payment reconcile checks passed');
|