feat: add provider bill download reconciliation jobs

This commit is contained in:
Codex
2026-06-29 21:59:42 +08:00
parent aeca84b260
commit f3577e257c
21 changed files with 1691 additions and 158 deletions

View File

@@ -2484,6 +2484,63 @@ async function testCommerce() {
});
assert.equal(crossTenantReconciliationDenied.code, 'TENANT_ADMIN_REQUIRED', 'reconciliation items must be tenant isolated');
const studentProviderBillRequestDenied = await request('/api/commerce/reconciliation/provider-bills/request', {
method: 'POST',
body: {
provider: 'wechat_pay',
billDate,
billType: 'payment',
},
expectStatus: 403,
});
assert.equal(studentProviderBillRequestDenied.code, 'TENANT_ADMIN_REQUIRED', 'students must not request official provider bill downloads');
const providerBillJob = await request('/api/commerce/reconciliation/provider-bills/request', {
userId: TENANT_ADMIN_USER_ID,
method: 'POST',
body: {
provider: 'wechat_pay',
billDate,
billType: 'payment',
metadata: { source: 'api-integration-test' },
},
});
assert.ok(providerBillJob.item?.id, 'tenant admin should request official provider bill download job');
assert.equal(providerBillJob.item?.status, 'queued', 'new provider bill job should be queued');
assert.equal(providerBillJob.item?.provider, 'wechat_pay', 'provider bill job should keep provider');
assert.ok(!JSON.stringify(providerBillJob).includes(paymentFixture.wechatApiV3Key), 'provider bill job response must not leak payment secrets');
assert.ok(!JSON.stringify(providerBillJob).includes('PRIVATE KEY'), 'provider bill job response must not leak private keys');
const providerBillJobAgain = await request('/api/commerce/reconciliation/provider-bills/request', {
userId: TENANT_ADMIN_USER_ID,
method: 'POST',
body: {
provider: 'wechat_pay',
billDate,
billType: 'payment',
},
});
assert.equal(providerBillJobAgain.item?.id, providerBillJob.item.id, 'provider bill job request should be idempotent by provider/date/type');
assert.equal(providerBillJobAgain.idempotent, true, 'provider bill duplicate request should return idempotent=true');
const providerBillJobs = await request('/api/commerce/reconciliation/provider-bills/jobs', {
userId: TENANT_ADMIN_USER_ID,
query: { provider: 'wechat_pay', billDate },
});
assert.ok(
providerBillJobs.items?.some(item => item.id === providerBillJob.item.id),
'provider bill jobs endpoint should list tenant jobs',
);
assert.ok(!JSON.stringify(providerBillJobs).includes(paymentFixture.wechatApiV3Key), 'provider bill jobs list must not leak payment secrets');
const crossTenantProviderBillJobsDenied = await request('/api/commerce/reconciliation/provider-bills/jobs', {
tenantId: PARTNER_TENANT_ID,
userId: TENANT_ADMIN_USER_ID,
query: { provider: 'wechat_pay', billDate },
expectStatus: 403,
});
assert.equal(crossTenantProviderBillJobsDenied.code, 'TENANT_ADMIN_REQUIRED', 'provider bill jobs must be tenant isolated');
const fakeWechatPay = await startFakeWechatPayServer();
const wechatAccount = await request('/api/tenant-admin/payment-accounts', {
userId: TENANT_ADMIN_USER_ID,