228 lines
9.1 KiB
JavaScript
228 lines
9.1 KiB
JavaScript
import { createRequestError } from './request-client.js'
|
|
import { normalizeOptionalNumericId } from './response-normalizers.js'
|
|
|
|
const normalizeVipText = (value, field, { required = false } = {}) => {
|
|
if (value === undefined || value === null) {
|
|
if (required) throw createRequestError(`VIP 响应缺少 ${field}`, 'VIP_RESPONSE_INVALID')
|
|
return ''
|
|
}
|
|
if (typeof value !== 'string') {
|
|
throw createRequestError(`VIP 响应 ${field} 无效`, 'VIP_RESPONSE_INVALID')
|
|
}
|
|
const text = value.trim()
|
|
if (required && !text) throw createRequestError(`VIP 响应缺少 ${field}`, 'VIP_RESPONSE_INVALID')
|
|
return text
|
|
}
|
|
|
|
const normalizeVipAmount = (value, field, { required = false } = {}) => {
|
|
if (value === undefined || value === null || value === '') {
|
|
if (required) throw createRequestError(`VIP 响应缺少 ${field}`, 'VIP_RESPONSE_INVALID')
|
|
return ''
|
|
}
|
|
const text = typeof value === 'number' && Number.isFinite(value) ? String(value) : value
|
|
if (typeof text !== 'string') throw createRequestError(`VIP 响应 ${field} 无效`, 'VIP_RESPONSE_INVALID')
|
|
const match = text.trim().match(/^(0|[1-9]\d*)(?:\.(\d{1,2}))?$/)
|
|
if (!match) throw createRequestError(`VIP 响应 ${field} 无效`, 'VIP_RESPONSE_INVALID')
|
|
return `${match[1]}.${(match[2] || '').padEnd(2, '0')}`
|
|
}
|
|
|
|
const vipOrderStatusFallbackLabels = Object.freeze({
|
|
'0': '待支付',
|
|
'1': '已支付',
|
|
'2': '已关闭',
|
|
'3': '已退款'
|
|
})
|
|
|
|
const normalizeVipOrderStatus = (value) => {
|
|
const status = normalizeVipText(value, 'payStatus', { required: true })
|
|
return {
|
|
status,
|
|
statusLabel: vipOrderStatusFallbackLabels[status] || '状态待确认'
|
|
}
|
|
}
|
|
|
|
export const VIP_PAYMENT_METHOD = Object.freeze({
|
|
WECHAT: 'WECHAT',
|
|
ALIPAY: 'ALIPAY',
|
|
BALANCE: 'BALANCE'
|
|
})
|
|
|
|
const vipPaymentMethods = new Set(Object.values(VIP_PAYMENT_METHOD))
|
|
const vipPaymentMethodLabels = Object.freeze({
|
|
[VIP_PAYMENT_METHOD.WECHAT]: '微信支付',
|
|
[VIP_PAYMENT_METHOD.ALIPAY]: '支付宝',
|
|
[VIP_PAYMENT_METHOD.BALANCE]: '余额支付'
|
|
})
|
|
|
|
export const assertVipPaymentMethod = (value) => {
|
|
if (!vipPaymentMethods.has(value)) {
|
|
throw new TypeError('VIP 支付方式必须是 WECHAT、ALIPAY 或 BALANCE')
|
|
}
|
|
return value
|
|
}
|
|
|
|
export const normalizeVipPackages = (value) => {
|
|
if (!Array.isArray(value)) throw createRequestError('VIP 套餐响应不是列表', 'VIP_RESPONSE_INVALID')
|
|
const packages = value.map((item) => {
|
|
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
throw createRequestError('VIP 套餐响应包含无效条目', 'VIP_RESPONSE_INVALID')
|
|
}
|
|
const id = normalizeOptionalNumericId(item.packageId, 'VIP 套餐标识', 'VIP_RESPONSE_INVALID')
|
|
if (!id) throw createRequestError('VIP 套餐响应缺少稳定标识', 'VIP_RESPONSE_INVALID')
|
|
return {
|
|
key: `vip-package-${id}`,
|
|
id,
|
|
name: normalizeVipText(item.packageName, 'packageName', { required: true }),
|
|
description: normalizeVipText(item.packageDesc, 'packageDesc'),
|
|
price: normalizeVipAmount(item.price, 'price', { required: true }),
|
|
originalPrice: normalizeVipAmount(item.originalPrice, 'originalPrice')
|
|
}
|
|
})
|
|
if (new Set(packages.map((item) => item.id)).size !== packages.length) {
|
|
throw createRequestError('VIP 套餐响应包含重复标识', 'VIP_RESPONSE_INVALID')
|
|
}
|
|
return packages
|
|
}
|
|
|
|
export const normalizeVipOrders = (value) => {
|
|
if (!Array.isArray(value)) throw createRequestError('VIP 订单响应不是列表', 'VIP_RESPONSE_INVALID')
|
|
const orders = value.map((item) => {
|
|
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
throw createRequestError('VIP 订单响应包含无效条目', 'VIP_RESPONSE_INVALID')
|
|
}
|
|
const id = normalizeOptionalNumericId(item.orderId, 'VIP 订单标识', 'VIP_RESPONSE_INVALID')
|
|
if (!id) throw createRequestError('VIP 订单响应缺少稳定标识', 'VIP_RESPONSE_INVALID')
|
|
const { status, statusLabel } = normalizeVipOrderStatus(item.payStatus)
|
|
return {
|
|
key: `vip-order-${id}`,
|
|
id,
|
|
orderNo: normalizeVipText(item.orderNo, 'orderNo'),
|
|
packageName: normalizeVipText(item.packageName, 'packageName', { required: true }),
|
|
amount: normalizeVipAmount(item.payAmount ?? item.orderAmount, 'payAmount', { required: true }),
|
|
paymentMethod: normalizeVipText(item.payType, 'payType'),
|
|
status,
|
|
statusLabel,
|
|
paidAt: normalizeVipText(item.payTime, 'payTime'),
|
|
expiresAt: normalizeVipText(item.expireTime, 'expireTime')
|
|
}
|
|
})
|
|
if (new Set(orders.map((item) => item.id)).size !== orders.length) {
|
|
throw createRequestError('VIP 订单响应包含重复标识', 'VIP_RESPONSE_INVALID')
|
|
}
|
|
return orders
|
|
}
|
|
|
|
export const normalizeVipCapability = (value) => {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value) || typeof value.enabled !== 'boolean') {
|
|
throw createRequestError('VIP 购买能力响应无效', 'VIP_RESPONSE_INVALID')
|
|
}
|
|
if (value.paymentMethods !== undefined && !Array.isArray(value.paymentMethods)) {
|
|
throw createRequestError('VIP 支付方式能力不是列表', 'VIP_RESPONSE_INVALID')
|
|
}
|
|
const paymentMethods = (value.paymentMethods || []).map((item) => {
|
|
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
throw createRequestError('VIP 支付方式能力包含无效条目', 'VIP_RESPONSE_INVALID')
|
|
}
|
|
const method = normalizeVipText(item.method, 'paymentMethods.method', { required: true })
|
|
if (!vipPaymentMethods.has(method) || typeof item.enabled !== 'boolean') {
|
|
throw createRequestError('VIP 支付方式能力字段无效', 'VIP_RESPONSE_INVALID')
|
|
}
|
|
return {
|
|
method,
|
|
label: vipPaymentMethodLabels[method],
|
|
enabled: item.enabled,
|
|
disabledReason: normalizeVipText(item.disabledReason, 'paymentMethods.disabledReason')
|
|
}
|
|
})
|
|
if (new Set(paymentMethods.map((item) => item.method)).size !== paymentMethods.length) {
|
|
throw createRequestError('VIP 支付方式能力包含重复方式', 'VIP_RESPONSE_INVALID')
|
|
}
|
|
if (value.enabled && !paymentMethods.some((item) => item.enabled)) {
|
|
throw createRequestError('VIP 已开放购买但没有可用支付方式', 'VIP_RESPONSE_INVALID')
|
|
}
|
|
return {
|
|
enabled: value.enabled,
|
|
disabledReason: normalizeVipText(value.disabledReason, 'disabledReason'),
|
|
paymentMethods
|
|
}
|
|
}
|
|
|
|
const normalizeVipId = (value, field) => {
|
|
const id = value === undefined || value === null ? '' : String(value)
|
|
if (!/^[1-9]\d*$/.test(id)) {
|
|
throw createRequestError(`VIP 响应 ${field} 无效`, 'VIP_RESPONSE_INVALID')
|
|
}
|
|
return id
|
|
}
|
|
|
|
const vipPaymentStatuses = new Set([
|
|
'CREATED',
|
|
'PAYING',
|
|
'SUCCESS',
|
|
'CLOSED',
|
|
'REFUNDING',
|
|
'REFUNDED'
|
|
])
|
|
|
|
export const normalizeVipPaymentOrder = (value, expectedPaymentMethod) => {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
throw createRequestError('VIP 支付订单响应无效', 'VIP_RESPONSE_INVALID')
|
|
}
|
|
const paymentMethod = normalizeVipText(value.paymentMethod, 'paymentMethod', { required: true })
|
|
if (!vipPaymentMethods.has(paymentMethod) || paymentMethod !== expectedPaymentMethod) {
|
|
throw createRequestError('VIP 支付订单方式与请求不匹配', 'VIP_RESPONSE_INVALID')
|
|
}
|
|
const transactionId = normalizeVipId(value.transactionId, 'transactionId')
|
|
if (paymentMethod === VIP_PAYMENT_METHOD.WECHAT) {
|
|
if (value.tradeType !== 'APP') {
|
|
throw createRequestError('VIP 微信支付订单 tradeType 无效', 'VIP_RESPONSE_INVALID')
|
|
}
|
|
return {
|
|
paymentMethod,
|
|
transactionId,
|
|
nativePaymentRequired: true,
|
|
orderInfo: {
|
|
appid: normalizeVipText(value.appId, 'appId', { required: true }),
|
|
partnerid: normalizeVipText(value.partnerId, 'partnerId', { required: true }),
|
|
prepayid: normalizeVipText(value.prepayId, 'prepayId', { required: true }),
|
|
package: normalizeVipText(value.packageValue, 'packageValue', { required: true }),
|
|
noncestr: normalizeVipText(value.nonceStr, 'nonceStr', { required: true }),
|
|
timestamp: normalizeVipText(value.timestamp, 'timestamp', { required: true }),
|
|
sign: normalizeVipText(value.sign, 'sign', { required: true })
|
|
}
|
|
}
|
|
}
|
|
if (paymentMethod === VIP_PAYMENT_METHOD.ALIPAY) {
|
|
return {
|
|
paymentMethod,
|
|
transactionId,
|
|
nativePaymentRequired: true,
|
|
orderInfo: normalizeVipText(value.orderString, 'orderString', { required: true })
|
|
}
|
|
}
|
|
if (value.completed !== true) {
|
|
throw createRequestError('VIP 余额支付没有原子完成', 'VIP_RESPONSE_INVALID')
|
|
}
|
|
return {
|
|
paymentMethod,
|
|
transactionId: normalizeVipId(value.transactionId, 'transactionId'),
|
|
nativePaymentRequired: false,
|
|
orderInfo: null,
|
|
paymentStatus: 'SUCCESS'
|
|
}
|
|
}
|
|
|
|
export const normalizeVipPaymentStatus = (value) => {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
throw createRequestError('VIP 支付状态响应无效', 'VIP_RESPONSE_INVALID')
|
|
}
|
|
const status = normalizeVipText(value.status, 'status', { required: true })
|
|
if (!vipPaymentStatuses.has(status)) {
|
|
throw createRequestError('VIP 支付状态超出约定范围', 'VIP_RESPONSE_INVALID')
|
|
}
|
|
return {
|
|
transactionId: normalizeVipId(value.transactionId, 'transactionId'),
|
|
status
|
|
}
|
|
}
|