1House Global API Documentation

Wallet

Manage wallets, transactions, and exchange rates for Keys currency

Manage user wallets, track transactions, and configure exchange rates for the Keys currency.

Keys Currency

The platform uses "Keys" as its internal currency. Keys are always whole numbers (integers) - users see "1 Key", "2 Keys", etc. Each Key has a configurable value in USD (default: 1 Key = 1 USD). Exchange rates can be adjusted by administrators, and the USD value is calculated on the backend.

Overview

The Wallet API provides:

  • Wallet balance management
  • Transaction history (ledger)
  • Key transfers between wallets
  • Wallet funding
  • Exchange rate management
  • USD conversion utilities

Base Paths:

  • Wallets: /v1/wallets
  • Exchange Rates: /v1/exchange-rates

Keys are Always Integers

Important: Keys are always whole numbers (integers). Users see "1 Key", "2 Keys", etc. on the frontend. The backend calculates the USD equivalent using the exchange rate. All amounts in API requests and responses for Keys must be integers (e.g., 100, not 100.50).

Wallet Management

Create Wallet

Create a new wallet for the authenticated user. If a wallet already exists, it will be returned instead of creating a duplicate.

POST /v1/wallets
X-API-Key: your-api-key
Authorization: Bearer your-jwt-token
# Development
curl -X POST "https://api-gateway.dev.1houseglobalservices.com/v1/wallets" \
  -H "X-API-Key: your-development-api-key" \
  -H "Authorization: Bearer your-jwt-token"

# Production
curl -X POST "https://api-gateway.prod.1houseglobalservices.com/v1/wallets" \
  -H "X-API-Key: your-production-api-key" \
  -H "Authorization: Bearer your-jwt-token"
// Development
const DEV_API_URL = 'https://api-gateway.dev.1houseglobalservices.com';
const DEV_API_KEY = 'your-development-api-key';

// Production
const PROD_API_URL = 'https://api-gateway.prod.1houseglobalservices.com';
const PROD_API_KEY = 'your-production-api-key';

// Use appropriate environment
const apiUrl = process.env.NODE_ENV === 'production' ? PROD_API_URL : DEV_API_URL;
const apiKey = process.env.NODE_ENV === 'production' ? PROD_API_KEY : DEV_API_KEY;

const response = await fetch(`${apiUrl}/v1/wallets`, {
  method: 'POST',
  headers: {
    'X-API-Key': apiKey,
    'Authorization': `Bearer ${token}`
  }
});
const data = await response.json();
import requests
import os

# Development
DEV_API_URL = 'https://api-gateway.dev.1houseglobalservices.com'
DEV_API_KEY = 'your-development-api-key'

# Production
PROD_API_URL = 'https://api-gateway.prod.1houseglobalservices.com'
PROD_API_KEY = 'your-production-api-key'

# Use appropriate environment
api_url = PROD_API_URL if os.getenv('ENVIRONMENT') == 'production' else DEV_API_URL
api_key = PROD_API_KEY if os.getenv('ENVIRONMENT') == 'production' else DEV_API_KEY

response = requests.post(
    f'{api_url}/v1/wallets',
    headers={
        'X-API-Key': api_key,
        'Authorization': f'Bearer {token}'
    }
)
data = response.json()

Try it out:

{
  "success": true,
  "status": 201,
  "message": "Wallet created successfully",
  "data": {
    "_id": "wallet_123",
    "userId": "user_456",
    "balance": 0,
    "currency": "Keys",
    "isActive": true,
    "locked": false,
    "createdAt": "2025-10-21T12:00:00.000Z",
    "updatedAt": "2025-10-21T12:00:00.000Z"
  },
  "meta": {
    "timestamp": "2025-10-21T12:00:00.000Z",
    "version": "v1"
  }
}

Idempotent Operation

If a wallet already exists for the user, the existing wallet will be returned instead of creating a duplicate. This makes the endpoint safe to call multiple times.

Get Wallet Balance

Retrieve the current balance for the authenticated user's wallet.

GET /v1/wallets/balance
X-API-Key: your-api-key
Authorization: Bearer your-jwt-token

Try it out:

# Development
curl "https://api-gateway.dev.1houseglobalservices.com/v1/wallets/balance" \
  -H "X-API-Key: your-development-api-key" \
  -H "Authorization: Bearer your-jwt-token"

