User Closes the Modal
When a customer closes the payment modal without completing, the SDK throws a CubePayClosedError. This is not a failure — it means the user chose to cancel.
try {
const result = await cubePayService.executePayment({
amount: 50.0,
currency: "USD",
});
} catch (error) {
if (error.name === "CubePayClosedError") {
// User closed the modal — not an error
console.log("Payment flow cancelled by user");
return;
}
// Actual error
console.error("Payment failed:", error.message);
}
Always check for CubePayClosedError before treating a thrown error as a failure. This is the most common “error” you will encounter.
Client-Side Errors
| Error | Cause | Resolution |
|---|
CubePayClosedError | User closed the payment modal | Let the user retry when ready |
| Missing merchant ID | NEXT_PUBLIC_CUBEPAY_MERCHANT_ID not set | Add the environment variable |
| Missing container | #cubepay-sdk-root element not in DOM | Add the mount point to your layout |
| Script load failure | SDK bundle not found at /vendor/cubist-pay-client-sdk.umd.js | Verify the file exists in your public/vendor/ directory |
| SDK initialization failure | window.CubePaySDK not available after script load | Check for script loading errors in the browser console |
Server-Side Errors
Session Creation Errors
| HTTP Status | Cause | Resolution |
|---|
400 | Invalid request body or missing fields | Check that amount is a positive number and currency is provided |
400 | Amount is not a valid number, is zero, or negative | Validate the amount before sending |
401 | Invalid or missing API key | Verify CUBEPAY_API_KEY in your environment |
500 | Internal server error | Retry with exponential backoff |
502 | Upstream API unavailable | The payment service is temporarily unreachable — retry shortly |
Handling Server Errors
export async function POST(request: Request) {
const { amount, currency } = await request.json();
// Validate before calling the API
const numericAmount = parseFloat(amount);
if (isNaN(numericAmount) || !isFinite(numericAmount) || numericAmount <= 0) {
return NextResponse.json(
{ error: "Amount must be a positive number" },
{ status: 400 }
);
}
try {
const response = await fetch(`${process.env.CUBEPAY_API_HOST}/payment-sessions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.CUBEPAY_API_KEY}`,
},
body: JSON.stringify({ amount: String(numericAmount), currency }),
});
if (!response.ok) {
const errorData = await response.json().catch(() => null);
return NextResponse.json(
{ error: errorData?.message || "Failed to create session" },
{ status: response.status }
);
}
return NextResponse.json(await response.json());
} catch (error) {
return NextResponse.json(
{ error: "Payment service unavailable" },
{ status: 502 }
);
}
}
Webhook Error Handling
Design your webhook handler to be idempotent and always return 200:
export async function POST(request: Request) {
try {
const body = await request.json();
const { method, params } = body;
if (method === "cubepay_paymentStatusUpdate") {
await processStatusUpdate(params[0]);
}
} catch (error) {
// Log the error but still return 200
console.error("Webhook processing error:", error);
}
return NextResponse.json({ jsonrpc: "2.0", result: "ok" });
}
If your webhook handler returns a non-2xx status code, the payment service will retry delivery. Return 200 even when your internal processing fails to avoid duplicate deliveries.
Next Steps
Set up your Sandbox Environment to test the full payment flow with test networks.