Merchant Advice Codes
Understand why transactions are declined and determine the appropriate next steps using merchant advice codes in charge responses.
Overviewโ
When a card transaction is declined, Omise provides a merchant_advice field in the charge response. This field contains a standardized message that helps you understand why the transaction failed and what action to take next.
Key Benefits:
- Consistent messaging - Same advice regardless of card scheme (Visa, Mastercard, etc.)
- Actionable guidance - Clear next steps for each decline type
- Improved customer experience - Better error messages for cardholders
- Reduced false declines - Know when to retry vs. when to request new payment method
Understanding the Responseโ
When a charge is declined, the response includes:
{
"object": "charge",
"id": "chrg_test_xxx",
"status": "failed",
"failure_code": "insufficient_fund",
"failure_message": "insufficient fund",
"merchant_advice": "Cardholder should contact their bank or use a different payment method.",
"merchant_advice_code": 3,
"card": {
"brand": "Visa",
"last_digits": "4242"
}
}
| Field | Description |
|---|---|
failure_code | Technical error code |
failure_message | Brief error description |
merchant_advice | Human-readable guidance for merchants |
merchant_advice_code | Numeric code for programmatic handling |
Merchant Advice Codesโ
Common Codes and Actionsโ
| Code | Advice | Description | Recommended Action |
|---|---|---|---|
| 1 | Do not retry | Transaction will never succeed | Request different payment method |
| 2 | Retry later | Temporary issue | Retry after delay (hours/next day) |
| 3 | Contact issuer | Cardholder must resolve with bank | Ask customer to contact their bank |
| 4 | Request new card | Card is invalid/expired | Ask for different card |
| 5 | Retry with 3DS | Authentication required | Retry with 3D Secure enabled |
| 6 | Technical error | System/network issue | Retry immediately or after short delay |
Detailed Code Referenceโ
Code 1: Do Not Retryโ
The transaction cannot succeed with this card. Do not attempt to retry.
Common causes:
- Card reported as lost/stolen
- Account closed
- Fraudulent activity detected
- Card permanently blocked
Action: Request a different payment method from the customer.
if (charge.merchant_advice_code === 1) {
// Remove this card from saved payment methods
await removePaymentMethod(customer.id, card.id);
// Notify customer
await sendNotification(customer, {
message: 'Your card could not be charged. Please add a new payment method.'
});
}
Code 2: Retry Laterโ
The decline is temporary. The transaction may succeed if retried later.
Common causes:
- Insufficient funds (temporary)
- Daily/weekly limit exceeded
- Issuer system maintenance
- High transaction volume
Action: Wait and retry (typically after 24 hours).
if (charge.merchant_advice_code === 2) {
// Schedule retry for tomorrow
await schedulePaymentRetry({
charge_id: charge.id,
retry_at: addHours(new Date(), 24),
max_retries: 3
});
}
Code 3: Contact Issuerโ
The cardholder needs to contact their bank to resolve the issue.
Common causes:
- Security hold on account
- Unusual activity flagged
- Verification required
- Travel notification needed
Action: Inform customer to contact their card issuer.
if (charge.merchant_advice_code === 3) {
await sendNotification(customer, {
message: 'Your payment was declined. Please contact your bank to resolve this issue, then try again.'
});
}
Code 4: Request New Cardโ
The card itself is invalid and cannot be used.
Common causes:
- Expired card
- Invalid card number
- Card not activated
- Cancelled card
Action: Ask customer to use a different card.
if (charge.merchant_advice_code === 4) {
await sendNotification(customer, {
message: 'This card is no longer valid. Please update your payment method.'
});
}
Code 5: Retry with 3D Secureโ
The issuer requires additional authentication.
Common causes:
- SCA required (European cards)
- Issuer policy requires 3DS
- High-value transaction
- First-time transaction
Action: Retry the transaction with 3D Secure enabled.
if (charge.merchant_advice_code === 5) {
// Retry with 3DS
const charge = await omise.charges.create({
amount: originalAmount,
currency: 'thb',
card: card.id,
return_uri: 'https://yoursite.com/3ds-complete',
// 3DS will be triggered automatically
});
// Redirect to 3DS authentication
if (charge.authorize_uri) {
redirect(charge.authorize_uri);
}
}
Code 6: Technical Errorโ
A system or network error occurred.
Common causes:
- Network timeout
- Issuer system unavailable
- Processing error
- Gateway connectivity issue
Action: Retry immediately or after a short delay.
if (charge.merchant_advice_code === 6) {
// Immediate retry
const retryCharge = await omise.charges.create({
amount: charge.amount,
currency: charge.currency,
card: charge.card.id
});
}
Implementation Exampleโ
Payment Retry Systemโ
const omise = require('omise')({
secretKey: process.env.OMISE_SECRET_KEY
});
async function processPayment(customerId, amount, cardId) {
try {
const charge = await omise.charges.create({
amount: amount,
currency: 'thb',
card: cardId,
metadata: {
customer_id: customerId,
attempt: 1
}
});
if (charge.status === 'successful') {
return { success: true, charge };
}
// Handle declined transaction
return handleDecline(charge, customerId);
} catch (error) {
return { success: false, error: error.message };
}
}
async function handleDecline(charge, customerId) {
const code = charge.merchant_advice_code;
switch (code) {
case 1: // Do not retry
await notifyCustomer(customerId, 'payment_failed_permanent', {
message: charge.merchant_advice
});
await flagCardForRemoval(charge.card.id);
return {
success: false,
action: 'request_new_payment_method',
message: charge.merchant_advice
};
case 2: // Retry later
await scheduleRetry(charge, customerId, { delay: '24h' });
return {
success: false,
action: 'retry_scheduled',
retry_at: getNextRetryTime()
};
case 3: // Contact issuer
await notifyCustomer(customerId, 'contact_bank', {
message: charge.merchant_advice
});
return {
success: false,
action: 'contact_issuer',
message: 'Please contact your bank to resolve this issue.'
};
case 4: // Request new card
await notifyCustomer(customerId, 'update_card', {
message: charge.merchant_advice
});
return {
success: false,
action: 'update_payment_method',
message: 'This card is no longer valid. Please add a new card.'
};
case 5: // Retry with 3DS
const retryCharge = await omise.charges.create({
amount: charge.amount,
currency: charge.currency,
card: charge.card.id,
return_uri: `https://example.com/3ds-callback/${charge.id}`
});
if (retryCharge.authorize_uri) {
return {
success: false,
action: 'redirect_3ds',
authorize_uri: retryCharge.authorize_uri
};
}
break;
case 6: // Technical error
// Immediate retry
const immediateRetry = await omise.charges.create({
amount: charge.amount,
currency: charge.currency,
card: charge.card.id
});
if (immediateRetry.status === 'successful') {
return { success: true, charge: immediateRetry };
}
// Schedule delayed retry
await scheduleRetry(charge, customerId, { delay: '1h' });
return {
success: false,
action: 'retry_scheduled',
message: 'Temporary issue. We will retry shortly.'
};
default:
return {
success: false,
action: 'unknown',
message: charge.merchant_advice || 'Payment failed. Please try again.'
};
}
}
// Smart retry with exponential backoff
async function scheduleRetry(charge, customerId, options) {
const attempt = (charge.metadata?.attempt || 1) + 1;
const maxAttempts = 4;
if (attempt > maxAttempts) {
await notifyCustomer(customerId, 'payment_failed_final', {
message: 'We were unable to process your payment after multiple attempts.'
});
return;
}
// Exponential backoff: 1h, 4h, 12h
const delays = ['1h', '4h', '12h'];
const delay = delays[Math.min(attempt - 2, delays.length - 1)];
await retryQueue.add({
charge_id: charge.id,
customer_id: customerId,
amount: charge.amount,
currency: charge.currency,
card_id: charge.card.id,
attempt: attempt
}, {
delay: parseDelay(delay)
});
}
Customer-Facing Error Messagesโ
const customerMessages = {
1: 'This payment method cannot be used. Please try a different card.',
2: 'Payment temporarily unavailable. We\'ll try again soon.',
3: 'Please contact your bank to authorize this payment.',
4: 'This card has expired or is invalid. Please use a different card.',
5: 'Additional verification required. You\'ll be redirected to your bank.',
6: 'A technical issue occurred. Please try again.'
};
function getCustomerMessage(merchantAdviceCode, defaultMessage) {
return customerMessages[merchantAdviceCode] || defaultMessage || 'Payment failed. Please try again.';
}
Testingโ
Use the dedicated test card to simulate different merchant advice codes:
Test Card: 4111 1111 1117 0018
The last two digits of the charge amount determine the merchant advice code:
| Amount (THB) | Advice Code | Scenario |
|---|---|---|
| 10001 | 1 | Do not retry |
| 10002 | 2 | Retry later |
| 10003 | 3 | Contact issuer |
| 10004 | 4 | Request new card |
| 10005 | 5 | Retry with 3DS |
| 10006 | 6 | Technical error |
Test Examplesโ
# Test "Do not retry" response
curl https://api.omise.co/charges \
-u skey_test_YOUR_KEY: \
-d "amount=10001" \
-d "currency=thb" \
-d "card=tokn_test_xxx"
# Test "Retry with 3DS" response
curl https://api.omise.co/charges \
-u skey_test_YOUR_KEY: \
-d "amount=10005" \
-d "currency=thb" \
-d "card=tokn_test_xxx"
Best Practicesโ
1. Always Check Merchant Adviceโ
if (charge.status === 'failed') {
// Always check merchant_advice_code for appropriate action
const action = determineAction(charge.merchant_advice_code);
handleDeclinedPayment(charge, action);
}
2. Implement Smart Retry Logicโ
- Code 1: Never retry
- Code 2: Retry after 24 hours, max 3 attempts
- Code 3-4: Wait for customer action
- Code 5: Retry immediately with 3DS
- Code 6: Retry immediately, then back off
3. Log for Analyticsโ
await analytics.track('payment_declined', {
charge_id: charge.id,
failure_code: charge.failure_code,
merchant_advice_code: charge.merchant_advice_code,
card_brand: charge.card.brand,
amount: charge.amount
});
4. Communicate Clearlyโ
Translate technical codes into customer-friendly messages that guide them to resolve the issue.
FAQโ
What if merchant_advice_code is not present?
Some older declines or non-card payment methods may not include a merchant advice code. In these cases, fall back to the failure_code and failure_message fields for guidance.
Should I show the merchant_advice to customers?
The merchant_advice field is intended for merchants, not customers. Create customer-friendly messages based on the advice code rather than displaying the raw message.
How often should I retry declined payments?
For code 2 (retry later), we recommend:
- First retry: 24 hours later
- Second retry: 3 days later
- Third retry: 7 days later
- Then notify customer to update payment method
Are merchant advice codes the same across all card brands?
Yes, Omise normalizes the advice across Visa, Mastercard, and other card schemes so you get consistent codes regardless of the card brand.
Where can I find a complete list of codes?
Download the complete merchant advice code reference document:
Download Merchant Advice Codes (Excel)
You can also contact support@omise.co for additional assistance.
Related Resourcesโ
- Charges API - Charge creation and management
- 3D Secure - Authentication for cards
- Testing - Test different scenarios
- Error Handling - API error codes