# Production
curl "https://api-gateway.prod.1houseglobalservices.com/v1/wallets/balance" \
  -H "X-API-Key: your-production-api-key" \
  -H "Authorization: Bearer your-jwt-token"
// Development
const DEV_API_URL = 'https://api-gateway.dev.1houseglobalservices.com';
const DEV_API_KEY = 'your-development-api-key';

// Production
const PROD_API_URL = 'https://api-gateway.prod.1houseglobalservices.com';
const PROD_API_KEY = 'your-production-api-key';

// Use appropriate environment
const apiUrl = process.env.NODE_ENV === 'production' ? PROD_API_URL : DEV_API_URL;
const apiKey = process.env.NODE_ENV === 'production' ? PROD_API_KEY : DEV_API_KEY;

const response = await fetch(`${apiUrl}/v1/wallets/balance`, {
  headers: {
    'X-API-Key': apiKey,
    'Authorization': `Bearer ${token}`
  }
});
const data = await response.json();
import requests
import os

# Development
DEV_API_URL = 'https://api-gateway.dev.1houseglobalservices.com'
DEV_API_KEY = 'your-development-api-key'

# Production
PROD_API_URL = 'https://api-gateway.prod.1houseglobalservices.com'
PROD_API_KEY = 'your-production-api-key'

# Use appropriate environment
api_url = PROD_API_URL if os.getenv('ENVIRONMENT') == 'production' else DEV_API_URL
api_key = PROD_API_KEY if os.getenv('ENVIRONMENT') == 'production' else DEV_API_KEY

response = requests.get(
    f'{api_url}/v1/wallets/balance',
    headers={
        'X-API-Key': api_key,
        'Authorization': f'Bearer {token}'
    }
)
data = response.json()
{
  "success": true,
  "status": 200,
  "message": "Balance retrieved successfully",
  "data": {
    "balance": 150,
    "balanceUSD": 150.00,
    "currency": "Keys",
    "locked": false,
    "isActive": true
  },
  "meta": {
    "timestamp": "2025-10-21T12:00:00.000Z",
    "version": "v1"
  }
}

Keys are Whole Numbers

The balance field represents the number of Keys (always an integer). The balanceUSD field shows the USD equivalent calculated using the current exchange rate. Users see Keys as whole numbers (1 Key, 2 Keys, etc.), while the backend calculates the USD value based on the exchange rate.

Fund Wallet (Generate Checkout URL)

Creates a Stripe checkout session for the user to purchase Keys. Returns a paymentLink URL that redirects the user to Stripe's hosted checkout page.

Checkout Flow

  1. Client calls POST /v1/wallets/fund with amount and redirect URLs
  2. Backend creates a Stripe checkout session via merchant service
  3. Returns paymentLink URL
  4. Redirect user to paymentLink to complete payment
  5. On success, user is redirected to successUrl and Keys are added to wallet
POST /v1/wallets/fund

Request Body:

FieldTypeRequiredDescription
amountintegerYesNumber of Keys to purchase (must be positive integer)
successUrlstringNoURL to redirect after successful payment
cancelUrlstringNoURL to redirect if payment is cancelled
metadataobjectNoAdditional metadata to attach to the transaction
{
  "amount": 100,
  "successUrl": "https://1houseglobal.com/payment/success",
  "cancelUrl": "https://1houseglobal.com/payment/cancel",
  "metadata": {
    "source": "mobile_app",
    "campaign": "new_user_bonus"
  }
}

Keys are Integers

The amount must be a whole number (integer). Keys cannot be fractional (e.g., 100 Keys, not 100.5 Keys).

Response:

{
  "success": true,
  "status": 201,
  "message": "Checkout session created successfully",
  "data": {
    "checkoutSessionId": "cs_live_abc123xyz",
    "paymentLink": "https://checkout.stripe.com/c/pay/cs_live_abc123xyz",
    "expiresAt": "2026-01-05T11:00:00.000Z",
    "keysAmount": 100,
    "usdAmount": 10.00,
    "currency": "USD"
  },
  "meta": {
    "timestamp": "2026-01-05T10:00:00.000Z",
    "version": "v1"
  }
}

Response Fields:

FieldTypeDescription
checkoutSessionIdstringStripe checkout session ID
paymentLinkstringURL to redirect user for payment
expiresAtstringWhen the checkout session expires (typically 24 hours)
keysAmountintegerNumber of Keys being purchased
usdAmountnumberUSD amount to be charged
currencystringCurrency code (USD)

