feat(finance): edit app USDT receive addresses
This commit is contained in:
parent
ee900d0127
commit
9045de6ad2
@ -4593,6 +4593,40 @@
|
|||||||
"x-permissions": ["finance-order:coin-seller-recharge:view"]
|
"x-permissions": ["finance-order:coin-seller-recharge:view"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/admin/finance/usdt-addresses/{app_code}/{chain}": {
|
||||||
|
"put": {
|
||||||
|
"operationId": "upsertFinanceUSDTAddress",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "app_code",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "chain",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["TRON", "BSC"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"$ref": "#/components/requestBodies/FinanceUSDTAddressRequest"
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"$ref": "#/components/responses/FinanceUSDTAddressResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-permission": "finance-order:usdt-address:update",
|
||||||
|
"x-permissions": ["finance-order:usdt-address:update"]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/admin/finance/coin-seller-recharge-exchange-rates/{app_code}": {
|
"/admin/finance/coin-seller-recharge-exchange-rates/{app_code}": {
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "getFinanceCoinSellerRechargeExchangeRate",
|
"operationId": "getFinanceCoinSellerRechargeExchangeRate",
|
||||||
@ -8229,6 +8263,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"FinanceUSDTAddressRequest": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/FinanceUSDTAddressInput"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"FinanceWithdrawalAuditRequest": {
|
"FinanceWithdrawalAuditRequest": {
|
||||||
"content": {
|
"content": {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
@ -8777,6 +8820,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"FinanceUSDTAddressResponse": {
|
||||||
|
"description": "OK",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ApiResponseFinanceUSDTAddressUpsert"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"FinanceCoinSellerRechargeReceiptVerificationResponse": {
|
"FinanceCoinSellerRechargeReceiptVerificationResponse": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"content": {
|
"content": {
|
||||||
@ -11019,6 +11072,21 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"ApiResponseFinanceUSDTAddressUpsert": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Envelope"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/components/schemas/FinanceUSDTAddressUpsert"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"ApiResponseFinanceCoinSellerRechargeReceiptVerification": {
|
"ApiResponseFinanceCoinSellerRechargeReceiptVerification": {
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
@ -13137,7 +13205,7 @@
|
|||||||
},
|
},
|
||||||
"chain": {
|
"chain": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["TRON", "BSC"]
|
"enum": ["C2C", "OFFCHAIN", "TRON", "BSC"]
|
||||||
},
|
},
|
||||||
"externalOrderNo": {
|
"externalOrderNo": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -13335,7 +13403,7 @@
|
|||||||
},
|
},
|
||||||
"chain": {
|
"chain": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["TRON", "BSC"]
|
"enum": ["C2C", "OFFCHAIN", "TRON", "BSC"]
|
||||||
},
|
},
|
||||||
"remark": {
|
"remark": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -13479,6 +13547,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"FinanceUSDTAddressInput": {
|
||||||
|
"type": "object",
|
||||||
|
"required": ["address"],
|
||||||
|
"properties": {
|
||||||
|
"address": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"FinanceUSDTAddressUpsert": {
|
||||||
|
"type": "object",
|
||||||
|
"required": ["appCode", "chain", "address", "updatedAtMs"],
|
||||||
|
"properties": {
|
||||||
|
"appCode": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"chain": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["TRON", "BSC"]
|
||||||
|
},
|
||||||
|
"address": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"updatedAtMs": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"FinanceCoinSellerRechargeExchangeRateInput": {
|
"FinanceCoinSellerRechargeExchangeRateInput": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": ["tiers", "whitelist"],
|
"required": ["tiers", "whitelist"],
|
||||||
@ -13550,7 +13647,7 @@
|
|||||||
},
|
},
|
||||||
"chain": {
|
"chain": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["TRON", "BSC"]
|
"enum": ["C2C", "OFFCHAIN", "TRON", "BSC"]
|
||||||
},
|
},
|
||||||
"receiveAddress": {
|
"receiveAddress": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -13582,7 +13679,7 @@
|
|||||||
},
|
},
|
||||||
"chain": {
|
"chain": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["TRON", "BSC"]
|
"enum": ["C2C", "OFFCHAIN", "TRON", "BSC"]
|
||||||
},
|
},
|
||||||
"usdAmount": {
|
"usdAmount": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import {
|
|||||||
replaceFinanceCoinSellerRechargeExchangeRate,
|
replaceFinanceCoinSellerRechargeExchangeRate,
|
||||||
rejectFinanceWithdrawalApplication,
|
rejectFinanceWithdrawalApplication,
|
||||||
quoteFinanceCoinSellerRechargeOrder,
|
quoteFinanceCoinSellerRechargeOrder,
|
||||||
|
upsertFinanceUSDTAddress,
|
||||||
verifyFinanceCoinSellerRechargeReceipt,
|
verifyFinanceCoinSellerRechargeReceipt,
|
||||||
verifyFinanceCoinSellerRechargeOrder,
|
verifyFinanceCoinSellerRechargeOrder,
|
||||||
} from "./api.js";
|
} from "./api.js";
|
||||||
@ -517,6 +518,18 @@ export function FinanceApp() {
|
|||||||
|
|
||||||
const loadUSDTAddresses = useCallback((appCode) => getFinanceUSDTAddresses(appCode), []);
|
const loadUSDTAddresses = useCallback((appCode) => getFinanceUSDTAddresses(appCode), []);
|
||||||
|
|
||||||
|
const saveUSDTAddress = useCallback(
|
||||||
|
async (appCode, chain, payload) => {
|
||||||
|
if (!session?.canUpdateUSDTAddress) {
|
||||||
|
throw new Error("无修改 USDT 收款地址权限");
|
||||||
|
}
|
||||||
|
const saved = await upsertFinanceUSDTAddress(appCode, chain, cleanPayload(payload));
|
||||||
|
showToast(`${chain} 收款地址已保存`, "success");
|
||||||
|
return saved;
|
||||||
|
},
|
||||||
|
[session?.canUpdateUSDTAddress, showToast],
|
||||||
|
);
|
||||||
|
|
||||||
const saveCoinSellerRechargeExchangeRate = useCallback(
|
const saveCoinSellerRechargeExchangeRate = useCallback(
|
||||||
async (appCode, payload) => {
|
async (appCode, payload) => {
|
||||||
const saved = await replaceFinanceCoinSellerRechargeExchangeRate(appCode, cleanPayload(payload));
|
const saved = await replaceFinanceCoinSellerRechargeExchangeRate(appCode, cleanPayload(payload));
|
||||||
@ -723,6 +736,7 @@ export function FinanceApp() {
|
|||||||
canCreate={session.canCreateCoinSellerRechargeOrder}
|
canCreate={session.canCreateCoinSellerRechargeOrder}
|
||||||
canConfigureExchangeRate={session.canConfigureCoinSellerRechargeExchangeRate}
|
canConfigureExchangeRate={session.canConfigureCoinSellerRechargeExchangeRate}
|
||||||
canGrant={session.canGrantCoinSellerRechargeOrder}
|
canGrant={session.canGrantCoinSellerRechargeOrder}
|
||||||
|
canUpdateUSDTAddress={session.canUpdateUSDTAddress}
|
||||||
canVerify={session.canVerifyCoinSellerRechargeOrder}
|
canVerify={session.canVerifyCoinSellerRechargeOrder}
|
||||||
error={coinSellerRechargeOrdersState.error}
|
error={coinSellerRechargeOrdersState.error}
|
||||||
filters={coinSellerRechargeOrderFilters}
|
filters={coinSellerRechargeOrderFilters}
|
||||||
@ -736,6 +750,7 @@ export function FinanceApp() {
|
|||||||
onReload={loadCoinSellerRechargeOrders}
|
onReload={loadCoinSellerRechargeOrders}
|
||||||
onQuote={quoteCoinSellerRechargeOrder}
|
onQuote={quoteCoinSellerRechargeOrder}
|
||||||
onSaveExchangeRate={saveCoinSellerRechargeExchangeRate}
|
onSaveExchangeRate={saveCoinSellerRechargeExchangeRate}
|
||||||
|
onSaveUSDTAddress={saveUSDTAddress}
|
||||||
onVerify={verifyCoinSellerRechargeOrder}
|
onVerify={verifyCoinSellerRechargeOrder}
|
||||||
onVerifyReceipt={verifyCoinSellerRechargeReceipt}
|
onVerifyReceipt={verifyCoinSellerRechargeReceipt}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -34,6 +34,7 @@ vi.mock("./api.js", () => ({
|
|||||||
rejectFinanceWithdrawalApplication: vi.fn(),
|
rejectFinanceWithdrawalApplication: vi.fn(),
|
||||||
refreshFinanceGoogleRechargePaid: vi.fn(),
|
refreshFinanceGoogleRechargePaid: vi.fn(),
|
||||||
replaceFinanceCoinSellerRechargeExchangeRate: vi.fn(),
|
replaceFinanceCoinSellerRechargeExchangeRate: vi.fn(),
|
||||||
|
upsertFinanceUSDTAddress: vi.fn(),
|
||||||
quoteFinanceCoinSellerRechargeOrder: vi.fn(),
|
quoteFinanceCoinSellerRechargeOrder: vi.fn(),
|
||||||
verifyFinanceCoinSellerRechargeOrder: vi.fn(),
|
verifyFinanceCoinSellerRechargeOrder: vi.fn(),
|
||||||
verifyFinanceCoinSellerRechargeReceipt: vi.fn()
|
verifyFinanceCoinSellerRechargeReceipt: vi.fn()
|
||||||
@ -45,6 +46,7 @@ const defaultSession = {
|
|||||||
canConfigureCoinSellerRechargeExchangeRate: false,
|
canConfigureCoinSellerRechargeExchangeRate: false,
|
||||||
canCreateCoinSellerRechargeOrder: false,
|
canCreateCoinSellerRechargeOrder: false,
|
||||||
canGrantCoinSellerRechargeOrder: false,
|
canGrantCoinSellerRechargeOrder: false,
|
||||||
|
canUpdateUSDTAddress: false,
|
||||||
canVerifyCoinSellerRechargeOrder: false,
|
canVerifyCoinSellerRechargeOrder: false,
|
||||||
canViewAppRechargeDetails: false,
|
canViewAppRechargeDetails: false,
|
||||||
canViewCoinSellerRechargeOrders: false,
|
canViewCoinSellerRechargeOrders: false,
|
||||||
|
|||||||
@ -16,6 +16,14 @@ import {
|
|||||||
|
|
||||||
const appCodeHeader = "X-App-Code";
|
const appCodeHeader = "X-App-Code";
|
||||||
|
|
||||||
|
function normalizeUSDTAddress(item = {}) {
|
||||||
|
return {
|
||||||
|
address: stringValue(item?.address),
|
||||||
|
chain: stringValue(item?.chain).toUpperCase(),
|
||||||
|
updatedAtMs: Number(item?.updatedAtMs || 0)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchFinanceSession() {
|
export async function fetchFinanceSession() {
|
||||||
const session = await currentSession();
|
const session = await currentSession();
|
||||||
const permissions = Array.isArray(session.permissions) ? session.permissions : [];
|
const permissions = Array.isArray(session.permissions) ? session.permissions : [];
|
||||||
@ -26,6 +34,7 @@ export async function fetchFinanceSession() {
|
|||||||
const canCreateCoinSellerRechargeOrder = permissions.includes(FINANCE_PERMISSIONS.createCoinSellerRechargeOrder);
|
const canCreateCoinSellerRechargeOrder = permissions.includes(FINANCE_PERMISSIONS.createCoinSellerRechargeOrder);
|
||||||
const canVerifyCoinSellerRechargeOrder = permissions.includes(FINANCE_PERMISSIONS.verifyCoinSellerRechargeOrder);
|
const canVerifyCoinSellerRechargeOrder = permissions.includes(FINANCE_PERMISSIONS.verifyCoinSellerRechargeOrder);
|
||||||
const canGrantCoinSellerRechargeOrder = permissions.includes(FINANCE_PERMISSIONS.grantCoinSellerRechargeOrder);
|
const canGrantCoinSellerRechargeOrder = permissions.includes(FINANCE_PERMISSIONS.grantCoinSellerRechargeOrder);
|
||||||
|
const canUpdateUSDTAddress = permissions.includes(FINANCE_PERMISSIONS.updateUSDTAddress);
|
||||||
const canConfigureCoinSellerRechargeExchangeRate = permissions.includes(FINANCE_PERMISSIONS.configureCoinSellerRechargeExchangeRate);
|
const canConfigureCoinSellerRechargeExchangeRate = permissions.includes(FINANCE_PERMISSIONS.configureCoinSellerRechargeExchangeRate);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -38,6 +47,7 @@ export async function fetchFinanceSession() {
|
|||||||
canConfigureCoinSellerRechargeExchangeRate,
|
canConfigureCoinSellerRechargeExchangeRate,
|
||||||
canCreateCoinSellerRechargeOrder,
|
canCreateCoinSellerRechargeOrder,
|
||||||
canGrantCoinSellerRechargeOrder,
|
canGrantCoinSellerRechargeOrder,
|
||||||
|
canUpdateUSDTAddress,
|
||||||
canVerifyCoinSellerRechargeOrder,
|
canVerifyCoinSellerRechargeOrder,
|
||||||
canViewAppRechargeDetails,
|
canViewAppRechargeDetails,
|
||||||
canViewCoinSellerRechargeOrders,
|
canViewCoinSellerRechargeOrders,
|
||||||
@ -113,15 +123,26 @@ export async function getFinanceUSDTAddresses(appCode) {
|
|||||||
return {
|
return {
|
||||||
appCode: stringValue(data?.appCode ?? appCode),
|
appCode: stringValue(data?.appCode ?? appCode),
|
||||||
items: listItems(data?.items)
|
items: listItems(data?.items)
|
||||||
.map((item) => ({
|
.map(normalizeUSDTAddress)
|
||||||
address: stringValue(item?.address),
|
|
||||||
chain: stringValue(item?.chain).toUpperCase(),
|
|
||||||
updatedAtMs: Number(item?.updatedAtMs || 0)
|
|
||||||
}))
|
|
||||||
.filter((item) => item.address && item.chain)
|
.filter((item) => item.address && item.chain)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function upsertFinanceUSDTAddress(appCode, chain, payload) {
|
||||||
|
const endpoint = API_ENDPOINTS.upsertFinanceUSDTAddress;
|
||||||
|
const data = await apiRequest(apiEndpointPath(API_OPERATIONS.upsertFinanceUSDTAddress, {
|
||||||
|
app_code: appCode,
|
||||||
|
chain
|
||||||
|
}), {
|
||||||
|
body: { address: stringValue(payload?.address) },
|
||||||
|
method: endpoint.method
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
...normalizeUSDTAddress(data),
|
||||||
|
appCode: stringValue(data?.appCode ?? appCode)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function replaceFinanceCoinSellerRechargeExchangeRate(appCode, payload) {
|
export async function replaceFinanceCoinSellerRechargeExchangeRate(appCode, payload) {
|
||||||
const endpoint = API_ENDPOINTS.replaceFinanceCoinSellerRechargeExchangeRate;
|
const endpoint = API_ENDPOINTS.replaceFinanceCoinSellerRechargeExchangeRate;
|
||||||
return apiRequest(apiEndpointPath(API_OPERATIONS.replaceFinanceCoinSellerRechargeExchangeRate, { app_code: appCode }), {
|
return apiRequest(apiEndpointPath(API_OPERATIONS.replaceFinanceCoinSellerRechargeExchangeRate, { app_code: appCode }), {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import {
|
|||||||
rejectFinanceWithdrawalApplication,
|
rejectFinanceWithdrawalApplication,
|
||||||
quoteFinanceCoinSellerRechargeOrder,
|
quoteFinanceCoinSellerRechargeOrder,
|
||||||
replaceFinanceCoinSellerRechargeExchangeRate,
|
replaceFinanceCoinSellerRechargeExchangeRate,
|
||||||
|
upsertFinanceUSDTAddress,
|
||||||
verifyFinanceCoinSellerRechargeReceipt,
|
verifyFinanceCoinSellerRechargeReceipt,
|
||||||
verifyFinanceCoinSellerRechargeOrder
|
verifyFinanceCoinSellerRechargeOrder
|
||||||
} from "./api.js";
|
} from "./api.js";
|
||||||
@ -202,6 +203,36 @@ test("finance USDT address API uses the selected app path and normalizes SQL row
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("finance USDT address update uses the selected app and chain path", async () => {
|
||||||
|
vi.stubGlobal(
|
||||||
|
"fetch",
|
||||||
|
vi.fn(async () =>
|
||||||
|
new Response(JSON.stringify({
|
||||||
|
code: 0,
|
||||||
|
data: {
|
||||||
|
address: "TLv7wERsM42ZeobMrrKVC5C72d3gFUa44J",
|
||||||
|
appCode: "aslan",
|
||||||
|
chain: "TRON",
|
||||||
|
updatedAtMs: 456
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await upsertFinanceUSDTAddress("aslan", "TRON", { address: "TLv7wERsM42ZeobMrrKVC5C72d3gFUa44J" });
|
||||||
|
const [url, init] = vi.mocked(fetch).mock.calls[0];
|
||||||
|
|
||||||
|
expect(String(url)).toContain("/api/v1/admin/finance/usdt-addresses/aslan/TRON");
|
||||||
|
expect(init?.method).toBe("PUT");
|
||||||
|
expect(JSON.parse(String(init?.body))).toEqual({ address: "TLv7wERsM42ZeobMrrKVC5C72d3gFUa44J" });
|
||||||
|
expect(result).toEqual({
|
||||||
|
address: "TLv7wERsM42ZeobMrrKVC5C72d3gFUa44J",
|
||||||
|
appCode: "aslan",
|
||||||
|
chain: "TRON",
|
||||||
|
updatedAtMs: 456
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test("finance recharge details query every app with app header and merge by recharge time", async () => {
|
test("finance recharge details query every app with app header and merge by recharge time", async () => {
|
||||||
vi.stubGlobal(
|
vi.stubGlobal(
|
||||||
"fetch",
|
"fetch",
|
||||||
|
|||||||
@ -42,6 +42,7 @@ export function FinanceCoinSellerRechargeOrderList({
|
|||||||
canCreate,
|
canCreate,
|
||||||
canConfigureExchangeRate,
|
canConfigureExchangeRate,
|
||||||
canGrant,
|
canGrant,
|
||||||
|
canUpdateUSDTAddress,
|
||||||
canVerify,
|
canVerify,
|
||||||
error,
|
error,
|
||||||
filters,
|
filters,
|
||||||
@ -54,6 +55,7 @@ export function FinanceCoinSellerRechargeOrderList({
|
|||||||
onReload,
|
onReload,
|
||||||
onQuote,
|
onQuote,
|
||||||
onSaveExchangeRate,
|
onSaveExchangeRate,
|
||||||
|
onSaveUSDTAddress,
|
||||||
onVerify,
|
onVerify,
|
||||||
onVerifyReceipt,
|
onVerifyReceipt,
|
||||||
orders,
|
orders,
|
||||||
@ -508,10 +510,12 @@ export function FinanceCoinSellerRechargeOrderList({
|
|||||||
/>
|
/>
|
||||||
<FinanceUSDTAddressDialog
|
<FinanceUSDTAddressDialog
|
||||||
apps={apps}
|
apps={apps}
|
||||||
|
canUpdate={canUpdateUSDTAddress}
|
||||||
initialAppCode={filters.appCode}
|
initialAppCode={filters.appCode}
|
||||||
open={usdtAddressOpen}
|
open={usdtAddressOpen}
|
||||||
onClose={() => setUSDTAddressOpen(false)}
|
onClose={() => setUSDTAddressOpen(false)}
|
||||||
onLoad={onLoadUSDTAddresses}
|
onLoad={onLoadUSDTAddresses}
|
||||||
|
onSave={onSaveUSDTAddress}
|
||||||
/>
|
/>
|
||||||
<FinanceCoinSellerGrantDialog
|
<FinanceCoinSellerGrantDialog
|
||||||
loading={Boolean(grantTarget && actionLoading === `grant:${grantTarget.id}`)}
|
loading={Boolean(grantTarget && actionLoading === `grant:${grantTarget.id}`)}
|
||||||
|
|||||||
@ -133,6 +133,18 @@ test("makeup order keeps zero coins while creating a normal recharge record", as
|
|||||||
expect(onQuote).not.toHaveBeenCalled();
|
expect(onQuote).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("USDT chain selector exposes Binance Off-chain as an explicit verification source", async () => {
|
||||||
|
render(<FinanceCoinSellerRechargeOrderList {...baseProps()} />);
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "创建币商充值" }));
|
||||||
|
fireEvent.mouseDown(screen.getByRole("combobox", { name: "支付渠道" }));
|
||||||
|
fireEvent.click(screen.getByRole("option", { name: "USDT" }));
|
||||||
|
fireEvent.mouseDown(screen.getByRole("combobox", { name: "USDT 链" }));
|
||||||
|
|
||||||
|
expect(screen.getByRole("option", { name: "Binance 站内转账 / Off-chain" })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole("option", { name: "Binance Pay / C2C" })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
test("coin seller recharge order actions follow verify and grant status", () => {
|
test("coin seller recharge order actions follow verify and grant status", () => {
|
||||||
const onVerify = vi.fn();
|
const onVerify = vi.fn();
|
||||||
const onGrant = vi.fn();
|
const onGrant = vi.fn();
|
||||||
@ -260,6 +272,7 @@ function baseProps(overrides = {}) {
|
|||||||
canCreate: true,
|
canCreate: true,
|
||||||
canConfigureExchangeRate: false,
|
canConfigureExchangeRate: false,
|
||||||
canGrant: true,
|
canGrant: true,
|
||||||
|
canUpdateUSDTAddress: false,
|
||||||
canVerify: true,
|
canVerify: true,
|
||||||
error: "",
|
error: "",
|
||||||
filters: {
|
filters: {
|
||||||
@ -280,6 +293,7 @@ function baseProps(overrides = {}) {
|
|||||||
onReload: vi.fn(),
|
onReload: vi.fn(),
|
||||||
onQuote: vi.fn(async () => ({ coinAmount: 800000, coinsPerUsd: 80000, rateSource: "tier" })),
|
onQuote: vi.fn(async () => ({ coinAmount: 800000, coinsPerUsd: 80000, rateSource: "tier" })),
|
||||||
onSaveExchangeRate: vi.fn(),
|
onSaveExchangeRate: vi.fn(),
|
||||||
|
onSaveUSDTAddress: vi.fn(),
|
||||||
onVerify: vi.fn(),
|
onVerify: vi.fn(),
|
||||||
onVerifyReceipt: vi.fn(),
|
onVerifyReceipt: vi.fn(),
|
||||||
orders: {
|
orders: {
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
|
import AddOutlined from "@mui/icons-material/AddOutlined";
|
||||||
import ContentCopyOutlined from "@mui/icons-material/ContentCopyOutlined";
|
import ContentCopyOutlined from "@mui/icons-material/ContentCopyOutlined";
|
||||||
|
import EditOutlined from "@mui/icons-material/EditOutlined";
|
||||||
import Alert from "@mui/material/Alert";
|
import Alert from "@mui/material/Alert";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import Dialog from "@mui/material/Dialog";
|
import Dialog from "@mui/material/Dialog";
|
||||||
@ -8,18 +10,22 @@ import DialogTitle from "@mui/material/DialogTitle";
|
|||||||
import LinearProgress from "@mui/material/LinearProgress";
|
import LinearProgress from "@mui/material/LinearProgress";
|
||||||
import MenuItem from "@mui/material/MenuItem";
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { AppIdentity } from "@/shared/ui/AppIdentity.jsx";
|
import { AppIdentity } from "@/shared/ui/AppIdentity.jsx";
|
||||||
import { formatTime } from "../format.js";
|
import { formatTime } from "../format.js";
|
||||||
|
|
||||||
const EMPTY_DATA = { appCode: "", items: [] };
|
const EMPTY_DATA = { appCode: "", items: [] };
|
||||||
|
const ADDRESS_CHAINS = ["TRON", "BSC"];
|
||||||
|
|
||||||
export function FinanceUSDTAddressDialog({ apps, initialAppCode, open, onClose, onLoad }) {
|
export function FinanceUSDTAddressDialog({ apps, canUpdate = false, initialAppCode, open, onClose, onLoad, onSave }) {
|
||||||
const [appCode, setAppCode] = useState("");
|
const [appCode, setAppCode] = useState("");
|
||||||
const [data, setData] = useState(EMPTY_DATA);
|
const [data, setData] = useState(EMPTY_DATA);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [copiedChain, setCopiedChain] = useState("");
|
const [copiedChain, setCopiedChain] = useState("");
|
||||||
|
const [editingChain, setEditingChain] = useState("");
|
||||||
|
const [addressDraft, setAddressDraft] = useState("");
|
||||||
|
const [saving, setSaving] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) {
|
if (!open) {
|
||||||
@ -27,6 +33,8 @@ export function FinanceUSDTAddressDialog({ apps, initialAppCode, open, onClose,
|
|||||||
}
|
}
|
||||||
setAppCode(initialAppCode || apps[0]?.appCode || "");
|
setAppCode(initialAppCode || apps[0]?.appCode || "");
|
||||||
setCopiedChain("");
|
setCopiedChain("");
|
||||||
|
setEditingChain("");
|
||||||
|
setAddressDraft("");
|
||||||
setError("");
|
setError("");
|
||||||
}, [apps, initialAppCode, open]);
|
}, [apps, initialAppCode, open]);
|
||||||
|
|
||||||
@ -40,6 +48,8 @@ export function FinanceUSDTAddressDialog({ apps, initialAppCode, open, onClose,
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError("");
|
setError("");
|
||||||
setCopiedChain("");
|
setCopiedChain("");
|
||||||
|
setEditingChain("");
|
||||||
|
setAddressDraft("");
|
||||||
onLoad(appCode)
|
onLoad(appCode)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
if (active) {
|
if (active) {
|
||||||
@ -62,6 +72,13 @@ export function FinanceUSDTAddressDialog({ apps, initialAppCode, open, onClose,
|
|||||||
};
|
};
|
||||||
}, [appCode, onLoad, open]);
|
}, [appCode, onLoad, open]);
|
||||||
|
|
||||||
|
const itemsByChain = useMemo(
|
||||||
|
() => new Map((data.items || []).map((item) => [item.chain, item])),
|
||||||
|
[data.items],
|
||||||
|
);
|
||||||
|
// 有写权限时始终展示 TRON/BSC 两个可维护槽位,未配置项直接提供“添加”;只读账号仍只看到真实配置。
|
||||||
|
const visibleChains = canUpdate ? ADDRESS_CHAINS : ADDRESS_CHAINS.filter((chain) => itemsByChain.has(chain));
|
||||||
|
|
||||||
const copyAddress = async (item) => {
|
const copyAddress = async (item) => {
|
||||||
try {
|
try {
|
||||||
if (!navigator.clipboard?.writeText) {
|
if (!navigator.clipboard?.writeText) {
|
||||||
@ -77,13 +94,53 @@ export function FinanceUSDTAddressDialog({ apps, initialAppCode, open, onClose,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const beginEdit = (chain) => {
|
||||||
|
setEditingChain(chain);
|
||||||
|
setAddressDraft(itemsByChain.get(chain)?.address || "");
|
||||||
|
setCopiedChain("");
|
||||||
|
setError("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveAddress = async () => {
|
||||||
|
const address = addressDraft.trim();
|
||||||
|
if (!validUSDTAddress(editingChain, address)) {
|
||||||
|
setError(`${chainLabel(editingChain)} 收款地址格式不正确`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!onSave) {
|
||||||
|
setError("收款地址保存接口未配置");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSaving(true);
|
||||||
|
setError("");
|
||||||
|
try {
|
||||||
|
const saved = await onSave(appCode, editingChain, { address });
|
||||||
|
const savedItem = {
|
||||||
|
address: saved?.address || address,
|
||||||
|
chain: saved?.chain || editingChain,
|
||||||
|
updatedAtMs: Number(saved?.updatedAtMs || Date.now()),
|
||||||
|
};
|
||||||
|
// 保存成功后直接用服务端回包更新当前行;下一次校验也会由 admin-server 重新读同一 SQL 行。
|
||||||
|
setData((current) => ({
|
||||||
|
...current,
|
||||||
|
items: [...current.items.filter((item) => item.chain !== savedItem.chain), savedItem],
|
||||||
|
}));
|
||||||
|
setEditingChain("");
|
||||||
|
setAddressDraft("");
|
||||||
|
} catch (saveError) {
|
||||||
|
setError(saveError.message || "保存 USDT 收款地址失败");
|
||||||
|
} finally {
|
||||||
|
setSaving(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog fullWidth maxWidth="sm" open={open} onClose={loading ? undefined : onClose}>
|
<Dialog fullWidth maxWidth="sm" open={open} onClose={loading || saving ? undefined : onClose}>
|
||||||
<DialogTitle>USDT 收款地址</DialogTitle>
|
<DialogTitle>USDT 收款地址</DialogTitle>
|
||||||
<DialogContent className="finance-usdt-address-dialog">
|
<DialogContent className="finance-usdt-address-dialog">
|
||||||
{error ? <Alert severity="error">{error}</Alert> : null}
|
{error ? <Alert severity="error">{error}</Alert> : null}
|
||||||
<TextField
|
<TextField
|
||||||
disabled={loading || !apps.length}
|
disabled={loading || saving || !apps.length}
|
||||||
label="APP"
|
label="APP"
|
||||||
select
|
select
|
||||||
value={appCode}
|
value={appCode}
|
||||||
@ -96,36 +153,82 @@ export function FinanceUSDTAddressDialog({ apps, initialAppCode, open, onClose,
|
|||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
{loading ? <LinearProgress aria-label="加载 USDT 收款地址" /> : null}
|
{loading ? <LinearProgress aria-label="加载 USDT 收款地址" /> : null}
|
||||||
{!loading && data.items.length ? (
|
{!loading && visibleChains.length ? (
|
||||||
<div className="finance-usdt-address-list">
|
<div className="finance-usdt-address-list">
|
||||||
{data.items.map((item) => (
|
{visibleChains.map((chain) => {
|
||||||
<section className="finance-usdt-address-item" key={item.chain}>
|
const item = itemsByChain.get(chain);
|
||||||
<div className="finance-usdt-address-item__meta">
|
const editing = editingChain === chain;
|
||||||
<strong>{chainLabel(item.chain)}</strong>
|
return (
|
||||||
<small>{item.updatedAtMs ? `更新于 ${formatTime(item.updatedAtMs)}` : ""}</small>
|
<section className="finance-usdt-address-item" key={chain}>
|
||||||
</div>
|
<div className="finance-usdt-address-item__meta">
|
||||||
<div className="finance-usdt-address-item__value">
|
<strong>{chainLabel(chain)}</strong>
|
||||||
<code>{item.address}</code>
|
<small>{item?.updatedAtMs ? `更新于 ${formatTime(item.updatedAtMs)}` : "未配置"}</small>
|
||||||
<Button
|
</div>
|
||||||
aria-label={`复制 ${item.chain} 地址`}
|
{editing ? (
|
||||||
size="small"
|
<div className="finance-usdt-address-item__editor">
|
||||||
startIcon={<ContentCopyOutlined fontSize="small" />}
|
<TextField
|
||||||
variant="outlined"
|
autoFocus
|
||||||
onClick={() => copyAddress(item)}
|
fullWidth
|
||||||
>
|
label={`${chainLabel(chain)} 收款地址`}
|
||||||
{copiedChain === item.chain ? "已复制" : "复制"}
|
size="small"
|
||||||
</Button>
|
value={addressDraft}
|
||||||
</div>
|
onChange={(event) => setAddressDraft(event.target.value)}
|
||||||
</section>
|
/>
|
||||||
))}
|
<div className="finance-usdt-address-item__actions">
|
||||||
|
<Button disabled={saving} size="small" variant="outlined" onClick={() => setEditingChain("")}>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
aria-label={`保存 ${chain} 地址`}
|
||||||
|
disabled={saving}
|
||||||
|
size="small"
|
||||||
|
variant="contained"
|
||||||
|
onClick={saveAddress}
|
||||||
|
>
|
||||||
|
{saving ? "保存中" : "保存"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="finance-usdt-address-item__value">
|
||||||
|
{item ? <code>{item.address}</code> : <span className="finance-usdt-address-item__missing">尚未添加</span>}
|
||||||
|
<div className="finance-usdt-address-item__actions">
|
||||||
|
{item ? (
|
||||||
|
<Button
|
||||||
|
aria-label={`复制 ${chain} 地址`}
|
||||||
|
size="small"
|
||||||
|
startIcon={<ContentCopyOutlined fontSize="small" />}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => copyAddress(item)}
|
||||||
|
>
|
||||||
|
{copiedChain === chain ? "已复制" : "复制"}
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
{canUpdate ? (
|
||||||
|
<Button
|
||||||
|
aria-label={`${item ? "修改" : "添加"} ${chain} 地址`}
|
||||||
|
size="small"
|
||||||
|
startIcon={item ? <EditOutlined fontSize="small" /> : <AddOutlined fontSize="small" />}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => beginEdit(chain)}
|
||||||
|
>
|
||||||
|
{item ? "修改" : "添加"}
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{!loading && appCode && !data.items.length && !error ? (
|
{!loading && appCode && !visibleChains.length && !error ? (
|
||||||
<div className="finance-usdt-address-empty">当前 APP 未配置 USDT 收款地址</div>
|
<div className="finance-usdt-address-empty">当前 APP 未配置 USDT 收款地址</div>
|
||||||
) : null}
|
) : null}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button disabled={loading} variant="outlined" onClick={onClose}>
|
<Button disabled={loading || saving} variant="outlined" onClick={onClose}>
|
||||||
关闭
|
关闭
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
@ -142,3 +245,13 @@ function chainLabel(chain) {
|
|||||||
}
|
}
|
||||||
return chain;
|
return chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validUSDTAddress(chain, address) {
|
||||||
|
if (chain === "TRON") {
|
||||||
|
return /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(address);
|
||||||
|
}
|
||||||
|
if (chain === "BSC") {
|
||||||
|
return /^0x[0-9a-fA-F]{40}$/.test(address);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@ -41,3 +41,52 @@ test("loads the selected app USDT address and copies the exact SQL value", async
|
|||||||
await waitFor(() => expect(writeText).toHaveBeenCalledWith("TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"));
|
await waitFor(() => expect(writeText).toHaveBeenCalledWith("TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"));
|
||||||
expect(screen.getByRole("button", { name: "复制 TRON 地址" })).toHaveTextContent("已复制");
|
expect(screen.getByRole("button", { name: "复制 TRON 地址" })).toHaveTextContent("已复制");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("modifies an existing TRON address and keeps the saved SQL value in the row", async () => {
|
||||||
|
const originalAddress = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t";
|
||||||
|
const updatedAddress = "TLv7wERsM42ZeobMrrKVC5C72d3gFUa44J";
|
||||||
|
const onSave = vi.fn(async (appCode, chain, payload) => ({
|
||||||
|
address: payload.address,
|
||||||
|
appCode,
|
||||||
|
chain,
|
||||||
|
updatedAtMs: 200,
|
||||||
|
}));
|
||||||
|
|
||||||
|
render(
|
||||||
|
<FinanceUSDTAddressDialog
|
||||||
|
apps={[{ appCode: "aslan", appName: "Aslan" }]}
|
||||||
|
canUpdate
|
||||||
|
initialAppCode="aslan"
|
||||||
|
open
|
||||||
|
onClose={vi.fn()}
|
||||||
|
onLoad={vi.fn(async () => ({ appCode: "aslan", items: [{ address: originalAddress, chain: "TRON" }] }))}
|
||||||
|
onSave={onSave}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
await screen.findByText(originalAddress);
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "修改 TRON 地址" }));
|
||||||
|
fireEvent.change(screen.getByRole("textbox", { name: "TRON · TRC20 收款地址" }), { target: { value: updatedAddress } });
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "保存 TRON 地址" }));
|
||||||
|
|
||||||
|
await waitFor(() => expect(onSave).toHaveBeenCalledWith("aslan", "TRON", { address: updatedAddress }));
|
||||||
|
expect(await screen.findByText(updatedAddress)).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole("button", { name: "修改 TRON 地址" })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("shows an add action when the selected app has no TRON address", async () => {
|
||||||
|
render(
|
||||||
|
<FinanceUSDTAddressDialog
|
||||||
|
apps={[{ appCode: "aslan", appName: "Aslan" }]}
|
||||||
|
canUpdate
|
||||||
|
initialAppCode="aslan"
|
||||||
|
open
|
||||||
|
onClose={vi.fn()}
|
||||||
|
onLoad={vi.fn(async () => ({ appCode: "aslan", items: [] }))}
|
||||||
|
onSave={vi.fn()}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(await screen.findByRole("button", { name: "添加 TRON 地址" })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole("button", { name: "添加 BSC 地址" })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|||||||
@ -3,6 +3,7 @@ export const FINANCE_PERMISSIONS = {
|
|||||||
configureCoinSellerRechargeExchangeRate: "coin-seller:exchange-rate",
|
configureCoinSellerRechargeExchangeRate: "coin-seller:exchange-rate",
|
||||||
createCoinSellerRechargeOrder: "finance-order:coin-seller-recharge:create",
|
createCoinSellerRechargeOrder: "finance-order:coin-seller-recharge:create",
|
||||||
grantCoinSellerRechargeOrder: "finance-order:coin-seller-recharge:grant",
|
grantCoinSellerRechargeOrder: "finance-order:coin-seller-recharge:grant",
|
||||||
|
updateUSDTAddress: "finance-order:usdt-address:update",
|
||||||
verifyCoinSellerRechargeOrder: "finance-order:coin-seller-recharge:verify",
|
verifyCoinSellerRechargeOrder: "finance-order:coin-seller-recharge:verify",
|
||||||
viewCoinSellerRechargeOrders: "finance-order:coin-seller-recharge:view",
|
viewCoinSellerRechargeOrders: "finance-order:coin-seller-recharge:view",
|
||||||
viewAppRechargeDetails: "payment-bill:view",
|
viewAppRechargeDetails: "payment-bill:view",
|
||||||
@ -22,7 +23,8 @@ export const COIN_SELLER_RECHARGE_PROVIDERS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const COIN_SELLER_RECHARGE_CHAINS = [
|
export const COIN_SELLER_RECHARGE_CHAINS = [
|
||||||
["C2C", "C2C"],
|
["C2C", "Binance Pay / C2C"],
|
||||||
|
["OFFCHAIN", "Binance 站内转账 / Off-chain"],
|
||||||
["TRON", "TRON"],
|
["TRON", "TRON"],
|
||||||
["BSC", "BSC"],
|
["BSC", "BSC"],
|
||||||
];
|
];
|
||||||
|
|||||||
@ -1897,6 +1897,24 @@ a {
|
|||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.finance-usdt-address-item__editor {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.finance-usdt-address-item__actions {
|
||||||
|
display: flex;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.finance-usdt-address-item__missing {
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
.finance-usdt-address-empty {
|
.finance-usdt-address-empty {
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
padding: var(--space-5) 0;
|
padding: var(--space-5) 0;
|
||||||
|
|||||||
@ -390,6 +390,7 @@ export const API_OPERATIONS = {
|
|||||||
updateWeeklyStarCycle: "updateWeeklyStarCycle",
|
updateWeeklyStarCycle: "updateWeeklyStarCycle",
|
||||||
uploadFile: "uploadFile",
|
uploadFile: "uploadFile",
|
||||||
uploadImage: "uploadImage",
|
uploadImage: "uploadImage",
|
||||||
|
upsertFinanceUSDTAddress: "upsertFinanceUSDTAddress",
|
||||||
upsertLuckyGiftConfig: "upsertLuckyGiftConfig",
|
upsertLuckyGiftConfig: "upsertLuckyGiftConfig",
|
||||||
upsertResourceShopItems: "upsertResourceShopItems",
|
upsertResourceShopItems: "upsertResourceShopItems",
|
||||||
upsertRule: "upsertRule",
|
upsertRule: "upsertRule",
|
||||||
@ -3023,6 +3024,13 @@ export const API_ENDPOINTS: Record<ApiOperationId, ApiEndpoint> = {
|
|||||||
permission: "upload:create",
|
permission: "upload:create",
|
||||||
permissions: ["upload:create"]
|
permissions: ["upload:create"]
|
||||||
},
|
},
|
||||||
|
upsertFinanceUSDTAddress: {
|
||||||
|
method: "PUT",
|
||||||
|
operationId: API_OPERATIONS.upsertFinanceUSDTAddress,
|
||||||
|
path: "/v1/admin/finance/usdt-addresses/{app_code}/{chain}",
|
||||||
|
permission: "finance-order:usdt-address:update",
|
||||||
|
permissions: ["finance-order:usdt-address:update"]
|
||||||
|
},
|
||||||
upsertLuckyGiftConfig: {
|
upsertLuckyGiftConfig: {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
operationId: API_OPERATIONS.upsertLuckyGiftConfig,
|
operationId: API_OPERATIONS.upsertLuckyGiftConfig,
|
||||||
|
|||||||
67
src/shared/api/generated/schema.d.ts
vendored
67
src/shared/api/generated/schema.d.ts
vendored
@ -2604,6 +2604,22 @@ export interface paths {
|
|||||||
patch?: never;
|
patch?: never;
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
|
"/admin/finance/usdt-addresses/{app_code}/{chain}": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put: operations["upsertFinanceUSDTAddress"];
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
"/admin/finance/coin-seller-recharge-exchange-rates/{app_code}": {
|
"/admin/finance/coin-seller-recharge-exchange-rates/{app_code}": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@ -5443,6 +5459,9 @@ export interface components {
|
|||||||
ApiResponseFinanceUSDTAddresses: components["schemas"]["Envelope"] & {
|
ApiResponseFinanceUSDTAddresses: components["schemas"]["Envelope"] & {
|
||||||
data?: components["schemas"]["FinanceUSDTAddresses"];
|
data?: components["schemas"]["FinanceUSDTAddresses"];
|
||||||
};
|
};
|
||||||
|
ApiResponseFinanceUSDTAddressUpsert: components["schemas"]["Envelope"] & {
|
||||||
|
data?: components["schemas"]["FinanceUSDTAddressUpsert"];
|
||||||
|
};
|
||||||
ApiResponseFinanceCoinSellerRechargeReceiptVerification: components["schemas"]["Envelope"] & {
|
ApiResponseFinanceCoinSellerRechargeReceiptVerification: components["schemas"]["Envelope"] & {
|
||||||
data?: components["schemas"]["FinanceCoinSellerRechargeReceiptVerification"];
|
data?: components["schemas"]["FinanceCoinSellerRechargeReceiptVerification"];
|
||||||
};
|
};
|
||||||
@ -6113,7 +6132,7 @@ export interface components {
|
|||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
providerCode: "mifapay" | "v5pay" | "usdt";
|
providerCode: "mifapay" | "v5pay" | "usdt";
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
chain?: "TRON" | "BSC";
|
chain?: "C2C" | "OFFCHAIN" | "TRON" | "BSC";
|
||||||
externalOrderNo: string;
|
externalOrderNo: string;
|
||||||
providerCountryCode?: string;
|
providerCountryCode?: string;
|
||||||
providerCurrencyCode?: string;
|
providerCurrencyCode?: string;
|
||||||
@ -6183,7 +6202,7 @@ export interface components {
|
|||||||
providerCurrencyCode?: string;
|
providerCurrencyCode?: string;
|
||||||
providerAmountMinor?: number;
|
providerAmountMinor?: number;
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
chain?: "TRON" | "BSC";
|
chain?: "C2C" | "OFFCHAIN" | "TRON" | "BSC";
|
||||||
remark?: string;
|
remark?: string;
|
||||||
};
|
};
|
||||||
FinanceCoinSellerRechargeQuoteInput: {
|
FinanceCoinSellerRechargeQuoteInput: {
|
||||||
@ -6230,6 +6249,17 @@ export interface components {
|
|||||||
appCode: string;
|
appCode: string;
|
||||||
items: components["schemas"]["FinanceUSDTAddress"][];
|
items: components["schemas"]["FinanceUSDTAddress"][];
|
||||||
};
|
};
|
||||||
|
FinanceUSDTAddressInput: {
|
||||||
|
address: string;
|
||||||
|
};
|
||||||
|
FinanceUSDTAddressUpsert: {
|
||||||
|
appCode: string;
|
||||||
|
/** @enum {string} */
|
||||||
|
chain: "TRON" | "BSC";
|
||||||
|
address: string;
|
||||||
|
/** Format: int64 */
|
||||||
|
updatedAtMs: number;
|
||||||
|
};
|
||||||
FinanceCoinSellerRechargeExchangeRateInput: {
|
FinanceCoinSellerRechargeExchangeRateInput: {
|
||||||
tiers: {
|
tiers: {
|
||||||
minUsdAmount: number;
|
minUsdAmount: number;
|
||||||
@ -6250,7 +6280,7 @@ export interface components {
|
|||||||
currencyCode?: string;
|
currencyCode?: string;
|
||||||
providerAmountMinor?: number;
|
providerAmountMinor?: number;
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
chain?: "TRON" | "BSC";
|
chain?: "C2C" | "OFFCHAIN" | "TRON" | "BSC";
|
||||||
receiveAddress?: string;
|
receiveAddress?: string;
|
||||||
verifiedAtMs?: number;
|
verifiedAtMs?: number;
|
||||||
failureReason?: string;
|
failureReason?: string;
|
||||||
@ -6262,7 +6292,7 @@ export interface components {
|
|||||||
providerCode: "mifapay" | "v5pay" | "usdt";
|
providerCode: "mifapay" | "v5pay" | "usdt";
|
||||||
externalOrderNo: string;
|
externalOrderNo: string;
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
chain?: "TRON" | "BSC";
|
chain?: "C2C" | "OFFCHAIN" | "TRON" | "BSC";
|
||||||
usdAmount?: number;
|
usdAmount?: number;
|
||||||
usdMinorAmount?: number;
|
usdMinorAmount?: number;
|
||||||
/**
|
/**
|
||||||
@ -7095,6 +7125,15 @@ export interface components {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
/** @description OK */
|
/** @description OK */
|
||||||
|
FinanceUSDTAddressResponse: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["ApiResponseFinanceUSDTAddressUpsert"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description OK */
|
||||||
FinanceCoinSellerRechargeReceiptVerificationResponse: {
|
FinanceCoinSellerRechargeReceiptVerificationResponse: {
|
||||||
headers: {
|
headers: {
|
||||||
[name: string]: unknown;
|
[name: string]: unknown;
|
||||||
@ -7514,6 +7553,11 @@ export interface components {
|
|||||||
"application/json": components["schemas"]["FinanceCoinSellerRechargeReceiptVerificationInput"];
|
"application/json": components["schemas"]["FinanceCoinSellerRechargeReceiptVerificationInput"];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
FinanceUSDTAddressRequest: {
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["FinanceUSDTAddressInput"];
|
||||||
|
};
|
||||||
|
};
|
||||||
FinanceWithdrawalAuditRequest: {
|
FinanceWithdrawalAuditRequest: {
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
@ -10833,6 +10877,21 @@ export interface operations {
|
|||||||
200: components["responses"]["FinanceUSDTAddressesResponse"];
|
200: components["responses"]["FinanceUSDTAddressesResponse"];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
upsertFinanceUSDTAddress: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
app_code: string;
|
||||||
|
chain: "TRON" | "BSC";
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: components["requestBodies"]["FinanceUSDTAddressRequest"];
|
||||||
|
responses: {
|
||||||
|
200: components["responses"]["FinanceUSDTAddressResponse"];
|
||||||
|
};
|
||||||
|
};
|
||||||
getFinanceCoinSellerRechargeExchangeRate: {
|
getFinanceCoinSellerRechargeExchangeRate: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user