Status Lifecycle
Understanding the status lifecycle is crucial for properly handling payments in your integration. This guide explains how payment intents and attempts transition through various states.
Payment Lifecycle Diagram
The payment lifecycle includes various states representing specific stages in the payment process:
Intent Statuses
selection
The initial state where the customer selects a payment method.
- Intent is created and waiting for customer action
- Customer is on the hosted checkout page
- No payment attempt has been made yet (or previous attempt failed with alternatives available)
What to do:
- Wait for customer to select payment method and make the payment
Transitions to:
pending- Customer initiates paymentcanceled- Customer/merchant cancels
pending
The state where the payment is being processed.
- Customer has selected a payment method and clicked "Pay"
- Payment attempt is in progress with the PSP
- May require additional steps (3D Secure, OTP, etc.)
What to do:
- Wait for webhook notification
- Do NOT fulfill order yet
Transitions to:
success- Payment succeedsfailed- Payment fails with no alternativesselection- Payment fails but alternatives existauthorized- Payment authorized (delayed capture)
Typical duration: Seconds to minutes (depending on payment method)
authorized
The payment has been authorized but not yet captured.
Only applicable when capture_mode: "prefer_delay" is used and the payment method supports authorization.
- Funds are reserved but not yet transferred
- You must manually capture within the allowed time window
- Authorization may expire if not captured
What to do:
- Verify order can be fulfilled
- Call capture endpoint:
GET /api/v2/intents/{intent_id}/capture - Or cancel authorization:
GET /api/v2/intents/{intent_id}/cancel-authorization
Transitions to:
success- Payment capturedcanceled- Authorization canceledfailed- Authorization expires
See Capture Modes Guide for details.
success
The state indicating a successful payment.
- Payment has been processed successfully
- Funds will be settled to your account
- Safe to fulfill the order
What to do:
- Fulfill the order/deliver the product
- Send confirmation email to customer
- Update your database
Transitions to:
chargeback- Customer disputes payment (rare)
failed
The payment failed and no other payment methods are available.
- All payment attempts have been exhausted
- No alternative payment methods exist
- Customer must start over with a new intent
What to do:
- Log failure reason for analysis
- Offer customer to try again (create new intent)
Common causes:
- Card declined
- Insufficient funds
- Payment method not supported
- All retry attempts failed
Does NOT transition further - This is a terminal state
canceled
The payment intent was canceled.
Cancellation can happen in two ways:
1. Customer Cancellation:
- Customer clicked "Cancel" on checkout page
2. Merchant Cancellation:
- You called
POST /api/intents/{intent_id}/cancel - Order was canceled before payment
What to do:
- Clean up any pending order records
- Allow customer to restart checkout if appropriate
Restrictions:
- Can only cancel intents in
selectionstatus - Cannot cancel
pending,success, orfailedintents
Does NOT transition further - This is a terminal state
chargeback
The payment was charged back by the customer.
- Customer disputed the charge with their bank
- Rare but important to handle
- Funds may be reversed
What to do:
- Review the transaction
- Gather evidence if you want to dispute
- Contact NjiaPay support for guidance
- Update your records
Does NOT transition further - This is a terminal state
Webhook Events for Status Changes
You'll receive StatusChange webhook events when intent status changes:
{
"type": "StatusChange",
"created": "2024-09-01T00:00:00Z",
"content": {
"intent_id": "550e8400-e29b-41d4-a716-446655440000",
"reference_id": "order_12345",
"purchaser_id": "customer_abc",
"amount": 20000,
"currency": "ZAR",
"status": "success",
"partner": "adyen",
"method": "card",
"brand": "mastercard",
"issuer_country": "ZA",
"event_ts": "2024-09-01T12:34:56Z",
"failure_reason": null
}
}
See Webhooks Guide for complete event documentation.
Handling Status in Your Application
Recommended Approach
function handlePaymentStatus(intent) {
switch (intent.status) {
case "pending":
// Payment is processing
displayProcessingMessage();
break;
case "authorized":
// Payment authorized, needs capture
// Verify order can be fulfilled
if (canFulfillOrder(intent.reference_id)) {
capturePayment(intent.id);
} else {
cancelAuthorization(intent.id);
}
break;
case "success":
// Payment successful - fulfill order
fulfillOrder(intent.reference_id);
sendConfirmationEmail(intent.purchaser_id);
displaySuccessPage();
break;
case "failed":
// Payment failed
logFailure(intent.failure_reason);
displayErrorPage("Payment failed. Please try again.");
break;
case "canceled":
// Payment canceled
displayCanceledPage();
break;
case "chargeback":
// Chargeback filed
alertFinanceTeam(intent);
suspendOrder(intent.reference_id);
break;
default:
console.error("Unknown status:", intent.status);
}
}
Status Polling (Not Recommended)
Instead of polling the API for status:
// Not recommended
setInterval(async () => {
const intent = await getIntent(intentId);
handlePaymentStatus(intent);
}, 5000); // Poll every 5 seconds
Use webhooks for real-time updates:
// Recommended
app.post("/webhook", (req, res) => {
const event = req.body;
if (event.type === "StatusChange") {
handlePaymentStatus(event.content);
}
res.status(200).send("OK");
});
Best Practices
✅ DO
- Use webhooks for status updates instead of polling
- Handle all possible statuses in your code
- Only fulfill orders when status is
success - Store status history for audit trails
- Display user-friendly messages for each status
❌ DON'T
- Rely on
return_urlquery parameters for status - Fulfill orders on
pendingorauthorizedstatus - Ignore
chargebackstatus - Poll the API excessively
- Assume statuses can only move forward