Usage Example (React Native):

import { walletService } from '@/services/wallet.service';
import { Linking } from 'react-native';

async function handleFundWallet(keysAmount: number) {
  const result = await walletService.fundWallet({
    amount: keysAmount,
    successUrl: 'https://1houseglobal.com/payment/success',
    cancelUrl: 'https://1houseglobal.com/payment/cancel'
  });
  
  if (result?.paymentLink) {
    // Open Stripe checkout in browser
    await Linking.openURL(result.paymentLink);
  } else {
    Alert.alert('Error', 'Failed to create checkout session');
  }
}

Usage Example (Web):

async function fundWallet(amount) {
  const response = await fetch('/v1/wallets/fund', {
    method: 'POST',
    headers: {
      'X-API-Key': apiKey,
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      amount,
      successUrl: `${window.location.origin}/payment/success`,
      cancelUrl: `${window.location.origin}/payment/cancel`
    })
  });
  
  const { data } = await response.json();
  
  if (data.paymentLink) {
    // Redirect to Stripe checkout
    window.location.href = data.paymentLink;
  }
}

Error Responses:

StatusMessageDescription
400Amount must be a positive whole numberInvalid amount provided
401User authentication requiredMissing or invalid auth token
503Payment service unavailableMerchant service is down

Transfer Keys

Transfer Keys from one wallet to another.

POST /v1/wallets/transfer
{
  "toUserId": "user_789",
  "amount": 50,
  "description": "Payment for course",
  "metadata": {
    "courseId": "course_123"
  }
}
{
  "success": true,
  "status": 201,
  "message": "Transfer completed successfully",
  "data": {
    "fromWallet": {
      "userId": "user_456",
      "balance": 200
    },
    "toWallet": {
      "userId": "user_789",
      "balance": 50
    },
    "transaction": {
      "transactionId": "TXN-1697891234-DEF67890",
      "type": "transfer",
      "amount": 50,
      "status": "completed"
    }
  }
}

Initiate Withdrawal

Create a withdrawal request (Keys are locked until processed by merchant service).

POST /v1/wallets/withdraw
{
  "amount": 75,
  "description": "Withdrawal to bank account",
  "metadata": {
    "paymentMethod": "bank_transfer"
  }
}
{
  "success": true,
  "status": 201,
  "message": "Withdrawal initiated successfully",
  "data": {
    "wallet": {
      "balance": 125
    },
    "transaction": {
      "transactionId": "TXN-1697891234-GHI90123",
      "type": "withdrawal",
      "amount": 75,
      "status": "pending"
    }
  }
}

Get Transaction History

Retrieve transaction history for the authenticated user.

GET /v1/wallets/transactions
ParameterTypeDescriptionDefault
pagenumberPage number1
limitnumberResults per page50
typestringFilter by type (fund, transfer, withdrawal)all
statusstringFilter by status (pending, completed, failed)all
startDatestringStart date (ISO 8601)none
endDatestringEnd date (ISO 8601)none
{
  "success": true,
  "status": 200,
  "message": "Transaction history retrieved successfully",
  "data": [
    {
      "transactionId": "TXN-1697891234-ABC12345",
      "type": "fund",
      "amount": 100,
      "status": "completed",
      "description": "Wallet funding",
      "toUserId": "user_456",
      "createdAt": "2025-10-21T10:00:00.000Z"
    },
    {
      "transactionId": "TXN-1697891234-DEF67890",
      "type": "transfer",
      "amount": 50,
      "status": "completed",
      "fromUserId": "user_456",
      "toUserId": "user_789",
      "createdAt": "2025-10-21T11:00:00.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 25,
    "pages": 1
  }
}

Get Transaction Statistics

Get statistics for the authenticated user's transactions.

GET /v1/wallets/statistics
ParameterTypeDescription
startDatestringStart date (ISO 8601)
endDatestringEnd date (ISO 8601)
{
  "success": true,
  "status": 200,
  "data": {
    "totalIn": 500,
    "totalOut": 200,
    "netBalance": 300,
    "totalTransactions": 15,
    "byType": {
      "fund": {
        "count": 2,
        "amount": 500
      },
      "transfer": {
        "count": 10,
        "amount": -150
      },
      "withdrawal": {
        "count": 3,
        "amount": -50
      }
    }
  }
}

Get All Wallets (Admin Only)

Retrieve all wallets in the system (administrator access required).

GET /v1/wallets/all
ParameterTypeDescriptionDefault
pagenumberPage number1
limitnumberResults per page50
lockedbooleanFilter by locked statusall
isActivebooleanFilter by active statusall

Admin Create Wallet (Admin Only)

Create a wallet for a user. This endpoint is restricted to administrators only. If a wallet already exists for the user, it will be returned instead of creating a duplicate.

POST /v1/wallets/admin/create
X-API-Key: your-api-key
Authorization: Bearer your-admin-jwt-token
{
  "userId": "user_789"
}

Admin Only

This endpoint requires administrator privileges. All wallet creation operations are logged with admin information for audit purposes.

# Development
curl -X POST "https://api-gateway.dev.1houseglobalservices.com/v1/wallets/admin/create" \
  -H "X-API-Key: your-development-api-key" \
  -H "Authorization: Bearer your-admin-jwt-token" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "user_789"
  }'

