压缩历史至 95081aff

This commit is contained in:
ryan
2026-06-08 20:34:27 +08:00
commit 8a782525de
435 changed files with 71146 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
/**
* 商户服务模块
*
* @description
* 提供商户相关的功能,包括:
* - API Key 管理(创建、查询、更新、删除)
* - 支付链接管理(创建、列表、删除)
* - 商户订单查询和支付
* - 商户订单退款
*
* @example
* ```typescript
* import { MerchantService } from '@/lib/services';
*
* // 创建 API Key
* const apiKey = await MerchantService.createAPIKey({
* app_name: '我的应用',
* app_homepage_url: 'https://example.com',
* redirect_uri: 'https://example.com/callback',
* notify_url: 'https://example.com/notify'
* });
*
* // 获取支付链接
* const links = await MerchantService.listPaymentLinks(apiKey.id);
* ```
*/
export { MerchantService } from './merchant.service';
export type {
MerchantAPIKey,
CreateAPIKeyRequest,
UpdateAPIKeyRequest,
PayMerchantOrderRequest,
GetMerchantOrderRequest,
GetMerchantOrderResponse,
PaymentLink,
CreatePaymentLinkRequest,
PayByLinkRequest,
GetPaymentLinkInfoResponse,
QueryMerchantOrderRequest,
QueryMerchantOrderResponse,
RefundMerchantOrderRequest,
RefundMerchantOrderResponse,
} from './types';
@@ -0,0 +1,491 @@
import { AxiosHeaders, type InternalAxiosRequestConfig } from 'axios';
import { BaseService } from '../core/base.service';
import { encodeBase64 } from '../../utils';
import type {
MerchantAPIKey,
CreateAPIKeyRequest,
UpdateAPIKeyRequest,
PayMerchantOrderRequest,
GetMerchantOrderRequest,
GetMerchantOrderResponse,
PaymentLink,
CreatePaymentLinkRequest,
UpdatePaymentLinkRequest,
PayByLinkRequest,
QueryMerchantOrderRequest,
QueryMerchantOrderResponse,
RefundMerchantOrderRequest,
RefundMerchantOrderResponse,
MerchantDistributeRequest,
MerchantDistributeResponse,
} from './types';
/**
* 商户服务
* 处理商户 API Key 管理和支付订单相关的 API 请求
*/
export class MerchantService extends BaseService {
protected static readonly basePath = '/api/v1/merchant';
// ==================== API Key 管理 ====================
/**
* 创建商户 API Key
* @param request - 创建 API Key 的请求参数
* @returns 创建的 API Key 信息(包含 client_secret)
* @throws {UnauthorizedError} 当未登录时
* @throws {ValidationError} 当参数验证失败时
*
* @example
* ```typescript
* const apiKey = await MerchantService.createAPIKey({
* app_name: '我的应用',
* app_homepage_url: 'https://example.com',
* app_description: '应用描述',
* redirect_uri: 'https://example.com/callback'
* });
* console.log('Client ID:', apiKey.client_id);
* console.log('Client Secret:', apiKey.client_secret); // 请保存,之后无法再次获取
* ```
*/
static async createAPIKey(request: CreateAPIKeyRequest): Promise<MerchantAPIKey> {
return this.post<MerchantAPIKey>('/api-keys', request);
}
/**
* 获取商户 API Key 列表
* @returns API Key 列表
* @throws {UnauthorizedError} 当未登录时
*
* @example
* ```typescript
* const apiKeys = await MerchantService.listAPIKeys();
* console.log('API Keys 数量:', apiKeys.length);
* ```
*/
static async listAPIKeys(): Promise<MerchantAPIKey[]> {
return this.get<MerchantAPIKey[]>('/api-keys');
}
/**
* 获取单个商户 API Key
* @param id - API Key ID
* @returns API Key 详细信息
* @throws {UnauthorizedError} 当未登录时
* @throws {NotFoundError} 当 API Key 不存在时
* @throws {ForbiddenError} 当无权访问该 API Key 时
*
* @example
* ```typescript
* const apiKey = await MerchantService.getAPIKey(123);
* console.log('应用名称:', apiKey.app_name);
* ```
*/
static async getAPIKey(id: string): Promise<MerchantAPIKey> {
return this.get<MerchantAPIKey>(`/api-keys/${ id }`);
}
/**
* 更新商户 API Key
* @param id - API Key ID
* @param request - 更新 API Key 的请求参数
* @returns void
* @throws {UnauthorizedError} 当未登录时
* @throws {NotFoundError} 当 API Key 不存在时
* @throws {ForbiddenError} 当无权访问该 API Key 时
* @throws {ValidationError} 当参数验证失败时
*
* @example
* ```typescript
* await MerchantService.updateAPIKey(123, {
* app_name: '新的应用名称',
* app_description: '新的描述'
* });
* ```
*/
static async updateAPIKey(
id: string,
request: UpdateAPIKeyRequest,
): Promise<void> {
return this.put<void>(`/api-keys/${ id }`, request);
}
/**
* 删除商户 API Key
* @param id - API Key ID
* @returns void
* @throws {UnauthorizedError} 当未登录时
* @throws {NotFoundError} 当 API Key 不存在时
* @throws {ForbiddenError} 当无权访问该 API Key 时
*
* @example
* ```typescript
* await MerchantService.deleteAPIKey(123);
* ```
*/
static async deleteAPIKey(id: string): Promise<void> {
return this.delete<void>(`/api-keys/${ id }`);
}
// ==================== 支付链接管理 ====================
/**
* 创建支付链接
* @param apiKeyId - API Key ID
* @param request - 创建支付链接请求参数
* @returns 创建的支付链接信息
* @throws {UnauthorizedError} 当未登录时
* @throws {NotFoundError} 当 API Key 不存在时
* @throws {ForbiddenError} 当无权访问该 API Key 时
*
* @example
* ```typescript
* const link = await MerchantService.createPaymentLink(123, {
* product_name: '测试商品',
* amount: 100,
* remark: '备注信息'
* });
* console.log('支付链接 Token:', link.token);
* ```
*/
static async createPaymentLink(apiKeyId: string, request: CreatePaymentLinkRequest): Promise<PaymentLink> {
return this.post<PaymentLink>(`/api-keys/${ apiKeyId }/payment-links`, request);
}
/**
* 获取支付链接列表
* @param apiKeyId - API Key ID
* @returns 支付链接列表
* @throws {UnauthorizedError} 当未登录时
* @throws {NotFoundError} 当 API Key 不存在时
* @throws {ForbiddenError} 当无权访问该 API Key 时
*
* @example
* ```typescript
* const links = await MerchantService.listPaymentLinks(123);
* console.log('支付链接数量:', links.length);
* ```
*/
static async listPaymentLinks(apiKeyId: string): Promise<PaymentLink[]> {
return this.get<PaymentLink[]>(`/api-keys/${ apiKeyId }/payment-links`);
}
/**
* 删除支付链接
* @param apiKeyId - API Key ID
* @param linkId - 支付链接 ID
* @returns void
* @throws {UnauthorizedError} 当未登录时
* @throws {NotFoundError} 当 API Key 或支付链接不存在时
* @throws {ForbiddenError} 当无权访问该 API Key 时
*
* @example
* ```typescript
* await MerchantService.deletePaymentLink(123, 456);
* ```
*/
static async deletePaymentLink(apiKeyId: string, linkId: string): Promise<void> {
return this.delete<void>(`/api-keys/${ apiKeyId }/payment-links/${ linkId }`);
}
/**
* 更新支付链接
* @param apiKeyId - API Key ID
* @param linkId - 支付链接 ID
* @param request - 更新支付链接请求参数
* @returns void
* @throws {UnauthorizedError} 当未登录时
* @throws {NotFoundError} 当 API Key 或支付链接不存在时
* @throws {ForbiddenError} 当无权访问该 API Key 时
* @throws {ValidationError} 当参数验证失败时
*
* @example
* ```typescript
* await MerchantService.updatePaymentLink(123, 456, {
* product_name: '更新后的商品',
* amount: 200,
* remark: '新的备注',
* total_limit: 100,
* user_limit: 1
* });
* ```
*/
static async updatePaymentLink(
apiKeyId: string,
linkId: string,
request: UpdatePaymentLinkRequest
): Promise<void> {
return this.put<void>(`/api-keys/${ apiKeyId }/payment-links/${ linkId }`, request);
}
/**
* 通过 Token 获取支付链接信息
*
* @description
* 公开接口,用于支付页面获取支付链接详情。
* 无需登录即可访问。
*
* @param token - 支付链接 Token
* @returns 支付链接信息(包含商品名称、金额等)
* @throws {NotFoundError} 当支付链接不存在时
*
* @example
* ```typescript
* const linkInfo = await MerchantService.getPaymentLinkByToken('abc123');
* console.log('商品名称:', linkInfo.payment_link.product_name);
* console.log('金额:', linkInfo.payment_link.amount);
* ```
*/
static async getPaymentLinkByToken(token: string): Promise<PaymentLink> {
return this.get<PaymentLink>(`/payment-links/${ token }`);
}
/**
* 通过支付链接支付
*
* @description
* 用户使用此接口通过支付链接进行支付。
* 需要用户登录,并且用户余额充足。
*
* @param request - 支付请求参数(token 和 pay_key)
* @returns void
* @throws {UnauthorizedError} 当用户未登录时
* @throws {NotFoundError} 当支付链接不存在时
* @throws {ApiErrorBase} 当余额不足、支付密码错误等业务错误时
*
* @example
* ```typescript
* try {
* await MerchantService.payByLink({
* token: 'abc123',
* pay_key: '123456',
* remark: '备注信息'
* });
* console.log('支付成功');
* } catch (error) {
* console.error('支付失败:', error.message);
* }
* ```
*
* @remarks
* - 用户不能支付自己创建的支付链接
* - 用户余额必须充足
* - 支付成功后会扣除手续费(根据商户的支付等级)
*/
static async payByLink(request: PayByLinkRequest): Promise<void> {
return this.post<void>('/payment-links/pay', request);
}
// ==================== 商户支付订单 ====================
/**
* 查询商户订单信息
*
* @description
* 查询商户创建的订单详细信息,用于支付页面显示订单信息。
* 订单必须处于待支付状态。
*
* @param request - 查询订单请求参数
* @returns 订单信息和用户积分配置
* @throws {UnauthorizedError} 当用户未登录时
* @throws {NotFoundError} 当订单不存在时
* @throws {ValidationError} 当订单号格式错误时
* @throws {ApiErrorBase} 当订单已过期或已支付等业务错误时
*
* @example
* ```typescript
* // 从 URL 获取订单号
* const params = new URLSearchParams(window.location.search);
* const orderNo = params.get('order_no');
*
* if (orderNo) {
* try {
* const orderInfo = await MerchantService.getMerchantOrder({ order_no: orderNo });
* console.log('订单信息:', orderInfo.order);
* console.log('积分配置:', orderInfo.user_pay_config);
* } catch (error) {
* console.error('查询订单失败:', error.message);
* }
* }
* ```
*
* @remarks
* - 订单必须处于待支付状态
* - 返回的用户积分配置包含手续费率等信息
*/
static async getMerchantOrder(request: GetMerchantOrderRequest): Promise<GetMerchantOrderResponse> {
return this.get<GetMerchantOrderResponse>('/payment/order', { order_no: request.order_no });
}
/**
* 支付商户订单
*
* @description
* 用户使用此接口支付商户创建的订单。
* 需要用户登录,并且用户余额充足。
*
* @param request - 支付订单请求参数
* @returns void
* @throws {UnauthorizedError} 当用户未登录时
* @throws {NotFoundError} 当订单不存在或已过期时
* @throws {ValidationError} 当订单号格式错误时
* @throws {ApiErrorBase} 当余额不足、订单已支付等业务错误时
*
* @example
* ```typescript
* // 从 URL 获取订单号
* const params = new URLSearchParams(window.location.search);
* const orderNo = params.get('order_no');
*
* if (orderNo) {
* try {
* await MerchantService.payMerchantOrder({
* order_no: orderNo,
* pay_key: '123456' // 用户的支付密码
* });
* // 支付成功
* console.log('支付成功');
* } catch (error) {
* console.error('支付失败:', error.message);
* }
* }
* ```
*
* @remarks
* - 用户不能支付自己作为商户创建的订单
* - 订单必须在有效期内(5分钟)
* - 用户余额必须充足
* - 支付成功后会扣除手续费(根据用户的积分等级)
*/
static async payMerchantOrder(request: PayMerchantOrderRequest): Promise<void> {
return this.post<void>('/payment', request);
}
/**
* 商户查询订单状态
*
* @description
* 商户使用此接口主动查询订单的支付状态。
* 需要提供商户凭证(Client ID 和 Client Secret)。
*
* @param params - 查询参数
* @returns 订单状态信息
* @throws {ValidationError} 当参数验证失败时
* @throws {ApiErrorBase} 当商户凭证无效或订单不存在时
*
* @example
* ```typescript
* const orderStatus = await MerchantService.queryMerchantOrder({
* trade_no: 12345,
* pid: 'your_client_id',
* key: 'your_client_secret'
* });
*
* if (orderStatus.status === 1) {
* console.log('订单已支付');
* } else {
* console.log('订单未支付');
* }
* ```
*
* @remarks
* - 使用 GET 请求调用 `/api.php` 接口
* - 返回的 status 字段:1 表示已支付,0 表示未支付
*/
static async queryMerchantOrder(
params: QueryMerchantOrderRequest
): Promise<QueryMerchantOrderResponse> {
return this.rawGet<QueryMerchantOrderResponse>('/epay/api.php', params);
}
/**
* 商户退款
*
* @description
* 商户使用此接口对已支付的订单进行退款。
* 需要提供商户凭证(Client ID 和 Client Secret)。
* 退款金额必须与订单金额一致。
*
* @param params - 退款请求参数
* @returns 退款结果
* @throws {ValidationError} 当参数验证失败时
* @throws {ApiErrorBase} 当商户凭证无效、订单不存在或退款失败时
*
* @example
* ```typescript
* const result = await MerchantService.refundMerchantOrder({
* trade_no: 12345,
* money: 99.99,
* pid: 'your_client_id',
* key: 'your_client_secret'
* });
*
* if (result.code === 1) {
* console.log('退款成功');
* } else {
* console.error('退款失败:', result.msg);
* }
* ```
*
* @remarks
* - 使用 POST 请求调用 `/api.php` 接口
* - 退款金额必须与订单金额完全一致
* - 只能对状态为"成功"的订单进行退款
* - 返回的 code 字段:1 表示成功,-1 表示失败
*/
static async refundMerchantOrder(
params: RefundMerchantOrderRequest
): Promise<RefundMerchantOrderResponse> {
return this.rawPost<RefundMerchantOrderResponse>('/epay/api.php', params);
}
// ==================== 商户分发 ====================
/**
* 商户向用户分发积分
*
* @description
* 商户使用此接口向指定用户分发积分。
* 需要商户认证(Basic Auth),分发金额会从商户余额中扣除,
* 收款人获得扣除分发费率后的金额。
*
* @param request - 分发请求参数
* @returns 分发结果(包含订单号)
* @throws {ValidationError} 当参数验证失败时
* @throws {ApiErrorBase} 当余额不足、用户不存在等业务错误时
*
* @example
* ```typescript
* const result = await MerchantService.distribute({
* user_id: 123,
* username: 'alice',
* amount: 100,
* out_trade_no: 'DIST20251231001',
* remark: '新年奖励'
* });
* console.log('订单号:', result.trade_no);
* ```
*
* @remarks
* - 使用 POST 请求调用 `/pay/distribute` 接口(前端通过 `/lpay/distribute` 代理)
* - 需要通过 Basic Auth 提供商户凭证
* - 不能分发给商户自己
* - 商户余额必须充足
* - 分发会扣除分发费率(根据商户的支付等级)
*/
static async distribute(
request: MerchantDistributeRequest,
auth?: { client_id: string; client_secret: string }
): Promise<MerchantDistributeResponse> {
const config: InternalAxiosRequestConfig | undefined = auth
? {
headers: new AxiosHeaders({
Authorization: `Basic ${encodeBase64(`${auth.client_id}:${auth.client_secret}`)}`,
}),
}
: undefined;
return this.rawPost<MerchantDistributeResponse>('/lpay/distribute', request, config);
}
}
+362
View File
@@ -0,0 +1,362 @@
/**
* 商户 API Key 信息
*/
export interface MerchantAPIKey {
/** API Key ID */
id: string;
/** 用户 ID */
user_id: string;
/** 客户端 ID */
client_id: string;
/** 客户端密钥 */
client_secret: string;
/** 应用名称 */
app_name: string;
/** 应用主页 URL */
app_homepage_url: string;
/** 应用描述 */
app_description: string;
/** 重定向 URI */
redirect_uri?: string;
/** 通知 URL */
notify_url: string;
/** 公钥 (Base64) */
public_key?: string;
/** 测试模式 */
test_mode: boolean;
/** 创建时间 */
created_at: string;
/** 更新时间 */
updated_at: string;
/** 删除时间(软删除) */
deleted_at: string | null;
}
/**
* 创建商户 API Key 请求参数
*/
export interface CreateAPIKeyRequest {
/** 应用名称(最大20字符) */
app_name: string;
/** 应用主页 URL(最大100字符,必须是有效的 URL) */
app_homepage_url: string;
/** 应用描述(最大100字符,可选) */
app_description?: string;
/** 重定向 URI(最大100字符,必须是有效的 URL,可选) */
redirect_uri?: string;
/** 通知 URL(最大100字符,必须是有效的 URL) */
notify_url: string;
/** 公钥 (Base64 编码,32字节,可选) */
public_key?: string;
/** 测试模式(可选,默认为 false) */
test_mode?: boolean;
}
/**
* 更新商户 API Key 请求参数
*/
export interface UpdateAPIKeyRequest {
/** 应用名称(最大20字符,可选) */
app_name?: string;
/** 应用主页 URL(最大100字符,必须是有效的 URL,可选) */
app_homepage_url?: string;
/** 应用描述(最大100字符,可选) */
app_description?: string;
/** 重定向 URI(最大100字符,必须是有效的 URL,可选) */
redirect_uri?: string;
/** 通知 URL(最大100字符,必须是有效的 URL,可选) */
notify_url?: string;
/** 公钥 (Base64 编码,32字节,可选) */
public_key?: string;
/** 测试模式(可选) */
test_mode?: boolean;
}
/**
* 支付商户订单请求参数
*/
export interface PayMerchantOrderRequest {
/** 订单号(加密后的订单ID) */
order_no: string;
/** 支付密码(6位数字) */
pay_key: string;
}
/**
* 查询商户订单请求参数
*/
export interface GetMerchantOrderRequest {
/** 订单号(加密后的订单ID) */
order_no: string;
}
/**
* 查询商户订单响应
*/
export interface GetMerchantOrderResponse {
/** 订单信息 */
order: {
/** 订单ID */
id: string;
/** 订单号 */
order_no: string;
/** 订单名称 */
order_name: string;
/** 付款方账户 */
payer_username: string;
/** 收款方账户 */
payee_username: string;
/** 交易金额 */
amount: string;
/** 订单状态 */
status: string;
/** 订单类型 */
type: string;
/** 支付类型 */
payment_type: string;
/** 备注 */
remark: string;
/** 客户端ID */
client_id: string;
/** 同步跳转URL */
return_url: string;
/** 异步通知URL */
notify_url: string;
/** 交易时间 */
trade_time: string | null;
/** 创建时间 */
created_at: string;
/** 更新时间 */
updated_at: string;
};
/** 用户积分配置信息 */
user_pay_config: {
/** 配置ID */
id: string;
/** 积分等级 */
level: number;
/** 最低分数 */
min_score: number;
/** 最高分数 */
max_score: number | null;
/** 每日限额 */
daily_limit: number | null;
/** 手续费率 */
fee_rate: string;
/** 创建时间 */
created_at: string;
/** 更新时间 */
updated_at: string;
};
/** 商户信息 */
merchant: {
/** 应用名称 */
app_name: string;
/** 跳转URI */
redirect_uri: string;
};
}
/**
* 支付链接信息
*/
export interface PaymentLink {
/** 链接 ID */
id: string;
/** 商户 API Key ID */
merchant_api_key_id: string;
/** 支付链接 Token */
token: string;
/** 金额 */
amount: string;
/** 商品名称 */
product_name: string;
/** 备注 */
remark: string;
/** 总支付次数限制(可选) */
total_limit?: number;
/** 单用户支付次数限制(可选) */
user_limit?: number;
/** 创建时间 */
created_at: string;
/** 更新时间 */
updated_at: string;
/** 应用名称 */
app_name: string;
/** 重定向 URI */
redirect_uri: string;
}
/**
* 创建支付链接请求参数
*/
export interface CreatePaymentLinkRequest {
/** 金额 */
amount: number | string;
/** 商品名称 */
product_name: string;
/** 备注(可选) */
remark?: string;
/** 总支付次数限制(可选,最小值为1) */
total_limit?: number;
/** 单用户支付次数限制(可选,最小值为1) */
user_limit?: number;
}
/**
* 更新支付链接请求参数
*/
export interface UpdatePaymentLinkRequest {
/** 金额 */
amount: number | string;
/** 商品名称 */
product_name: string;
/** 备注(可选) */
remark?: string;
/** 总支付次数限制(可选,最小值为1) */
total_limit?: number;
/** 单用户支付次数限制(可选,最小值为1) */
user_limit?: number;
}
/**
* 通过支付链接支付请求参数
*/
export interface PayByLinkRequest {
/** 支付链接 Token */
token: string;
/** 支付密码(6位数字) */
pay_key: string;
/** 备注(可选,最大100字符) */
remark?: string;
}
/**
* 获取支付链接信息响应
* 包含支付链接信息和商户信息
*/
export interface GetPaymentLinkInfoResponse {
/** 支付链接信息 */
payment_link: PaymentLink;
/** 商户信息 */
merchant: {
/** 应用名称 */
app_name: string;
/** 跳转URI */
redirect_uri: string;
};
/** 用户积分配置信息 */
user_pay_config: {
/** 配置ID */
id: string;
/** 积分等级 */
level: number;
/** 最低分数 */
min_score: number;
/** 最高分数 */
max_score: number | null;
/** 每日限额 */
daily_limit: number | null;
/** 手续费率 */
fee_rate: string;
/** 创建时间 */
created_at: string;
/** 更新时间 */
updated_at: string;
};
}
/**
* 商户查询订单请求参数
*/
export interface QueryMerchantOrderRequest {
/** 商户订单号(可选) */
out_trade_no?: string;
/** 平台订单号 */
trade_no: string;
/** 客户端 ID */
pid: string;
/** 客户端密钥 */
key: string;
}
/**
* 商户查询订单响应
*/
export interface QueryMerchantOrderResponse {
/** 状态码(1表示成功,-1表示失败) */
code: number;
/** 响应消息 */
msg: string;
/** 平台订单号 */
trade_no: string;
/** 商户订单号 */
out_trade_no: string;
/** 支付类型 */
type: string;
/** 客户端 ID */
pid: string;
/** 订单创建时间 */
addtime: string;
/** 订单完成时间 */
endtime: string;
/** 订单名称 */
name: string;
/** 订单金额 */
money: string;
/** 订单状态(1表示已支付,0表示未支付) */
status: number;
}
/**
* 商户退款请求参数
*/
export interface RefundMerchantOrderRequest {
/** 客户端 ID */
pid: string;
/** 客户端密钥 */
key: string;
/** 商户订单号(可选) */
out_trade_no?: string;
/** 平台订单号 */
trade_no: string;
/** 退款金额 */
money: number | string;
}
/**
* 商户退款响应
*/
export interface RefundMerchantOrderResponse {
/** 状态码(1表示成功,-1表示失败) */
code: number;
/** 响应消息 */
msg: string;
}
/**
* 商户分发请求参数
*/
export interface MerchantDistributeRequest {
/** 接收用户 ID (必填) */
user_id: number;
/** 接收用户名,用于验证 (必填) */
username: string;
/** 分发金额 (必填) */
amount: number | string;
/** 商户订单号 (可选,最大64字符) */
out_trade_no?: string;
/** 备注 (可选,最大100字符) */
remark?: string;
}
/**
* 商户分发响应
*/
export interface MerchantDistributeResponse {
/** 平台订单号 */
trade_no: string;
/** 商户订单号 */
out_trade_no: string;
}