feat(store): 添加门店、门店验证
This commit is contained in:
@@ -41,6 +41,10 @@ export class PlatformsService {
|
||||
platform.capabilities,
|
||||
'default_locale',
|
||||
),
|
||||
default_currency_code: stringCapability(
|
||||
platform.capabilities,
|
||||
'default_currency_code',
|
||||
),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
createDecipheriv,
|
||||
createCipheriv,
|
||||
createHash,
|
||||
createHmac,
|
||||
@@ -35,3 +36,23 @@ export function encryptStoreSecret(
|
||||
.digest('base64url'),
|
||||
};
|
||||
}
|
||||
|
||||
/** 仅供受信任的后台 Worker 使用;调用方不得记录返回值。 */
|
||||
export function decryptStoreSecret(
|
||||
ciphertextWithTag: Buffer,
|
||||
nonce: Buffer,
|
||||
keyMaterial: string,
|
||||
): string {
|
||||
const authenticationTagLength = 16;
|
||||
if (ciphertextWithTag.length <= authenticationTagLength) {
|
||||
throw new Error('Invalid encrypted store secret');
|
||||
}
|
||||
const key = createHash('sha256').update(keyMaterial).digest();
|
||||
const ciphertext = ciphertextWithTag.subarray(0, -authenticationTagLength);
|
||||
const tag = ciphertextWithTag.subarray(-authenticationTagLength);
|
||||
const decipher = createDecipheriv('aes-256-gcm', key, nonce);
|
||||
decipher.setAuthTag(tag);
|
||||
return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString(
|
||||
'utf8',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -470,6 +470,19 @@ export class StoresService {
|
||||
'门店凭据尚未验证通过,无法同步仓库',
|
||||
);
|
||||
}
|
||||
if (kind === 'store_credential_verification') {
|
||||
await transaction.query(
|
||||
`UPDATE atlas.stores SET status = 'verifying', credential_status = 'unknown',
|
||||
last_error_code = NULL, last_error_message = NULL, last_error_at = NULL,
|
||||
revision = revision + 1, updated_at = now() WHERE id = $1`,
|
||||
[storeId],
|
||||
);
|
||||
await transaction.query(
|
||||
`UPDATE atlas.store_secrets SET status = 'unknown', updated_at = now()
|
||||
WHERE store_id = $1 AND kind = 'seller_api_key' AND revoked_at IS NULL`,
|
||||
[storeId],
|
||||
);
|
||||
}
|
||||
const operation = await this.createOperation(
|
||||
transaction,
|
||||
userId,
|
||||
@@ -605,6 +618,20 @@ export class StoresService {
|
||||
[kind, storeId, userId],
|
||||
);
|
||||
const operation = toOperation(created.rows[0]);
|
||||
const payload: { store_id: string; operation_id: string; secret_id?: string } = {
|
||||
store_id: storeId,
|
||||
operation_id: operation.id,
|
||||
};
|
||||
// 验证任务绑定创建当刻的密钥行;密钥后来被替换时,旧任务不得验证或覆盖新状态。
|
||||
if (kind === 'store_credential_verification') {
|
||||
const secret = await transaction.query<{ id: string }>(
|
||||
`SELECT id FROM atlas.store_secrets
|
||||
WHERE store_id = $1 AND kind = 'seller_api_key' AND revoked_at IS NULL`,
|
||||
[storeId],
|
||||
);
|
||||
if (!secret.rows[0]) throw new Error('Current store secret is missing');
|
||||
payload.secret_id = secret.rows[0].id;
|
||||
}
|
||||
await transaction.query(
|
||||
`INSERT INTO atlas.outbox_events
|
||||
(workspace_id, aggregate_type, aggregate_id, event_type, event_payload, deduplication_key)
|
||||
@@ -612,7 +639,7 @@ export class StoresService {
|
||||
[
|
||||
storeId,
|
||||
`${kind}.requested`,
|
||||
{ store_id: storeId, operation_id: operation.id },
|
||||
payload,
|
||||
operation.id,
|
||||
],
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user