# Production
curl -X POST "https://api-gateway.prod.1houseglobalservices.com/v1/wallets/admin/create" \
  -H "X-API-Key: your-production-api-key" \
  -H "Authorization: Bearer your-admin-jwt-token" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "user_789"
  }'
// Development
const DEV_API_URL = 'https://api-gateway.dev.1houseglobalservices.com';
const DEV_API_KEY = 'your-development-api-key';

// Production
const PROD_API_URL = 'https://api-gateway.prod.1houseglobalservices.com';
const PROD_API_KEY = 'your-production-api-key';

// Use appropriate environment
const apiUrl = process.env.NODE_ENV === 'production' ? PROD_API_URL : DEV_API_URL;
const apiKey = process.env.NODE_ENV === 'production' ? PROD_API_KEY : DEV_API_KEY;

const response = await fetch(`${apiUrl}/v1/wallets/admin/create`, {
  method: 'POST',
  headers: {
    'X-API-Key': apiKey,
    'Authorization': `Bearer ${adminToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    userId: 'user_789'
  })
});
const data = await response.json();
import requests
import os

# Development
DEV_API_URL = 'https://api-gateway.dev.1houseglobalservices.com'
DEV_API_KEY = 'your-development-api-key'

# Production
PROD_API_URL = 'https://api-gateway.prod.1houseglobalservices.com'
PROD_API_KEY = 'your-production-api-key'

# Use appropriate environment
api_url = PROD_API_URL if os.getenv('ENVIRONMENT') == 'production' else DEV_API_URL
api_key = PROD_API_KEY if os.getenv('ENVIRONMENT') == 'production' else DEV_API_KEY

response = requests.post(
    f'{api_url}/v1/wallets/admin/create',
    headers={
        'X-API-Key': api_key,
        'Authorization': f'Bearer {admin_token}',
        'Content-Type': 'application/json'
    },
    json={
        'userId': 'user_789'
    }
)
data = response.json()
{
  "success": true,
  "status": 201,
  "message": "Wallet created successfully by admin",
  "data": {
    "_id": "wallet_789",
    "userId": "user_789",
    "balance": 0,
    "currency": "Keys",
    "isActive": true,
    "locked": false,
    "createdAt": "2025-10-21T12:00:00.000Z",
    "updatedAt": "2025-10-21T12:00:00.000Z"
  },
  "meta": {
    "timestamp": "2025-10-21T12:00:00.000Z",
    "version": "v1"
  }
}

Idempotent Operation

If a wallet already exists for the user, the existing wallet will be returned instead of creating a duplicate. This makes the endpoint safe to call multiple times.

Admin Fund Wallet (Admin Only)

Manually add Keys to a user's wallet. This endpoint is restricted to administrators only and records admin information for audit purposes.

POST /v1/wallets/admin/fund
X-API-Key: your-api-key
Authorization: Bearer your-admin-jwt-token
{
  "userId": "user_789",
  "amount": 100,
  "description": "Promotional bonus",
  "metadata": {
    "reason": "New user welcome bonus",
    "campaign": "summer_2025"
  }
}

Admin Only

This endpoint requires administrator privileges. All funding operations are logged with admin information (adminId, adminEmail) for audit purposes.

Keys are Integers

The amount must be a whole number (integer). Keys cannot be fractional (e.g., 100 Keys, not 100.5 Keys).

# Development
curl -X POST "https://api-gateway.dev.1houseglobalservices.com/v1/wallets/admin/fund" \
  -H "X-API-Key: your-development-api-key" \
  -H "Authorization: Bearer your-admin-jwt-token" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "user_789",
    "amount": 100,
    "description": "Promotional bonus"
  }'

# Production
curl -X POST "https://api-gateway.prod.1houseglobalservices.com/v1/wallets/admin/fund" \
  -H "X-API-Key: your-production-api-key" \
  -H "Authorization: Bearer your-admin-jwt-token" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "user_789",
    "amount": 100,
    "description": "Promotional bonus"
  }'
// Development
const DEV_API_URL = 'https://api-gateway.dev.1houseglobalservices.com';
const DEV_API_KEY = 'your-development-api-key';

// Production
const PROD_API_URL = 'https://api-gateway.prod.1houseglobalservices.com';
const PROD_API_KEY = 'your-production-api-key';

// Use appropriate environment
const apiUrl = process.env.NODE_ENV === 'production' ? PROD_API_URL : DEV_API_URL;
const apiKey = process.env.NODE_ENV === 'production' ? PROD_API_KEY : DEV_API_KEY;

const response = await fetch(`${apiUrl}/v1/wallets/admin/fund`, {
  method: 'POST',
  headers: {
    'X-API-Key': apiKey,
    'Authorization': `Bearer ${adminToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    userId: 'user_789',
    amount: 100, // Integer number of Keys
    description: 'Promotional bonus',
    metadata: {
      reason: 'New user welcome bonus'
    }
  })
});
const data = await response.json();
import requests
import os

