feat(M08-B): 补保洁微信转账回调与轮询
This commit is contained in:
@@ -51,6 +51,59 @@ const actor = {
|
||||
assert.equal(harness.repository.paid.length, 0);
|
||||
}
|
||||
|
||||
{
|
||||
const harness = createHarness({ queryState: 'SUCCESS' });
|
||||
const result = await harness.service.syncWechatTransfer({
|
||||
...actor,
|
||||
settlementId: '501',
|
||||
note: 'poll success'
|
||||
});
|
||||
assert.equal(result.transferState, 'SUCCESS');
|
||||
assert.equal(harness.state.queryOutBillNo, 'CLS-20260627-501');
|
||||
assert.equal(harness.repository.paid[0].payoutReference, 'wx-transfer-query-501');
|
||||
}
|
||||
|
||||
{
|
||||
const harness = createHarness({ queryState: 'FAIL', queryFailReason: 'ACCOUNT_ABNORMAL' });
|
||||
const result = await harness.service.syncWechatTransfer({
|
||||
...actor,
|
||||
settlementId: '501'
|
||||
});
|
||||
assert.equal(result.transferState, 'FAIL');
|
||||
assert.equal(harness.repository.failures[0].error, 'ACCOUNT_ABNORMAL');
|
||||
}
|
||||
|
||||
{
|
||||
const harness = createHarness({ queryState: 'PROCESSING' });
|
||||
const result = await harness.service.syncWechatTransfer({
|
||||
...actor,
|
||||
settlementId: '501'
|
||||
});
|
||||
assert.equal(result.transferState, 'PROCESSING');
|
||||
assert.equal(harness.repository.pending[0].payoutState, 'PROCESSING');
|
||||
}
|
||||
|
||||
{
|
||||
const harness = createHarness({
|
||||
notificationPayload: {
|
||||
mch_id: '1900000109',
|
||||
out_bill_no: 'CLS-20260627-501',
|
||||
transfer_bill_no: 'wx-notify-501',
|
||||
state: 'SUCCESS'
|
||||
}
|
||||
});
|
||||
const result = await harness.service.processWechatTransferNotification({
|
||||
timestamp: '1782700000',
|
||||
nonce: 'notify-nonce',
|
||||
serial: 'PLATFORM-SERIAL',
|
||||
signature: 'signature'
|
||||
}, '{"resource":"encrypted"}', 'notify-trace');
|
||||
assert.equal(result.transferState, 'SUCCESS');
|
||||
assert.equal(harness.state.verifiedNotification.rawBody, '{"resource":"encrypted"}');
|
||||
assert.equal(harness.repository.paid[0].tenantId, '7');
|
||||
assert.equal(harness.repository.paid[0].payoutReference, 'wx-notify-501');
|
||||
}
|
||||
|
||||
{
|
||||
const harness = createHarness({ transferState: 'FAIL', failReason: 'REAL_NAME_CHECK_FAILED' });
|
||||
const result = await harness.service.executeWechatTransfer({
|
||||
@@ -89,6 +142,7 @@ const actor = {
|
||||
function createHarness(options = {}) {
|
||||
const state = {
|
||||
transferInput: null,
|
||||
queryOutBillNo: null,
|
||||
settlement: {
|
||||
id: '501',
|
||||
settlementNo: 'CLS-20260627-501',
|
||||
@@ -96,8 +150,10 @@ function createHarness(options = {}) {
|
||||
storeId: '11',
|
||||
status: options.status ?? 'CONFIRMED',
|
||||
totalRewardCents: 1200,
|
||||
payoutReference: '',
|
||||
payoutState: ''
|
||||
payoutChannel: options.payoutChannel ?? 'WECHAT_TRANSFER',
|
||||
payoutReference: options.payoutReference ?? 'CLS-20260627-501',
|
||||
payoutState: options.payoutState ?? 'WAIT_USER_CONFIRM',
|
||||
payoutPackageInfo: options.payoutPackageInfo ?? 'package-info'
|
||||
},
|
||||
account: {
|
||||
id: '41',
|
||||
@@ -110,6 +166,9 @@ function createHarness(options = {}) {
|
||||
};
|
||||
const pool = {
|
||||
async execute(sql) {
|
||||
if (sql.includes("payout_channel = 'WECHAT_TRANSFER'")) {
|
||||
return [[{ ...state.settlement, tenantId: '7' }], []];
|
||||
}
|
||||
if (sql.includes('FROM qipai_cleaning_settlements')) {
|
||||
return [[state.settlement], []];
|
||||
}
|
||||
@@ -149,6 +208,21 @@ function createHarness(options = {}) {
|
||||
failReason: options.failReason ?? '',
|
||||
packageInfo: options.packageInfo ?? ''
|
||||
};
|
||||
},
|
||||
async queryMerchantTransferByOutBillNo(_credential, outBillNo) {
|
||||
state.queryOutBillNo = outBillNo;
|
||||
return {
|
||||
merchantId: options.queryMerchantId ?? '1900000109',
|
||||
outBillNo,
|
||||
transferBillNo: 'wx-transfer-query-501',
|
||||
state: options.queryState ?? 'SUCCESS',
|
||||
failReason: options.queryFailReason ?? '',
|
||||
amountCents: options.queryAmountCents ?? 1200
|
||||
};
|
||||
},
|
||||
verifyAndDecrypt(_credential, headers, rawBody) {
|
||||
state.verifiedNotification = { headers, rawBody };
|
||||
return options.notificationPayload ?? {};
|
||||
}
|
||||
};
|
||||
const credential = {
|
||||
@@ -157,7 +231,7 @@ function createHarness(options = {}) {
|
||||
serialNo: 'serial',
|
||||
privateKeyPem: 'private-key',
|
||||
apiV3Key: '0123456789abcdef0123456789abcdef',
|
||||
platformCertificates: {},
|
||||
platformCertificates: { 'PLATFORM-SERIAL': 'certificate' },
|
||||
transferSceneId: '1000',
|
||||
transferSceneReportInfos: [{ infoType: '岗位类型', infoContent: '保洁员' }]
|
||||
};
|
||||
|
||||
@@ -159,6 +159,30 @@ const app = await buildApp({
|
||||
transferState: input.mode === 'MOCK' ? 'SUCCESS' : 'WAIT_USER_CONFIRM',
|
||||
idempotent: false
|
||||
};
|
||||
},
|
||||
async syncWechatTransfer(input) {
|
||||
calls.push(['syncWechatTransfer', input]);
|
||||
return {
|
||||
settlement: {
|
||||
...settlementResponse('PAID'),
|
||||
payoutChannel: 'WECHAT_TRANSFER',
|
||||
payoutReference: 'wx-transfer-sync-501'
|
||||
},
|
||||
transferState: 'SUCCESS',
|
||||
idempotent: false
|
||||
};
|
||||
},
|
||||
async processWechatTransferNotification(headers, rawBody, traceId) {
|
||||
calls.push(['processWechatTransferNotification', { headers, rawBody, traceId }]);
|
||||
return {
|
||||
settlement: {
|
||||
...settlementResponse('PAID'),
|
||||
payoutChannel: 'WECHAT_TRANSFER',
|
||||
payoutReference: 'wx-transfer-notify-501'
|
||||
},
|
||||
transferState: 'SUCCESS',
|
||||
idempotent: false
|
||||
};
|
||||
}
|
||||
},
|
||||
mediaStorage: {
|
||||
@@ -395,6 +419,36 @@ assert.equal(calls.at(-1)[0], 'executeWechatTransfer');
|
||||
assert.equal(calls.at(-1)[1].settlementId, '501');
|
||||
assert.equal(calls.at(-1)[1].mode, 'API');
|
||||
|
||||
const syncedWechatTransfer = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/admin-api/cleaning/settlements/501/wechat-transfer/sync',
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
payload: { note: 'poll wechat transfer' }
|
||||
});
|
||||
assert.equal(syncedWechatTransfer.statusCode, 200);
|
||||
assert.equal(syncedWechatTransfer.json().data.transferState, 'SUCCESS');
|
||||
assert.equal(syncedWechatTransfer.json().data.settlement.payoutReference, 'wx-transfer-sync-501');
|
||||
assert.equal(calls.at(-1)[0], 'syncWechatTransfer');
|
||||
assert.equal(calls.at(-1)[1].settlementId, '501');
|
||||
|
||||
const wechatTransferNotify = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/app-api/cleaning/wechat-transfer/notify',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
'wechatpay-timestamp': '1782700001',
|
||||
'wechatpay-nonce': 'notify-nonce',
|
||||
'wechatpay-serial': 'PLATFORM-SERIAL',
|
||||
'wechatpay-signature': 'signature'
|
||||
},
|
||||
payload: '{"resource":"encrypted"}'
|
||||
});
|
||||
assert.equal(wechatTransferNotify.statusCode, 200);
|
||||
assert.equal(wechatTransferNotify.json().code, 'SUCCESS');
|
||||
assert.equal(calls.at(-1)[0], 'processWechatTransferNotification');
|
||||
assert.equal(calls.at(-1)[1].headers.serial, 'PLATFORM-SERIAL');
|
||||
assert.equal(calls.at(-1)[1].rawBody, '{"resource":"encrypted"}');
|
||||
|
||||
const reclaimed = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/admin-api/cleaning/reclaim-timeouts',
|
||||
|
||||
@@ -35,6 +35,13 @@ const responses = [
|
||||
transfer_bill_no: 'wx-transfer-501',
|
||||
state: 'WAIT_USER_CONFIRM',
|
||||
package_info: 'package-info'
|
||||
},
|
||||
{
|
||||
mch_id: '1900000109',
|
||||
out_bill_no: 'CLP501',
|
||||
transfer_bill_no: 'wx-transfer-501',
|
||||
state: 'SUCCESS',
|
||||
transfer_amount: 800
|
||||
}
|
||||
];
|
||||
const transport = {
|
||||
@@ -94,6 +101,12 @@ assert.match(requests[4].body, /"out_bill_no":"CLP501"/);
|
||||
assert.match(requests[4].body, /"transfer_amount":800/);
|
||||
assert.equal(requests[4].headers.Authorization.includes(merchantKeys.privateKey), false);
|
||||
|
||||
const queriedTransfer = await client.queryMerchantTransferByOutBillNo(credential, 'CLP501');
|
||||
assert.equal(queriedTransfer.state, 'SUCCESS');
|
||||
assert.equal(queriedTransfer.amountCents, 800);
|
||||
assert.match(requests[5].url, /\/v3\/fund-app\/mch-transfer\/transfer-bills\/out-bill-no\/CLP501$/);
|
||||
assert.equal(requests[5].headers.Authorization.includes(merchantKeys.privateKey), false);
|
||||
|
||||
const notification = encryptedNotification({
|
||||
out_trade_no: 'PAY-M05B-001',
|
||||
transaction_id: 'wx-transaction-test',
|
||||
|
||||
Reference in New Issue
Block a user