Card Transactions
How card transactions settle against the Master Account.
Before You Start
Read the following guides before proceeding:
| Guide | Why |
|---|---|
| Overview | Understand External Authorization model |
| Authorization API | How transaction authorization works |
| Issue and Manage a Card | Standard card issuance flow |
Card Issuance
Card issuance follows the same API flow as standard integration. See Issue and Manage a Card.
All card management operations (block, unblock, close, set limits) work identically.
Transaction Settlement
The difference is in how transactions are settled:
| Aspect | Standard Flow | External Authorization |
|---|---|---|
| Balance check | User's AA wallet | Your authorization API |
| Settlement source | User's AA wallet | Master Account |
| Balance updates | On-chain per user | Your external system |
Standard Flow
1. User pays with card
2. Wirex checks user's on-chain balance
3. Wirex holds funds on user's AA wallet
4. Transaction settles from user's AA wallet
5. Webhook sent
External Authorization Flow
1. User pays with card
2. Wirex calls your authorization API
3. You check balance in your system
4. If approved, Wirex debits Master Account
5. Webhook sent
6. You update user's balance in your system
Transaction Flow Diagram
sequenceDiagram
participant User
participant Merchant
participant Wirex
participant Partner as Your System
participant Master as Master Account
User->>Merchant: Pay with card
Merchant->>Wirex: Authorization request
Wirex->>Partner: POST /external-authorization/...
Partner->>Partner: Check user balance
Partner-->>Wirex: { is_allowed: true }
Wirex->>Master: Debit Master Account
Wirex-->>Merchant: Approved
Wirex->>Partner: Transaction webhook
Partner->>Partner: Debit user balance
Webhooks
After transaction completion, Wirex sends a webhook notification. Use this to update your internal balance records.
Card Transaction Webhook
Endpoint: POST {your_webhook_base_url}/v2/webhooks/activities
{
"id": "a7b2a4b3-2f61-41a7-9f5a-3f3f0b1a3d22",
"type": "CardTransaction",
"status": "Completed",
"direction": "Outbound",
"source_amount": {
"amount": 123.45,
"currency": "USD"
},
"user_id": "5f7e3f7b-8e0c-48b8-9f2c-3e0c9d7a2a11",
"card_id": "c0b2c7b1-1d2e-4d7a-9a5d-2b7e3c4d5f66",
"created_at": "2024-01-01T10:00:00Z"
}| Field | Description |
|---|---|
type | CardTransaction |
status | Completed or Failed |
direction | Outbound (purchase) or Inbound (refund) |
source_amount | Transaction amount and currency |
Handling Webhooks
app.post('/v2/webhooks/activities', async (req, res) => {
const { type, status, direction, source_amount, user_id } = req.body;
if (type !== 'CardTransaction' || status !== 'Completed') {
return res.sendStatus(200);
}
if (direction === 'Outbound') {
// Purchase: debit user balance
await db.debitUserBalance(user_id, source_amount.amount, source_amount.currency);
} else {
// Refund: credit user balance
await db.creditUserBalance(user_id, source_amount.amount, source_amount.currency);
}
res.sendStatus(200);
});@app.post("/v2/webhooks/activities")
async def handle_activity_webhook(request: ActivityWebhook):
if request.type != "CardTransaction" or request.status != "Completed":
return Response(status_code=200)
if request.direction == "Outbound":
# Purchase: debit user balance
await db.debit_user_balance(
request.user_id,
request.source_amount.amount,
request.source_amount.currency
)
else:
# Refund: credit user balance
await db.credit_user_balance(
request.user_id,
request.source_amount.amount,
request.source_amount.currency
)
return Response(status_code=200)func handleActivityWebhook(w http.ResponseWriter, r *http.Request) {
var activity ActivityWebhook
json.NewDecoder(r.Body).Decode(&activity)
if activity.Type != "CardTransaction" || activity.Status != "Completed" {
w.WriteHeader(http.StatusOK)
return
}
if activity.Direction == "Outbound" {
// Purchase: debit user balance
db.DebitUserBalance(activity.UserID, activity.SourceAmount.Amount, activity.SourceAmount.Currency)
} else {
// Refund: credit user balance
db.CreditUserBalance(activity.UserID, activity.SourceAmount.Amount, activity.SourceAmount.Currency)
}
w.WriteHeader(http.StatusOK)
}Balance Reconciliation
Your system is the source of truth for user balances. Wirex only tracks the Master Account balance.
Reconciliation approach:
- Track all authorization requests you approve
- Match against transaction webhooks received
- The Master Account balance should equal the sum of all processed transactions
Discrepancies may occur when:
- Mandatory transactions processed without your approval
- Network timeouts where transaction actually succeeded
- Chargebacks or reversals
Always use unique_operation_id for idempotent handling.
FAQ
Q: What if I approve a transaction but the Master Account has insufficient funds? A: The transaction is declined. Monitor and maintain Master Account balance.
Q: How do refunds work? A: Refunds are mandatory Credit transactions. The Master Account is credited, and you receive a webhook to credit the user's balance.
Q: Can I query transaction history? A: Use the standard Activity History endpoints. Transactions are attributed to users by their EOA identifier.
Updated about 1 month ago