# Development
DEV_API_URL = 'https://api-gateway.dev.1houseglobalservices.com'
DEV_API_KEY = 'your-development-api-key'

# Production
PROD_API_URL = 'https://api-gateway.prod.1houseglobalservices.com'
PROD_API_KEY = 'your-production-api-key'

# Use appropriate environment
api_url = PROD_API_URL if os.getenv('ENVIRONMENT') == 'production' else DEV_API_URL
api_key = PROD_API_KEY if os.getenv('ENVIRONMENT') == 'production' else DEV_API_KEY

response = requests.post(
    f'{api_url}/v1/wallets/admin/fund',
    headers={
        'X-API-Key': api_key,
        'Authorization': f'Bearer {admin_token}',
        'Content-Type': 'application/json'
    },
    json={
        'userId': 'user_789',
        'amount': 100,  # Integer number of Keys
        'description': 'Promotional bonus',
        'metadata': {
            'reason': 'New user welcome bonus'
        }
    }
)
data = response.json()
{
  "success": true,
  "status": 201,
  "message": "Wallet funded successfully by admin",
  "data": {
    "wallet": {
      "_id": "wallet_789",
      "userId": "user_789",
      "balance": 100,
      "currency": "Keys",
      "isActive": true,
      "locked": false
    },
    "transaction": {
      "_id": "transaction_123",
      "transactionId": "TXN-1697891234-ADMIN001",
      "type": "fund",
      "amount": 100,
      "status": "completed",
      "description": "Manual funding by admin admin_456",
      "metadata": {
        "adminId": "admin_456",
        "adminEmail": "admin@example.com",
        "manualFunding": true,
        "fundedAt": "2025-10-21T12:00:00.000Z"
      },
      "toUserId": "user_789",
      "createdAt": "2025-10-21T12:00:00.000Z"
    },
    "transactionId": "transaction_123"
  },
  "meta": {
    "timestamp": "2025-10-21T12:00:00.000Z",
    "version": "v1"
  }
}

Exchange Rates

Get Current Exchange Rate

Retrieve the current exchange rate for Keys to USD.

GET /v1/exchange-rates/current
ParameterTypeDescriptionDefault
currencystringCurrency code (USD, EUR, GBP)USD
{
  "success": true,
  "status": 200,
  "data": {
    "_id": "rate_123",
    "rate": 1.0,
    "currency": "USD",
    "isActive": true,
    "effectiveFrom": "2025-10-01T00:00:00.000Z",
    "updatedBy": {
      "email": "admin@example.com"
    }
  }
}

Set Exchange Rate (Admin Only)

Update the exchange rate for Keys (administrator access required).

