Prepaid Wallet Use Cases
Real-world implementations of the Prepaid Wallet system.
Use Case 1: OpenAI-Style API Billing
Scenario: Customer has prepaid API credits, each API call consumes tokens.
Configuration
1. Wallet Template:
{
"code": "API_CREDITS",
"walletType": "PREPAID",
"lowBalanceLevel": 10.00,
"rejectLevel": 0.00
}
2. Charge Template:
{
"code": "API_TOKEN_USAGE",
"chargeType": "USAGE",
"inputUnitDescription": "tokens",
"ratingUnitDescription": "1K tokens"
}
3. Price Plan:
{
"code": "TOKEN_PRICING",
"chargeTemplates": ["API_TOKEN_USAGE"],
"versions": [{
"price": 0.002,
"validity": { "from": "2024-01-01" }
}]
}
API Call Flow
# 1. Customer tops up wallet (CREDIT operation)
curl -X POST "${BASE_URL}/api/rest/billing/wallet/operation" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '{
"userAccount": "UA_001",
"walletTemplate": "API_CREDITS",
"chargeInstance": "API_CREDIT_CHARGE",
"currency": "USD",
"amountWithTax": 100.00,
"description": "Top-up $100"
}'
# 2. Check balance before API call
curl -X POST "${BASE_URL}/api/rest/billing/wallet/balance/open" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '{
"userAccountCode": "UA_001",
"walletCode": "API_CREDITS"
}'
# Returns: { "amountWithTax": 100.00 }
# 3. Reserve tokens for API call
curl -X POST "${BASE_URL}/api/rest/billing/mediation/reserveCdr" \
-H "Content-Type: text/plain" \
-H "Authorization: Bearer ${TOKEN}" \
-d "2024-01-15T10:30:00;SUB_001;5000;API_TOKENS;GPT-4"
# Returns: { "reservationId": 12345, "availableQuantity": 5000 }
# 4. Make actual OpenAI API call
response=$(curl -X POST "https://api.openai.com/v1/chat/completions" ...)
# 5a. Success - Confirm reservation
if [ $? -eq 0 ]; then
curl -X POST "${BASE_URL}/api/rest/billing/mediation/confirmReservation" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '{"reservationId": 12345}'
fi
# 5b. Failure - Cancel reservation
if [ $? -ne 0 ]; then
curl -X POST "${BASE_URL}/api/rest/billing/mediation/cancelReservation" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '{"reservationId": 12345}'
fi
Flow Diagram
Use Case 2: Telecom Prepaid
Scenario: Mobile subscriber with prepaid voice/SMS/data bundles.
Configuration
UserAccount: MOBILE_USER_001
├── Wallet: VOICE_BUNDLE
│ ├── walletType: PREPAID
│ ├── lowBalanceLevel: 10 (minutes)
│ └── rejectLevel: 0
├── Wallet: SMS_BUNDLE
│ ├── walletType: PREPAID
│ ├── lowBalanceLevel: 50 (SMS)
│ └── rejectLevel: 0
└── Wallet: DATA_PACK
├── walletType: PREPAID
├── lowBalanceLevel: 512 (MB)
└── rejectLevel: 0
Call Flow
# 1. Call initiated - estimate 5 minutes
curl -X POST "${BASE_URL}/api/rest/billing/mediation/reserveCdr" \
-H "Content-Type: text/plain" \
-d "2024-01-15T10:30:00;MOBILE_001;5;VOICE;DOMESTIC"
# Returns: { "reservationId": 456, "availableQuantity": 5 }
# 2. Call ends after 4 minutes - confirm actual usage
curl -X POST "${BASE_URL}/api/rest/billing/mediation/confirmReservation" \
-H "Content-Type: application/json" \
-d '{"reservationId": 456, "actualQuantity": 4}'
# Charges 4 minutes, releases 1 minute
# 3. Check remaining balance
curl -X POST "${BASE_URL}/api/rest/billing/wallet/balance/open" \
-H "Content-Type: application/json" \
-d '{"userAccountCode": "MOBILE_001", "walletCode": "VOICE_BUNDLE"}'
# Returns remaining voice minutes
Use Case 3: IoT Device Consumption
Scenario: IoT devices with metered data consumption.
Configuration
{
"walletTemplate": {
"code": "IOT_DATA_PLAN",
"walletType": "PREPAID",
"lowBalanceLevel": 100,
"rejectLevel": 10,
"lowBalanceLevelEl": "#{device.priorityLevel == 'HIGH' ? 200 : 100}"
}
}
Device Flow
# 1. Device connects - reserve data quota
curl -X POST "${BASE_URL}/api/rest/billing/mediation/reserveCdr" \
-H "Content-Type: text/plain" \
-d "2024-01-15T10:30:00;DEVICE_001;1024;DATA_MB;IOT"
# 2. Periodic usage reporting (every hour)
curl -X POST "${BASE_URL}/api/rest/billing/mediation/chargeCdr" \
-H "Content-Type: text/plain" \
-d "2024-01-15T11:30:00;DEVICE_001;256;DATA_MB;IOT"
# 3. Check if device should be throttled
balance=$(curl -X POST "${BASE_URL}/api/rest/billing/wallet/balance/open" \
-H "Content-Type: application/json" \
-d '{"userAccountCode": "DEVICE_001"}' | jq '.amountWithTax')
if [ $(echo "$balance < 10" | bc) -eq 1 ]; then
echo "Device should be throttled - low balance"
fi
Use Case 4: Gaming Virtual Currency
Scenario: In-game currency with real-money purchases.
Configuration
{
"walletTemplate": {
"code": "GAME_COINS",
"walletType": "PREPAID",
"lowBalanceLevel": 100,
"rejectLevel": 0
},
"priceMapping": {
"1000_COINS": { "price": 9.99 },
"5000_COINS": { "price": 39.99 },
"10000_COINS": { "price": 69.99 }
}
}
Purchase Flow
# 1. Player buys coin pack
curl -X POST "${BASE_URL}/api/rest/billing/wallet/operation" \
-H "Content-Type: application/json" \
-d '{
"userAccount": "PLAYER_001",
"walletTemplate": "GAME_COINS",
"chargeInstance": "COIN_PURCHASE",
"currency": "USD",
"quantity": 5000,
"amountWithTax": 39.99,
"description": "5000 Coin Pack"
}'
# 2. Player spends coins in-game
curl -X POST "${BASE_URL}/api/rest/billing/mediation/chargeCdr" \
-H "Content-Type: text/plain" \
-d "2024-01-15T15:30:00;PLAYER_001;500;COINS;ITEM_PURCHASE"
# 3. Check balance
curl -X POST "${BASE_URL}/api/rest/billing/wallet/balance/open" \
-H "Content-Type: application/json" \
-d '{"userAccountCode": "PLAYER_001", "walletCode": "GAME_COINS"}'
# Returns: { "quantity": 4500 }
Low Balance Notifications
Configuration
{
"notification": {
"eventType": "LOW_BALANCE",
"classNameFilter": "WalletInstance",
"notificationType": "EMAIL",
"emailTemplate": "low_balance_alert",
"emailToEl": "#{entity.userAccount.billingAccount.email}"
}
}
Email Template
<h2>Low Balance Alert</h2>
<p>Dear #{entity.userAccount.name.fullName},</p>
<p>Your wallet <strong>#{entity.code}</strong> balance is low.</p>
<p>Current balance: <strong>#{entity.balance}</strong></p>
<p>Low balance threshold: <strong>#{entity.lowBalanceLevel}</strong></p>
<p><a href="#{topUpUrl}">Top Up Now</a></p>
Best Practices
1. Set Appropriate Timeout
- API calls: 5 minutes (300000 ms)
- Voice calls: 60 minutes (3600000 ms)
- Data sessions: 24 hours (86400000 ms)
2. Handle Reservation Failures
async function makeApiCall(tokens) {
// Reserve
const reservation = await reserve(tokens);
if (!reservation.success) {
if (reservation.errorCode === 'INSUFFICIENT_BALANCE') {
promptUserToTopUp();
return;
}
throw new Error(reservation.message);
}
try {
// Make call
const result = await externalApiCall();
// Confirm
await confirm(reservation.id);
return result;
} catch (error) {
// Cancel on failure
await cancel(reservation.id);
throw error;
}
}
3. Batch Small Operations
For high-frequency, low-value operations, consider batching:
# Instead of reserving each API call
# Batch reserve for estimated session
curl -X POST "${BASE_URL}/api/rest/billing/mediation/reserveCdr" \
-d "2024-01-15T10:00:00;SUB_001;10000;API_CALLS;SESSION_BATCH"
# At session end, reconcile actual usage
curl -X POST "${BASE_URL}/api/rest/billing/mediation/confirmReservation" \
-d '{"reservationId": 789, "actualQuantity": 8500}'