Your redemption failures stem from three interconnected issues in Loyalty Management API integration: transaction processing timing, balance calculation methodology, and API call sequencing. Here’s the complete solution:
1. Loyalty Management API Integration - Configure Real-Time Processing:
The 5-10 minute delay indicates batch processing mode. Reconfigure your Loyalty Program for real-time transaction processing:
In Salesforce Setup → Loyalty Management Settings → Loyalty Programs:
- Select your program
- Set Processing Mode: Real-time (not Batch)
- Set Transaction Processing: Immediate
- Enable Real-time Balance Updates: true
For API-driven point accruals, always include the immediate processing flag:
POST /loyalty/transactions
{
"memberId": "LM-00123456",
"points": 100,
"processImmediately": true,
"journalType": "Accrual"
}
This ensures points are immediately available for redemption rather than queued for batch processing.
2. Real-Time Balance Synchronization - Implement Atomic Operations:
Your current two-step process (check balance, then redeem) creates a race condition. Replace with atomic redemption:
Current Problematic Pattern:
// Step 1: Check balance
const balance = await GET('/loyalty/members/{id}/balance');
if (balance.available >= requestedPoints) {
// Step 2: Redeem (might fail if balance changed)
await POST('/loyalty/redemptions', {points: requestedPoints});
}
Correct Atomic Pattern:
// Single atomic operation
const redemption = await POST('/loyalty/redemptions', {
memberId: 'LM-00123456',
points: 500,
validateBalance: true,
failOnInsufficientBalance: true
});
The Loyalty API handles balance validation within the transaction, eliminating race conditions.
3. Redemption Error Handling - Distinguish Balance Types:
Loyalty Management tracks multiple balance types:
- Total Balance: All earned points
- Available Balance: Points eligible for redemption
- Pending Balance: Points awaiting processing
- Reserved Balance: Points in pending redemption transactions
Your API calls must query the correct balance:
GET /loyalty/members/{id}/balance?balanceType=AVAILABLE
Not:
GET /loyalty/members/{id}/balance // Returns total, not available
Implementation Architecture:
Phase 1: Point Accrual (E-commerce → Salesforce)
// When customer completes purchase
async function awardLoyaltyPoints(orderId, customerId, points) {
const response = await fetch('/loyalty/transactions', {
method: 'POST',
headers: {
'X-Request-Id': generateIdempotencyKey(orderId),
'Content-Type': 'application/json'
},
body: JSON.stringify({
memberId: customerId,
points: points,
transactionType: 'Purchase',
orderId: orderId,
processImmediately: true,
journalSubType: 'Purchase'
})
});
// Wait for processing confirmation
await pollTransactionStatus(response.transactionId);
}
Phase 2: Balance Check (Checkout)
async function getRedemptionBalance(memberId) {
const response = await fetch(
`/loyalty/members/${memberId}/balance?balanceType=AVAILABLE&includeExpiring=true`,
{
method: 'GET',
headers: {'Cache-Control': 'no-cache'}
}
);
return {
available: response.availableBalance,
pending: response.pendingBalance,
expiringSoon: response.expiringBalance,
expiryDate: response.nextExpiryDate
};
}
Phase 3: Redemption (Atomic)
async function redeemPoints(memberId, points, orderId) {
try {
const response = await fetch('/loyalty/redemptions', {
method: 'POST',
headers: {
'X-Request-Id': generateIdempotencyKey(`redeem-${orderId}`),
'Content-Type': 'application/json'
},
body: JSON.stringify({
memberId: memberId,
points: points,
orderId: orderId,
validateBalance: true,
failOnInsufficientBalance: true,
redemptionType: 'Checkout'
})
});
return {success: true, redemptionId: response.id};
} catch (error) {
if (error.code === 'INSUFFICIENT_LOYALTY_BALANCE') {
// Fetch current balance for error message
const currentBalance = await getRedemptionBalance(memberId);
return {
success: false,
error: 'insufficient_balance',
requested: points,
available: currentBalance.available,
pending: currentBalance.pending
};
}
throw error;
}
}
Advanced Considerations:
Handling Concurrent Redemptions:
If a customer attempts multiple redemptions simultaneously (e.g., multiple browser tabs):
// Use optimistic locking
POST /loyalty/redemptions
{
"memberId": "LM-00123456",
"points": 500,
"lockBalance": true, // Prevents concurrent redemptions
"lockTimeout": 60 // Seconds
}
Transaction Reversal:
Implement reversal logic for failed checkouts:
if (checkoutFailed) {
// Reverse redemption
POST /loyalty/transactions
{
"transactionType": "Reversal",
"originalTransactionId": redemptionId,
"processImmediately": true
}
}
Expiring Points Awareness:
Warn customers about expiring points during checkout:
if (balance.expiringBalance > 0 && balance.nextExpiryDate < 30days) {
displayWarning(`${balance.expiringBalance} points expiring on ${balance.nextExpiryDate}`);
}
Performance Optimization:
- Cache Balance Strategically: Cache available balance for 30 seconds max, refresh before redemption
- Batch Balance Queries: If showing multiple customers’ balances, use composite API
- Webhook Integration: Subscribe to loyalty balance change events instead of polling
- Connection Pooling: Maintain persistent connections to Salesforce API
Monitoring and Alerting:
Implement comprehensive monitoring:
- Track redemption failure rate by error type
- Alert when pending balance exceeds 10% of total balance (indicates processing delays)
- Monitor average time from accrual to availability
- Track idempotency key collision rate
Customer Experience Improvements:
UI Transparency:
// Display in checkout
{
"Total Points": 850,
"Available Now": 450,
"Processing": 400,
"Can Redeem": 450
}
Graceful Degradation:
If real-time balance is unavailable:
if (balanceAPIDown) {
// Allow redemption up to last known balance
// Flag for manual reconciliation
processRedemptionWithFallback();
}
Testing Checklist:
- Test immediate redemption after point accrual
- Verify concurrent redemption handling
- Test redemption during point expiry
- Validate reversal scenarios
- Test with batch processing disabled
- Verify idempotency key behavior
- Test balance calculation with multiple transaction types
By implementing real-time processing, atomic redemption operations, and proper balance type handling, you’ll eliminate the synchronization delays and race conditions causing failed redemptions. The key is treating redemption as a single atomic transaction rather than separate check-and-redeem operations.