POST /v1/exchange-rates
{
  "rate": 0.85,
  "currency": "USD",
  "notes": "Market adjustment"
}
{
  "success": true,
  "status": 201,
  "message": "Exchange rate updated successfully",
  "data": {
    "_id": "rate_124",
    "rate": 0.85,
    "currency": "USD",
    "isActive": true,
    "effectiveFrom": "2025-10-21T12:00:00.000Z",
    "effectiveTo": null
  }
}

Get Exchange Rate History

Retrieve historical exchange rate changes.

GET /v1/exchange-rates/history
ParameterTypeDescriptionDefault
currencystringCurrency codeUSD
limitnumberNumber of records50

Get All Active Rates

Retrieve all active exchange rates for different currencies.

GET /v1/exchange-rates/active

Convert Keys to USD

Convert a Keys amount to USD using the current exchange rate.

POST /v1/exchange-rates/convert/keys-to-usd
{
  "keys": 100,
  "currency": "USD"
}
{
  "success": true,
  "status": 200,
  "data": {
    "keys": 100,
    "usd": 100.00,
    "currency": "USD"
  }
}

Keys vs USD

Note that keys is always an integer (whole number), while usd may have decimal places based on the exchange rate calculation.

Convert USD to Keys

Convert a USD amount to Keys using the current exchange rate.

POST /v1/exchange-rates/convert/usd-to-keys
{
  "usd": 100.00,
  "currency": "USD"
}
{
  "success": true,
  "status": 200,
  "data": {
    "usd": 100.00,
    "keys": 100,
    "currency": "USD"
  }
}

Conversion Result

When converting USD to Keys, the result is rounded down to the nearest whole number (integer) since Keys cannot be fractional.

Transaction Types

Transaction Status

StatusDescription
pendingTransaction is pending processing
completedTransaction successfully completed
failedTransaction failed
cancelledTransaction was cancelled

Wallet Object

FieldTypeDescription
userIdstringUser ID (ObjectId)
balanceintegerCurrent balance in Keys (number of Keys, always whole number)
balanceUSDnumberCurrent balance in USD (calculated using exchange rate)
currencystringCurrency code (always "Keys")
isActivebooleanWhether wallet is active
lockedbooleanWhether wallet is locked
lockedReasonstringReason for lock (if locked)
createdAtdatetimeWallet creation date
updatedAtdatetimeLast update date

Transaction Object

FieldTypeDescription
transactionIdstringUnique transaction identifier
fromUserIdstringSource user ID (null for fund/withdrawal)
toUserIdstringDestination user ID
amountintegerTransaction amount in Keys (number of Keys, always whole number)
currencystringCurrency code (always "Keys")
typestringTransaction type (fund, transfer, withdrawal, refund)
statusstringTransaction status
descriptionstringTransaction description
metadataobjectAdditional metadata
feenumberTransaction fee in Keys (if applicable, always integer)
netAmountintegerNet amount after fees in Keys (always whole number)
fromBalanceBeforenumberSender balance before transaction
fromBalanceAfternumberSender balance after transaction
toBalanceBeforenumberRecipient balance before transaction
toBalanceAfternumberRecipient balance after transaction
createdAtdatetimeTransaction creation date
processedAtdatetimeTransaction processing date

Best Practices

1. Always Check Balance Before Transfer

const balance = await getBalance();
const transferKeys = Math.floor(transferAmount); // Ensure integer
if (balance.balance < transferKeys) {
  throw new Error('Insufficient balance');
}

2. Use Metadata for Tracking

{
  "amount": 50,
  "description": "Course purchase",
  "metadata": {
    "courseId": "course_123",
    "courseName": "Forex Trading Masterclass"
  }
}

2a. Keys are Always Integers

// Always use whole numbers for Keys
const keysAmount = Math.floor(userInput); // Round down to nearest integer
if (keysAmount <= 0) {
  throw new Error('Amount must be a positive integer');
}

3. Handle Transaction Status

if (transaction.status === 'pending') {
  // Show pending indicator
} else if (transaction.status === 'completed') {
  // Update UI
} else if (transaction.status === 'failed') {
  // Show error message
}

4. Monitor Exchange Rates

const rate = await getCurrentExchangeRate();
const keysAmount = 100; // Integer number of Keys
const usdValue = await convertKeysToUSD(keysAmount);
// Display: "100 Keys = $100.00 USD" (or current rate)
// Frontend shows: "100 Keys"
// Backend calculates: 100 Keys × 1.0 (exchange rate) = $100.00 USD