Files
order_site/apps/backend/src/services/open-91/payload.ts
T
2026-05-26 09:03:03 +08:00

137 lines
3.8 KiB
TypeScript

import { createHttpError } from '../../utils/http.js'
import { assertOpen91Config } from './config.js'
type JsonObject = Record<string, any>
export function normalizeOpen91String(value: unknown) {
return String(value || '').trim()
}
export function normalizeOpen91CreatePayload(payload: JsonObject = {}) {
return {
orderNo: normalizeOpen91String(payload.orderNo),
productNo: normalizeOpen91String(payload.productNo),
buyNum: normalizeOpen91BuyNum(payload.buyNum),
maxAmount: normalizeOpen91OptionalAmount(payload.maxAmount),
callbackUrl: normalizeOpen91String(payload.callbackUrl),
timestamp: normalizeOpen91Timestamp(payload.timestamp),
version: normalizeOpen91String(payload.version),
sign: normalizeOpen91String(payload.sign).toUpperCase(),
}
}
export function normalizeOpen91QueryPayload(payload: JsonObject = {}) {
return {
orderNo: normalizeOpen91String(payload.orderNo),
timestamp: normalizeOpen91Timestamp(payload.timestamp),
version: normalizeOpen91String(payload.version),
sign: normalizeOpen91String(payload.sign).toUpperCase(),
}
}
export function assertOpen91CreatePayload(payload: JsonObject, config = assertOpen91Config()) {
if (!payload.orderNo) {
throw createHttpError('缺少 orderNo', {
statusCode: 400,
errorCode: 'open91_missing_order_no',
})
}
if (!payload.productNo) {
throw createHttpError('缺少 productNo', {
statusCode: 400,
errorCode: 'open91_missing_product_no',
})
}
if (!Number.isInteger(payload.buyNum) || payload.buyNum <= 0) {
throw createHttpError('buyNum 必须是大于 0 的整数', {
statusCode: 400,
errorCode: 'open91_invalid_buy_num',
})
}
assertOpen91CommonPayload(payload, config)
}
export function assertOpen91QueryPayload(payload: JsonObject, config = assertOpen91Config()) {
if (!payload.orderNo) {
throw createHttpError('缺少 orderNo', {
statusCode: 400,
errorCode: 'open91_missing_order_no',
})
}
assertOpen91CommonPayload(payload, config)
}
export function assertOpen91Timestamp(timestamp: unknown, toleranceSeconds = assertOpen91Config().timestampToleranceSeconds) {
const nowSeconds = Math.floor(Date.now() / 1000)
if (Math.abs(nowSeconds - Number(timestamp)) > Math.max(1, Number(toleranceSeconds) || 600)) {
throw createHttpError('timestamp 已过期', {
statusCode: 400,
errorCode: 'open91_expired_timestamp',
})
}
}
function assertOpen91CommonPayload(payload: JsonObject, config: JsonObject) {
if (!payload.timestamp) {
throw createHttpError('缺少 timestamp', {
statusCode: 400,
errorCode: 'open91_missing_timestamp',
})
}
if (!payload.version) {
throw createHttpError('缺少 version', {
statusCode: 400,
errorCode: 'open91_missing_version',
})
}
if (payload.version !== config.version) {
throw createHttpError(`version 不匹配,当前仅支持 ${config.version}`, {
statusCode: 400,
errorCode: 'open91_invalid_version',
})
}
if (!payload.sign) {
throw createHttpError('缺少 sign', {
statusCode: 400,
errorCode: 'open91_missing_sign',
})
}
}
function normalizeOpen91BuyNum(value: unknown) {
const parsed = Number(value)
return Number.isInteger(parsed) ? parsed : 0
}
function normalizeOpen91OptionalAmount(value: unknown) {
const normalized = normalizeOpen91String(value)
if (!normalized) {
return ''
}
if (!/^\d+(?:\.\d+)?$/.test(normalized)) {
throw createHttpError('maxAmount 格式无效', {
statusCode: 400,
errorCode: 'open91_invalid_max_amount',
})
}
return normalized
}
function normalizeOpen91Timestamp(value: unknown) {
const normalized = normalizeOpen91String(value)
if (!/^\d{10}$/.test(normalized)) {
return 0
}
return Number(normalized)
}