Files
sub2apipay/src/lib/sub2api/client.ts
erio d5719bf213 feat: migrate payment provider to easy-pay, add order history and refund support
- Replace zpay with easy-pay payment provider (new lib/easy-pay/ module)
- Add order history page for users (pay/orders)
- Add GET /api/orders/my endpoint to list user's own orders
- Add GET /api/users/[id] endpoint for sub2api user lookup
- Add order status tracking module (lib/order/status.ts)
- Update config to support easy-pay credentials (merchant ID, key, gateway)
- Update PaymentForm and PaymentQRCode components for easy-pay flow
- Update pay page and admin page with new order management UI
- Update order service to support easy-pay, cancellation, and refund
2026-03-01 03:04:24 +08:00

103 lines
2.6 KiB
TypeScript

import { getEnv } from '@/lib/config';
import type { Sub2ApiUser, Sub2ApiRedeemCode } from './types';
function getHeaders(idempotencyKey?: string): Record<string, string> {
const env = getEnv();
const headers: Record<string, string> = {
'Content-Type': 'application/json',
'x-api-key': env.SUB2API_ADMIN_API_KEY,
};
if (idempotencyKey) {
headers['Idempotency-Key'] = idempotencyKey;
}
return headers;
}
export async function getCurrentUserByToken(token: string): Promise<Sub2ApiUser> {
const env = getEnv();
const response = await fetch(`${env.SUB2API_BASE_URL}/api/v1/auth/me`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (!response.ok) {
throw new Error(`Failed to get current user: ${response.status}`);
}
const data = await response.json();
return data.data as Sub2ApiUser;
}
export async function getUser(userId: number): Promise<Sub2ApiUser> {
const env = getEnv();
const response = await fetch(`${env.SUB2API_BASE_URL}/api/v1/admin/users/${userId}`, {
headers: getHeaders(),
});
if (!response.ok) {
if (response.status === 404) throw new Error('USER_NOT_FOUND');
throw new Error(`Failed to get user: ${response.status}`);
}
const data = await response.json();
return data.data as Sub2ApiUser;
}
export async function createAndRedeem(
code: string,
value: number,
userId: number,
notes: string,
): Promise<Sub2ApiRedeemCode> {
const env = getEnv();
const response = await fetch(
`${env.SUB2API_BASE_URL}/api/v1/admin/redeem-codes/create-and-redeem`,
{
method: 'POST',
headers: getHeaders(`sub2apipay:recharge:${code}`),
body: JSON.stringify({
code,
type: 'balance',
value,
user_id: userId,
notes,
}),
},
);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(`Recharge failed (${response.status}): ${JSON.stringify(errorData)}`);
}
const data = await response.json();
return data.redeem_code as Sub2ApiRedeemCode;
}
export async function subtractBalance(
userId: number,
amount: number,
notes: string,
idempotencyKey: string,
): Promise<void> {
const env = getEnv();
const response = await fetch(
`${env.SUB2API_BASE_URL}/api/v1/admin/users/${userId}/balance`,
{
method: 'POST',
headers: getHeaders(idempotencyKey),
body: JSON.stringify({
operation: 'subtract',
amount,
notes,
}),
},
);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(`Subtract balance failed (${response.status}): ${JSON.stringify(errorData)}`);
}
}