Overview
Card lifecycle management gives users control over their card’s operational state. The primary operations are freezing (temporarily disabling) and unfreezing (reactivating) cards for security purposes.
Freezing a card is a temporary, reversible action. Users can freeze and unfreeze their cards as many times as needed.
Card States
Understanding card states is essential for proper lifecycle management:
Active Card is operational and can process transactions
Frozen Card is temporarily disabled by user
Blocked Card is permanently disabled by system
State Transitions
State Details
Description : Normal operational state. Card can be used for all transactions.Allowed Operations :
Make purchases (online, in-store)
View card details
View/set PIN
Check transaction history
Freeze card
User Actions :
✅ Use card for purchases
✅ Freeze card if needed
✅ View card details and PIN
✅ Change PIN
Description : Temporarily disabled by user for security. Reversible.Allowed Operations :
View card details (read-only)
Check transaction history
Unfreeze card
Blocked Operations :
❌ Make purchases (all declined)
❌ Change PIN
❌ ATM withdrawals
Common Use Cases :
Lost wallet (not sure if card is missing)
Traveling and not using card
Suspicious activity detected
Temporary security measure
User Actions :
✅ Unfreeze when ready to use again
❌ Cannot make purchases while frozen
Description : Permanently disabled by system. Not reversible.Allowed Operations :
View transaction history
Request replacement card
Blocked Operations :
❌ Make purchases
❌ Unfreeze card
❌ Change PIN
❌ All card operations
Common Causes :
Fraud detection triggered
Multiple failed PIN attempts
Compliance/KYC issues
Card reported as lost/stolen (permanent)
Regulatory requirement
Resolution :
Contact support to understand reason
Resolve underlying issue (if applicable)
Request replacement card
Freezing a Card
Temporarily disable a card to prevent unauthorized transactions.
When to Freeze
Wallet misplaced (not confirmed lost)
Suspicious transaction notification
Traveling and not using card
Taking a break from spending
Device with saved card details lost
API Request
JavaScript/Node.js
Python
cURL
const response = await fetch ( 'https://dev.api.baanx.com/v1/card/freeze' , {
method: 'POST' ,
headers: {
'X-Client-ID' : 'your_client_id' ,
'Authorization' : `Bearer ${ userAccessToken } `
}
});
const result = await response . json ();
if ( result . success ) {
console . log ( 'Card frozen successfully' );
// Update UI to show frozen state
}
Response
Success (200)
Error (400)
Error (404)
Card has been frozen. All transaction attempts will now be declined until the card is unfrozen. {
"message" : "Card is already frozen" ,
"code" : "INVALID_STATE"
}
Card is already in frozen state. No action needed. {
"message" : "Card not found"
}
User doesn’t have a card. Direct them to order a card first.
Implementation Example
React Component
Flask Backend
import { useState } from 'react' ;
export function CardFreezeButton ({ clientId , accessToken , currentStatus }) {
const [ loading , setLoading ] = useState ( false );
const [ status , setStatus ] = useState ( currentStatus );
const [ error , setError ] = useState ( null );
async function freezeCard () {
setLoading ( true );
setError ( null );
try {
const response = await fetch ( '/v1/card/freeze' , {
method: 'POST' ,
headers: {
'X-Client-ID' : clientId ,
'Authorization' : `Bearer ${ accessToken } `
}
});
if ( ! response . ok ) {
const errorData = await response . json ();
throw new Error ( errorData . message || 'Failed to freeze card' );
}
const result = await response . json ();
if ( result . success ) {
setStatus ( 'FROZEN' );
// Optionally show success notification
}
} catch ( err ) {
setError ( err . message );
} finally {
setLoading ( false );
}
}
if ( status === 'FROZEN' ) {
return (
< div className = "card-frozen" >
< span > 🥶 Card is frozen </ span >
</ div >
);
}
return (
< div >
< button
onClick = { freezeCard }
disabled = { loading || status !== 'ACTIVE' }
className = "btn-freeze"
>
{ loading ? 'Freezing...' : 'Freeze Card' }
</ button >
{ error && (
< div className = "error-message" >
{ error }
</ div >
) }
</ div >
);
}
Unfreezing a Card
Reactivate a frozen card to resume normal operations.
When to Unfreeze
Suspicious activity resolved
API Request
JavaScript/Node.js
Python
cURL
const response = await fetch ( 'https://dev.api.baanx.com/v1/card/unfreeze' , {
method: 'POST' ,
headers: {
'X-Client-ID' : 'your_client_id' ,
'Authorization' : `Bearer ${ userAccessToken } `
}
});
const result = await response . json ();
if ( result . success ) {
console . log ( 'Card unfrozen - ready to use' );
}
Response
Success (200)
Error (400)
Error (404)
Card has been unfrozen and is now active. Transactions will be authorized normally. {
"message" : "Card is not frozen" ,
"code" : "INVALID_STATE"
}
Card is not in frozen state (may be active or blocked). Check current status. {
"message" : "Card not found"
}
User doesn’t have a card.
Implementation Example
React Component
Flask Backend
import { useState } from 'react' ;
export function CardUnfreezeButton ({ clientId , accessToken , currentStatus }) {
const [ loading , setLoading ] = useState ( false );
const [ status , setStatus ] = useState ( currentStatus );
const [ error , setError ] = useState ( null );
async function unfreezeCard () {
setLoading ( true );
setError ( null );
try {
const response = await fetch ( '/v1/card/unfreeze' , {
method: 'POST' ,
headers: {
'X-Client-ID' : clientId ,
'Authorization' : `Bearer ${ accessToken } `
}
});
if ( ! response . ok ) {
const errorData = await response . json ();
throw new Error ( errorData . message || 'Failed to unfreeze card' );
}
const result = await response . json ();
if ( result . success ) {
setStatus ( 'ACTIVE' );
// Show success notification
}
} catch ( err ) {
setError ( err . message );
} finally {
setLoading ( false );
}
}
if ( status === 'ACTIVE' ) {
return (
< div className = "card-active" >
< span > ✅ Card is active </ span >
</ div >
);
}
if ( status === 'BLOCKED' ) {
return (
< div className = "card-blocked" >
< span > 🚫 Card is blocked - contact support </ span >
</ div >
);
}
return (
< div >
< button
onClick = { unfreezeCard }
disabled = { loading || status !== 'FROZEN' }
className = "btn-unfreeze"
>
{ loading ? 'Unfreezing...' : 'Unfreeze Card' }
</ button >
{ error && (
< div className = "error-message" >
{ error }
</ div >
) }
</ div >
);
}
Complete Card Control Component
Here’s a comprehensive component that handles all card states:
import { useState , useEffect } from 'react' ;
export function CardControl ({ clientId , accessToken }) {
const [ card , setCard ] = useState ( null );
const [ loading , setLoading ] = useState ( false );
const [ error , setError ] = useState ( null );
useEffect (() => {
fetchCardStatus ();
}, []);
async function fetchCardStatus () {
try {
const response = await fetch ( '/v1/card/status' , {
headers: {
'X-Client-ID' : clientId ,
'Authorization' : `Bearer ${ accessToken } `
}
});
if ( response . ok ) {
const cardData = await response . json ();
setCard ( cardData );
}
} catch ( err ) {
console . error ( 'Failed to fetch card status:' , err );
}
}
async function toggleFreeze () {
setLoading ( true );
setError ( null );
try {
const endpoint = card . status === 'ACTIVE' ? 'freeze' : 'unfreeze' ;
const response = await fetch ( `/v1/card/ ${ endpoint } ` , {
method: 'POST' ,
headers: {
'X-Client-ID' : clientId ,
'Authorization' : `Bearer ${ accessToken } `
}
});
if ( ! response . ok ) {
const errorData = await response . json ();
throw new Error ( errorData . message );
}
// Refresh card status
await fetchCardStatus ();
} catch ( err ) {
setError ( err . message );
} finally {
setLoading ( false );
}
}
if ( ! card ) {
return < div > Loading card details... </ div > ;
}
return (
< div className = "card-control" >
< div className = "card-info" >
< h3 > Card ending in { card . panLast4 } </ h3 >
< CardStatusBadge status = { card . status } />
</ div >
{ card . status === 'BLOCKED' ? (
< div className = "blocked-message" >
< p > ⚠️ This card has been blocked and cannot be used. </ p >
< p > Please contact support for assistance. </ p >
< button > Contact Support </ button >
</ div >
) : (
< div className = "card-actions" >
< button
onClick = { toggleFreeze }
disabled = { loading }
className = { card . status === 'ACTIVE' ? 'btn-freeze' : 'btn-unfreeze' }
>
{ loading
? 'Processing...'
: card . status === 'ACTIVE'
? '❄️ Freeze Card'
: '✅ Unfreeze Card'
}
</ button >
< p className = "help-text" >
{ card . status === 'ACTIVE'
? 'Temporarily disable your card for security'
: 'Reactivate your card to resume using it'
}
</ p >
</ div >
) }
{ error && (
< div className = "error-message" >
{ error }
</ div >
) }
</ div >
);
}
function CardStatusBadge ({ status }) {
const statusConfig = {
ACTIVE: { icon: '✅' , color: 'green' , text: 'Active' },
FROZEN: { icon: '❄️' , color: 'blue' , text: 'Frozen' },
BLOCKED: { icon: '🚫' , color: 'red' , text: 'Blocked' }
};
const config = statusConfig [ status ] || statusConfig . ACTIVE ;
return (
< span className = { `status-badge status- ${ config . color } ` } >
{ config . icon } { config . text }
</ span >
);
}
Security Best Practices
Confirmation Dialogs
Always confirm destructive or significant actions:
async function freezeCard () {
const confirmed = await showConfirmDialog ({
title: 'Freeze Card?' ,
message: 'Your card will be temporarily disabled. You can unfreeze it anytime.' ,
confirmText: 'Freeze Card' ,
cancelText: 'Cancel'
});
if ( ! confirmed ) return ;
// Proceed with freeze
}
Audit Trail
Log all state changes for security and support:
async function logCardStateChange ( action , cardId , userId ) {
await fetch ( '/api/audit/log' , {
method: 'POST' ,
body: JSON . stringify ({
event: `card_ ${ action } ` ,
cardId ,
userId ,
timestamp: new Date (). toISOString (),
userAgent: navigator . userAgent
})
});
}
Notification
Notify users of all card state changes:
async function notifyCardStateChange ( action , userEmail ) {
await fetch ( '/api/notifications/send' , {
method: 'POST' ,
body: JSON . stringify ({
to: userEmail ,
template: `card_ ${ action } ` ,
data: {
timestamp: new Date (). toISOString (),
action: action === 'freeze' ? 'frozen' : 'unfrozen'
}
})
});
}
Freeze vs Block: When to Use Each
User initiates freeze when:
Wallet temporarily misplaced
Suspicious activity noticed
Traveling without plans to use card
Taking break from spending
Device with saved card lost
Characteristics:
✅ Reversible by user anytime
✅ No support contact needed
✅ Instant activation/deactivation
✅ Can freeze/unfreeze repeatedly
System blocks card when:
Fraud detection triggered
Multiple failed PIN attempts (3+)
Card reported lost/stolen (permanent)
Compliance/regulatory issue
Chargeback fraud detected
Characteristics:
❌ Not reversible by user
⚠️ Requires support contact
🔍 Investigation may be needed
🔄 Replacement card required
User Experience Guidelines
Status Visibility
Always display current card status prominently:
┌─────────────────────────────────────┐
│ Virtual Card │
│ •••• •••• •••• 1234 │
│ │
│ Status: [❄️ FROZEN] │
│ │
│ Last 4 transactions declined │
│ [Unfreeze Card] │
└─────────────────────────────────────┘
Action Feedback
Provide immediate visual feedback:
Freeze : Show snowflake icon, blue color, disable purchase buttons
Unfreeze : Show checkmark, green color, enable purchase buttons
Blocked : Show stop icon, red color, display support contact
State Persistence
Remember user’s last action to prevent confusion:
// Store last action in local storage
localStorage . setItem ( 'lastCardAction' , JSON . stringify ({
action: 'freeze' ,
timestamp: Date . now (),
cardId: card . id
}));
Troubleshooting
Freeze succeeds but transactions still authorize
Cause : Processing delay or cached card stateSolution :
Wait 5-10 seconds for state propagation
Verify status with GET /v1/card/status
If issue persists after 1 minute, contact support
Cannot unfreeze card (400 error)
Cause : Card may be blocked instead of frozenSolution :const card = await fetch ( '/v1/card/status' );
if ( card . status === 'BLOCKED' ) {
// Show blocked message and support contact
showBlockedCardMessage ();
}
User accidentally freezes card
Cause : No confirmation dialog implementedSolution : Always show confirmation before state changesif ( action === 'freeze' ) {
const confirmed = confirm (
'Freeze card? All transactions will be declined until you unfreeze it.'
);
if ( ! confirmed ) return ;
}
Frozen card still shows as active in UI
Cause : UI not updated after freezeSolution : Refresh card status after state changeawait fetch ( '/v1/card/freeze' , { method: 'POST' });
// Refresh status
const updatedCard = await fetch ( '/v1/card/status' );
setCard ( updatedCard );
Rate Limiting
Freeze/unfreeze operations are rate limited to prevent abuse:
Maximum 10 freeze/unfreeze operations per hour
Maximum 50 operations per day
Exceeding limits results in 429 Too Many Requests
Handle rate limiting gracefully:
async function freezeCard () {
try {
const response = await fetch ( '/v1/card/freeze' , { method: 'POST' });
if ( response . status === 429 ) {
const retryAfter = response . headers . get ( 'Retry-After' );
throw new Error (
`Too many requests. Please try again in ${ retryAfter } seconds.`
);
}
} catch ( err ) {
showError ( err . message );
}
}
Next Steps