Skip to main content

Error Handling

This guide explains how to handle errors returned by the Drop-in Checkout.

Error Sources

Errors can originate from two sources:

SourceDeliveryWhen
Event listenerevent.code === "error"Initialization failures, internal errors
Method returnresult instanceof ErrorsubmitPayment(), mount(), setSession(), setWalletSession()

Handling Event Errors

Listen for errors through the eventListener callback:

const dropin = await Purse.createDropinCheckout({
session: clientSession.widget.data,
eventListener: (event) => {
if (event.code === "error") {
console.error("Error:", event.payload.message);
showErrorToUser(event.payload.message);
}
}
});

Error Event Structure

{
code: "error";
payload: {
message: string; // Human-readable description
code?: string; // Error code (when available)
};
}

Handling Method Errors

Wrap async method calls in try/catch:

submitPayment()

try {
await dropin.submitPayment();
// Success - SDK handles redirection
} catch (error) {
console.error("Payment failed:", error.message);
payButton.disabled = false;
payButton.textContent = "Pay";
showErrorToUser(error.message);
}

mount()

try {
await dropin.mount(container);
} catch (error) {
console.error("Mount failed:", error.message);
// Show fallback or error state
}

setWalletSession()

try {
await dropin.setWalletSession(walletSession);
} catch (error) {
console.error("Wallet session failed:", error.message);
}

setSession()

setSession() handles errors internally. Errors are reported through the eventListener callback:

await dropin.setSession(newSessionData);
// Errors are emitted via the eventListener, not thrown

Common Error Scenarios

ScenarioSuggested Action
Network errorDisplay retry option
Session expiredFetch new session and call setSession()
Payment declinedShow decline message, allow retry
Validation errorPrompt user to correct input

Session Expiration

When a session expires, the Drop-in shows a blocking overlay. You have two options.

Provide a hooks.onRefreshSession callback when creating the Drop-in. A Refresh session button is shown on the overlay and calls your hook when clicked. The Drop-in reinitializes automatically with the new session.

const dropin = await Purse.createDropinCheckout({
session: clientSession.widget.data,
hooks: {
async onRefreshSession() {
const newSession = await fetchClientSessionFromBackend();
return newSession.widget.data;
}
}
});

The refresh button is only shown when the hook is provided. If the callback returns void or throws, the overlay remains and the user can retry.

Manual refresh with setSession()

You can also update the session programmatically at any time:

async function refreshSession() {
const newSession = await fetchClientSessionFromBackend();
await dropin.setSession(newSession.widget.data);
}

Example: Comprehensive Error Handling

const payButton = document.getElementById("pay-button");

try {
const dropin = await Purse.createDropinCheckout({
session: clientSession.widget.data,
eventListener: (event) => {
if (event.code === "ready") {
document.getElementById("loading-spinner").style.display = "none";
}
if (event.code === "error") {
showError(event.payload.message);
}
}
});

await dropin.mount(container);

// Enable pay button when payment method is selected
dropin.isPaymentFulfilled.subscribe((isFulfilled) => {
payButton.disabled = !isFulfilled;
});

payButton.addEventListener("click", async () => {
payButton.disabled = true;
payButton.textContent = "Processing...";
hideError();

try {
await dropin.submitPayment();
} catch (error) {
showError(error.message || "Payment failed. Please try again.");
payButton.disabled = false;
payButton.textContent = "Pay";
}
});
} catch (error) {
showError("Failed to load payment form. Please refresh.");
}

Next Steps