API Response Comparison - Before vs After
GET /api/v1/wallet/me
BEFORE (Old System)
User had single wallet regardless of country:
{
"data": {
"_id": "507f1f77bcf86cd799439011",
"user": "507f191e810c19729de860ea",
"availableZishPoints": 150,
"withdrawalBalance": 50,
"ledger": ["..."],
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-02T00:00:00.000Z"
}
}
Issue: User in US could see/use points earned in UK
AFTER (Country-Specific System)
Scenario 1: User in US (has US wallet)
{
"data": {
"_id": "507f1f77bcf86cd799439011",
"user": "507f191e810c19729de860ea",
"country": "US", // ⭐ NEW: Country field
"availableZishPoints": 150,
"withdrawalBalance": 50,
"ledger": ["..."],
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-02T00:00:00.000Z"
}
}
Scenario 2: User changed to UK (no UK wallet yet)
{
"data": null // ⭐ NEW: null when no wallet for current country
}
Scenario 3: User changed to UK (has UK wallet)
{
"data": {
"_id": "507f1f77bcf86cd799439012",
"user": "507f191e810c19729de860ea",
"country": "UK", // ⭐ NEW: Shows UK wallet
"availableZishPoints": 0, // ⭐ NEW: Separate balance
"withdrawalBalance": 0,
"ledger": [],
"createdAt": "2025-01-15T00:00:00.000Z",
"updatedAt": "2025-01-15T00:00:00.000Z"
}
}
Benefit: User only sees balance for current country ✅
POST /api/v1/payments/stripe/webhook (Plan Purchase)
BEFORE
Credits were added to user's single wallet:
// Old code
let wallet = await Wallet.findOne({ user: userId })
wallet.availableZishPoints += points
Issue: points from different countries mixed in one wallet
AFTER
Credits go to country-specific wallet:
// New code
const user = await User.findById(userId).select('address')
const country = String(user?.address?.country || '').toUpperCase() || 'UNKNOWN'
let wallet = await Wallet.findOne({ user: userId, country })
if (!wallet) {
wallet = await Wallet.create({ user: userId, country, ... })
}
wallet.availableZishPoints += points
Benefit: points isolated per country ✅
GET /admin/wallets/:userId
BEFORE
Returned single wallet:
{
"_id": "507f1f77bcf86cd799439011",
"user": "507f191e810c19729de860ea",
"availableZishPoints": 150,
"withdrawalBalance": 50,
"ledger": ["..."]
}
AFTER
Without country query parameter
Returns ALL wallets for user:
{
"user": "507f191e810c19729de860ea",
"wallets": [ // ⭐ NEW: Array of wallets
{
"_id": "507f1f77bcf86cd799439011",
"user": "507f191e810c19729de860ea",
"country": "US", // ⭐ NEW
"availableZishPoints": 150,
"withdrawalBalance": 50,
"ledger": ["..."]
},
{
"_id": "507f1f77bcf86cd799439012",
"user": "507f191e810c19729de860ea",
"country": "UK", // ⭐ NEW
"availableZishPoints": 200,
"withdrawalBalance": 75,
"ledger": ["..."]
},
{
"_id": "507f1f77bcf86cd799439013",
"user": "507f191e810c19729de860ea",
"country": "IN", // ⭐ NEW
"availableZishPoints": 300,
"withdrawalBalance": 100,
"ledger": ["..."]
}
]
}
With country query parameter
Returns specific country wallet:
Request: GET /admin/wallets/:userId?country=US
{
"user": "507f191e810c19729de860ea",
"country": "US",
"availableZishPoints": 150,
"withdrawalBalance": 50,
"ledger": ["..."],
"_virtual": false
}
Benefit: Admin can see all country wallets separately ✅
POST /api/v1/tournaments/:id/join
BEFORE
Debited from single wallet:
// Old code
const wallet = await Wallet.findOne({ user: userId })
wallet.availableZishPoints -= entryFee
Issue: Could use points earned in any country
AFTER
Debits only from current country wallet:
// New code
const user = await User.findById(userId)
const country = String(user?.address?.country || '').toUpperCase() || 'UNKNOWN'
const wallet = await Wallet.findOne({ user: userId, country })
if (!wallet) {
throw new Error("No wallet found for your current country")
}
wallet.availableZishPoints -= entryFee
Error Response (if no wallet for current country):
{
"error": "NOT_FOUND",
"message": "No wallet found for your current country"
}
Benefit: Enforces country isolation ✅
Database Schema Comparison
BEFORE
{
_id: ObjectId,
user: ObjectId, // ref: User
availableZishPoints: Number,
withdrawalBalance: Number,
ledger: [ObjectId], // ref: Ledger
createdAt: Date,
updatedAt: Date
}
// Index: { user: 1 }
// Constraint: One wallet per user
AFTER
{
_id: ObjectId,
user: ObjectId, // ref: User
country: String, // ⭐ NEW: Required, uppercase, default: 'UNKNOWN'
availableZishPoints: Number,
withdrawalBalance: Number,
ledger: [ObjectId], // ref: Ledger
createdAt: Date,
updatedAt: Date
}
// Index: { user: 1, country: 1 } // ⭐ NEW: Compound unique index
// Constraint: One wallet per user per country
Real-World Example: User Journey
Scenario: User travels from US to UK
| Event | Old System | New System |
|---|---|---|
| 1. Register in US | Single wallet created | Wallet created with country: "US" |
| 2. Buy $10 plan in US | Balance: $10 | US wallet: $10, UK wallet: doesn't exist |
| 3. Join tournament in US | Balance: $5 | US wallet: $5, UK wallet: doesn't exist |
| 4. Travel to UK, update profile | Same wallet | New UK wallet created (balance: $0) |
| 5. Check wallet in UK | Shows: $5 (US balance) ❌ | Shows: $0 (UK balance) ✅ |
| 6. Buy £20 plan in UK | Balance: $25 (mixed!) ❌ | US wallet: $5, UK wallet: £20 ✅ |
| 7. Join tournament in UK | Uses mixed balance ❌ | Uses only UK balance ✅ |
| 8. Travel back to US | Shows: mixed balance ❌ | US wallet: $5 (preserved) ✅ |
Migration Impact
For Existing Users
Before Migration:
{
"_id": "...",
"user": "507f191e810c19729de860ea",
"availableZishPoints": 150,
// No country field
}
After Migration:
{
"_id": "...",
"user": "507f191e810c19729de860ea",
"country": "US", // ⭐ Added based on user's current country
"availableZishPoints": 150, // Balance preserved
}
For New Users
All new users get country-specific wallets from the start:
- User registers with country → Wallet created with that country
- User has no country → Wallet created with
country: "UNKNOWN" - User changes country → New wallet created automatically
Backward Compatibility Summary
| Old Wallet State | After Migration | Behavior |
|---|---|---|
| No country field | Country added (user's current) | ✅ Works normally |
| Had balance | Balance preserved | ✅ Balance intact |
| User had no country | country: "UNKNOWN" | ✅ Still accessible |
| Ledger entries | Preserved | ✅ History intact |
Key Takeaways
✅ What Works the Same
- Wallet creation on user registration
- Balance tracking
- Ledger entries
- Admin operations
⭐ What's New
countryfield on every wallet- Multiple wallets per user (one per country)
- API returns null if no wallet for current country
- Admin can see all country wallets
🚫 What's Different
- Users can't access other countries' balances
- Country change creates new wallet (starts at 0)
- Tournament/topup/payout use current country wallet only
- API response structure changed for admin endpoints
🎯 The Goal
Isolation: Keep country finances separate for compliance and clarity ✅