Card Transactions
Understand the card transaction lifecycle, on-chain execution, and webhook notifications.
Before You Start
Read the following guides before proceeding:
| Guide | Why |
|---|---|
| Getting Started | Platform overview and setup |
| Api Basics | Required headers and request configuration |
| Authentication | How to obtain access tokens |
| Onboarding | User and wallet registration |
| KYC | KYC verification requirements |
Overview
When a user makes a card payment, the transaction flows through both on-chain and off-chain systems. This document explains:
- What happens on-chain during a card transaction
- The webhook notifications you receive
- Transaction lifecycle stages
On-Chain Flow
Card transactions execute through the FundsManagement contract, an ERC-7579 executor module installed on the user's Smart Wallet.
Card Debit (User Spending)
When the user makes a purchase:
User's Smart Wallet → FundsManagement Contract → FundsBuffer Contract
(WUSD/WEUR) ExecuteOutgoingTransaction Transfer tokens
- Wirex oracle initiates the transaction
- FundsManagement transfers unified tokens from user's wallet
- Tokens arrive in FundsBuffer (Wirex treasury)
- Transaction hash is recorded
Card Credit (Refund/Reversal)
When a refund or reversal occurs:
FundsBuffer Contract → FundsManagement Contract → User's Smart Wallet
ProcessCreditTransaction (WUSD/WEUR)
- Wirex initiates the credit
- Tokens transfer from FundsBuffer to user's wallet
- Balance restored to user
Tokens Used
| Token | Chain | Description |
|---|---|---|
| WUSD | Base | Unified USD stablecoin (18 decimals) |
| WEUR | Base | Unified EUR stablecoin (18 decimals) |
The system automatically selects the appropriate token based on the card's currency and available balance.
Transaction Lifecycle
Card transactions progress through multiple stages:
Initiated → CryptoOut → CardOut → Completed
↓
(on failure/refund)
↓
Reversal
Transaction Stages
| Stage | Description | On-Chain |
|---|---|---|
Initiated | Transaction request received | No |
CryptoOut | Tokens debited from user wallet | Yes |
CardOut | Card network processing complete | No |
Completed | Transaction finalized | No |
Reversal | Tokens returned (failure, partial refund, or full refund) | Yes |
Credit Transaction Stages
For refunds and reversals:
| Stage | Description | On-Chain |
|---|---|---|
Initiated | Credit request received | No |
CardIn | Card network processing | No |
CryptoIn | Tokens credited to user wallet | Yes |
Completed | Credit finalized | No |
Activity Webhook
You receive webhook notifications at each stage of the transaction lifecycle.
Endpoint: POST {your_webhook_base_url}/v2/webhooks/activities
Webhook Payload
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"user_address": "0x1234567890abcdef1234567890abcdef12345678",
"source": {
"type": "Cards",
"card": {
"id": "64120850-73a1-4df5-a074-d463258c9deb",
"pan_last": "1234",
"payment_system": "Visa"
}
},
"destination": {
"type": "Merchant",
"merchant": {
"name": "Amazon",
"category": "Online Shopping",
"country": "US"
}
},
"status": "Completed",
"status_reason": null,
"source_amount": {
"amount": 50.00,
"token_symbol": "WUSD",
"token_address": "0x..."
},
"destination_amount": {
"amount": 50.00,
"currency": "USD"
},
"type": "CardTransaction",
"direction": "Outbound",
"operations": [
{
"hash": "0xabc123...",
"operation_amount": {
"amount": -50.00,
"token_symbol": "WUSD",
"token_address": "0x..."
},
"transaction_amount": {
"amount": 50.00,
"currency": "USD"
},
"rate": {
"ticker": "USD/WUSD",
"rate": 1.0
}
}
],
"activity_steps": [
{
"type": "Initiated",
"status": "Completed",
"created_at": "2024-01-15T10:00:00Z",
"completed_at": "2024-01-15T10:00:00Z"
},
{
"type": "CryptoOut",
"status": "Completed",
"created_at": "2024-01-15T10:00:01Z",
"completed_at": "2024-01-15T10:00:05Z"
},
{
"type": "CardOut",
"status": "Completed",
"created_at": "2024-01-15T10:00:05Z",
"completed_at": "2024-01-15T10:00:06Z"
},
{
"type": "Completed",
"status": "Completed",
"created_at": "2024-01-15T10:00:06Z",
"completed_at": "2024-01-15T10:00:06Z"
}
],
"created_at": "2024-01-15T10:00:00Z"
}Payload Fields
| Field | Description |
|---|---|
id | Unique activity identifier |
user_address | User's EOA address |
source | Transaction source (card details) |
destination | Transaction destination (merchant details) |
status | Overall transaction status |
status_reason | Reason for status (set on decline/failure) |
source_amount | Amount debited (crypto for outbound, fiat for inbound) |
destination_amount | Amount received (fiat for outbound, crypto for inbound) |
type | CardTransaction |
direction | Outbound (debit) or Inbound (credit) |
operations | On-chain operations with transaction hashes |
activity_steps | Ordered list of transaction stages |
Amount Fields by Direction
The source_amount and destination_amount fields contain different asset types depending on transaction direction:
| Direction | source_amount | destination_amount |
|---|---|---|
Outbound (user spending) | Crypto (token_symbol, token_address) | Fiat (currency) |
Inbound (card receive) | Fiat (currency) | Crypto (token_symbol, token_address) |
Outbound example (user pays $50 with WUSD):
{
"source_amount": { "amount": 50.00, "token_symbol": "WUSD", "token_address": "0x..." },
"destination_amount": { "amount": 50.00, "currency": "USD" }
}Inbound example (user receives $50 via card send):
{
"source_amount": { "amount": 50.00, "currency": "USD" },
"destination_amount": { "amount": 50.00, "token_symbol": "WUSD", "token_address": "0x..." }
}Note: The source_amount and destination_amount fields always use positive values. Until the crypto step (CryptoOut or CryptoIn) is executed, the crypto amount may be 0 or missing from the webhook payload, as it is calculated based on actual on-chain operations.
Operation Amount Signs
The operation_amount within each operation uses signed values to indicate debit or credit:
| Direction | Amount Sign | Example |
|---|---|---|
Outbound (debit) | Negative | -50.00 |
Inbound (credit) | Positive | 50.00 |
This allows you to distinguish the transaction direction from the operation data alone without checking the direction field.
Transaction Status
| Status | Description |
|---|---|
Pending | Transaction in progress |
Completed | Transaction successful |
Failed | Transaction declined or reversed |
Status Reasons
When status is Failed, status_reason indicates why:
| Reason | Description |
|---|---|
InsufficientFunds | User balance too low |
DailySpendAmountIsExceeded | Daily card spending limit exceeded |
GeneralError | Processing error |
Webhook Sequence
You may receive multiple webhooks for a single transaction as it progresses through stages.
Successful Debit Transaction
Webhook 1: status=Pending, steps=[Initiated✓]
↓
Webhook 2: status=Pending, steps=[Initiated✓, CryptoOut✓]
↓
Webhook 3: status=Pending, steps=[Initiated✓, CryptoOut✓, CardOut✓]
↓
Webhook 4: status=Completed, steps=[Initiated✓, CryptoOut✓, CardOut✓, Completed✓]
Transaction with Reversal (Failure or Refund)
When a transaction fails after on-chain debit, or when a merchant issues a partial/full refund:
Webhook 1: status=Pending, steps=[Initiated✓]
↓
Webhook 2: status=Pending, steps=[Initiated✓, CryptoOut✓]
↓
Webhook 3: status=Completed, steps=[Initiated✓, CryptoOut✓, CardOut✓, Completed✓]
↓
Webhook 4: status=Completed, steps=[..., Reversal✓] // Refund issued
Or for immediate failure:
Webhook 1: status=Pending, steps=[Initiated✓, CryptoOut✓]
↓
Webhook 2: status=Failed, steps=[Initiated✓, CryptoOut✓, Reversal✓]
The Reversal step indicates tokens were returned to the user's wallet. Check operations[] for the reversal transaction hash.
Declined at Authorization
Webhook 1: status=Failed, status_reason=DailySpendAmountIsExceeded, steps=[Initiated✓]
No on-chain transaction occurs when declined at authorization.
Refunds
Refunds are not separate transactions. They appear as credit operations within the original debit transaction, indicated by the Reversal step.
When a refund occurs:
- The original transaction webhook is sent again with an additional
Reversalstep - A new operation with positive
operation_amountis added to theoperationsarray - The original debit operation (negative amount) remains in the array
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "CardTransaction",
"direction": "Outbound",
"status": "Completed",
"operations": [
{
"hash": "0xabc123...",
"operation_amount": {
"amount": -50.00,
"token_symbol": "WUSD",
"token_address": "0x..."
}
},
{
"hash": "0xdef456...",
"operation_amount": {
"amount": 50.00,
"token_symbol": "WUSD",
"token_address": "0x..."
}
}
],
"activity_steps": [
{ "type": "Initiated", "status": "Completed" },
{ "type": "CryptoOut", "status": "Completed" },
{ "type": "CardOut", "status": "Completed" },
{ "type": "Completed", "status": "Completed" },
{ "type": "Reversal", "status": "Completed" }
]
}For partial refunds, the credit operation amount will be less than the original debit amount.
Original Credit (Card Receive)
Inbound card transactions occur when funds are sent from another card to the user's Wirex card (OriginalCredit). These are separate transactions with direction: Inbound.
{
"type": "CardTransaction",
"direction": "Inbound",
"status": "Completed",
"source_amount": {
"amount": 50.00,
"currency": "USD"
},
"destination_amount": {
"amount": 50.00,
"token_symbol": "WUSD",
"token_address": "0x..."
},
"activity_steps": [
{
"type": "Initiated",
"status": "Completed"
},
{
"type": "CardIn",
"status": "Completed"
},
{
"type": "CryptoIn",
"status": "Completed"
},
{
"type": "Completed",
"status": "Completed"
}
],
"operations": [
{
"hash": "0xdef456...",
"operation_amount": {
"amount": 50.00,
"token_symbol": "WUSD",
"token_address": "0x..."
},
"transaction_amount": {
"amount": 50.00,
"currency": "USD"
},
"rate": {
"ticker": "USD/WUSD",
"rate": 1.0
}
}
]
}Verifying On-Chain Transactions
Each operation in the webhook includes a transaction hash. You can verify the on-chain transaction:
// Example: Verify transaction on Base
const txHash = webhook.operations[0].hash;
const receipt = await provider.getTransactionReceipt(txHash);
if (receipt.status === 1) {
console.log('Transaction confirmed on-chain');
}# Example: Verify transaction on Base
tx_hash = webhook["operations"][0]["hash"]
receipt = w3.eth.get_transaction_receipt(tx_hash)
if receipt["status"] == 1:
print("Transaction confirmed on-chain")// Example: Verify transaction on Base
txHash := common.HexToHash(webhook.Operations[0].Hash)
receipt, _ := client.TransactionReceipt(ctx, txHash)
if receipt.Status == 1 {
fmt.Println("Transaction confirmed on-chain")
}Implementation Notes
Idempotency
- Each activity has a unique
id - You may receive duplicate webhooks for the same activity
- Use
id+statusto deduplicate
Timing
CryptoOut/CryptoInsteps depend on blockchain confirmation- Card network steps (
CardOut/CardIn) depend on issuer processing - Total time varies from seconds to minutes
Balance Updates
- User's on-chain balance updates immediately after
CryptoOut/CryptoIn - Use
operations[].hashto query the exact on-chain state
Updated 8 days ago
