Card Transactions

Understand the card transaction lifecycle, on-chain execution, and webhook notifications.

Before You Start

Read the following guides before proceeding:

GuideWhy
Getting StartedPlatform overview and setup
Api BasicsRequired headers and request configuration
AuthenticationHow to obtain access tokens
OnboardingUser and wallet registration
KYCKYC 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
  1. Wirex oracle initiates the transaction
  2. FundsManagement transfers unified tokens from user's wallet
  3. Tokens arrive in FundsBuffer (Wirex treasury)
  4. 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)
  1. Wirex initiates the credit
  2. Tokens transfer from FundsBuffer to user's wallet
  3. Balance restored to user

Tokens Used

TokenChainDescription
WUSDBaseUnified USD stablecoin (18 decimals)
WEURBaseUnified 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

StageDescriptionOn-Chain
InitiatedTransaction request receivedNo
CryptoOutTokens debited from user walletYes
CardOutCard network processing completeNo
CompletedTransaction finalizedNo
ReversalTokens returned (failure, partial refund, or full refund)Yes

Credit Transaction Stages

For refunds and reversals:

StageDescriptionOn-Chain
InitiatedCredit request receivedNo
CardInCard network processingNo
CryptoInTokens credited to user walletYes
CompletedCredit finalizedNo

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

FieldDescription
idUnique activity identifier
user_addressUser's EOA address
sourceTransaction source (card details)
destinationTransaction destination (merchant details)
statusOverall transaction status
status_reasonReason for status (set on decline/failure)
source_amountAmount debited (crypto for outbound, fiat for inbound)
destination_amountAmount received (fiat for outbound, crypto for inbound)
typeCardTransaction
directionOutbound (debit) or Inbound (credit)
operationsOn-chain operations with transaction hashes
activity_stepsOrdered list of transaction stages

Amount Fields by Direction

The source_amount and destination_amount fields contain different asset types depending on transaction direction:

Directionsource_amountdestination_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:

DirectionAmount SignExample
Outbound (debit)Negative-50.00
Inbound (credit)Positive50.00

This allows you to distinguish the transaction direction from the operation data alone without checking the direction field.

Transaction Status

StatusDescription
PendingTransaction in progress
CompletedTransaction successful
FailedTransaction declined or reversed

Status Reasons

When status is Failed, status_reason indicates why:

ReasonDescription
InsufficientFundsUser balance too low
DailySpendAmountIsExceededDaily card spending limit exceeded
GeneralErrorProcessing 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 Reversal step
  • A new operation with positive operation_amount is added to the operations array
  • 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 + status to deduplicate

Timing

  • CryptoOut/CryptoIn steps 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[].hash to query the exact on-chain state