Card Transactions
Flow Summary
The transaction flows are currently:
Step 1. Make sure the source card is active
Use Virtual Cards to confirm card status first
Step 2. Start a top-up or transfer
Use POST /api/v1/card-topup or POST /api/v1/cards/transfer
Step 3. Check the follow-up record
Use GET /api/v1/card-topup or GET /api/v1/cards/transfer/{id}
All routes below should be treated as protected routes and sent with:
Authorization: Bearer <access_token>
The POST /api/v1/card-topup route also passes through DecryptField(), so it accepts either plain JSON or the encrypted wrapper described in Overview.
1. Create Card Top-Up
POST /api/v1/card-topup
Creates a new top-up order for a card.
Request
{
"amount": 100,
"card_id": 508
}
Request rules
amountis requiredcard_idis requiredcard_idis the local numeric card record ID
Important backend behavior
- The card must belong to the current user
- The card must currently be
ACTIVE - The input
amountis treated as the user's USDT funding amount - Fees, adjusted exchange rate, and the final net amount are calculated server-side
- The new top-up row is created with
status = PENDING - After the DB row is created, the service makes a synchronous RPC call to create a payment order
Success response notes
- The response returns the new top-up entity
- It includes derived payment fields such as rate, fee, and payable totals generated by the service
2. List Card Top-Ups
GET /api/v1/card-topup
Lists top-up records with pagination.
Query parameters
| Name | Required | Notes |
|---|---|---|
page | No | Default 1 |
limit | No | Default 10 |
search | No | Free-text search |
status | No | Filters by top-up status |
from_date | No | Date range start |
to_date | No | Date range end |
Important backend caveat
- The current repository implementation is not explicitly scoped to the authenticated mobile user
- In practice, this behaves more like a shared top-up list filtered only by the provided query params
- Clients should treat this as an implementation gap and avoid assuming strict user-only isolation until the backend is tightened
3. Create Card Transfer
POST /api/v1/cards/transfer
Transfers balance from one card to another.
Request
{
"source_card_uuid": "5e5d4990-2241-46dc-9dbd-8abcf97e6061",
"destination_card_uuid": "6f77454d-400f-4ec4-8e2c-7a7ebfa0bcfe",
"amount": "25.50",
"remark": "Split payment"
}
Request rules
source_card_uuidis required and must be a UUIDdestination_card_uuidis required and must be a UUIDamountis requiredamountmust be sent as a JSON string, not a JSON numberamountmust parse as a decimal value greater than0remarkis optional, max length255
Important backend behavior
- The route is IP-rate-limited to
60requests per minute - The source and destination cards cannot be the same card
- The source card must belong to the current user
- Both cards must be
ACTIVE - The service checks the source-card balance before executing the transfer
- If the transaction type is empty internally, the service defaults it to
TRANSFER - A transfer row is created first with
status = PENDING, then the transfer executes synchronously - If the downstream transfer fails, the API returns an error and includes transfer metadata such as
transfer_uuidandstatusin the error payload
4. Get Transfer By ID
GET /api/v1/cards/transfer/{id}
Returns one transfer record by local numeric ID.
Notes
- The lookup is scoped to the current mobile user
- If the transfer belongs to another user or does not exist, the route returns not found
- Use this endpoint as the follow-up record lookup after creating a transfer