Appearance
Error Handling
The SDK distinguishes between two error categories:
- Fatal configuration errors — thrown synchronously by
ExpressButtons()when the entire config is unusable (missingprovidersoronClick). - Per-provider configuration errors — reported asynchronously via
onErrorduringrender()for individual invalid providers, so other valid providers still render. - Payment errors — surfaced asynchronously through
onErrorduring the payment flow.
Configuration Errors — ConfigValidationError
ConfigValidationError is thrown synchronously by ExpressButtons() only for fatal global mistakes (missing providers array or missing onClick). Wrap the call in a try/catch for these:
ts
import { ExpressButtons, ConfigValidationError } from 'collana-pay-js';
try {
const buttons = ExpressButtons({ ... });
await buttons.render();
} catch (err) {
if (err instanceof ConfigValidationError) {
console.error(err.code, err.message);
// e.g. 'MISSING_PROVIDERS' or 'MISSING_REQUIRED_CALLBACK'
}
}Per-provider configuration mistakes (wrong options, unknown provider, etc.) do not throw. Instead, the affected provider is skipped and its error is reported through onError alongside runtime payment errors. This ensures other correctly-configured providers still render.
ts
ExpressButtons({
providers: [...],
onClick: async (identity, options) => { ... },
onError: (identity, error) => {
// Receives both config errors (e.g. MISSING_REQUIRED_OPTION) and
// runtime payment errors (e.g. RENDER_FAILED).
console.error(error.code, error.message);
},
});Error Codes
| Code | Scope | When it occurs |
|---|---|---|
MISSING_PROVIDERS | Fatal (throws) | The providers array is empty or not provided. |
MISSING_REQUIRED_CALLBACK | Fatal (throws) | onClick is missing or not a function. |
INVALID_PROVIDER_CONFIG | Per-provider (onError) | A provider entry is missing providerProtocolType, paymentMethodType, or container. |
UNKNOWN_PROVIDER | Per-provider (onError) | The provider key is not registered in the SDK (check spelling and that the provider is imported). |
MISSING_REQUIRED_OPTION | Per-provider (onError) | A required options field for a specific provider is absent (e.g. PayPal without clientId or merchantId). |
CONFLICTING_OPTIONS | Per-provider (onError) | Two mutually exclusive options are both set, or two providers on the same page have incompatible SDK parameters. |
Provider-specific rules
PayPal (PayPal:PayPalExpress, PayPal:PayLater)
PayPal supports two integration modes. Exactly one of the following must be set per provider entry:
| Field | Mode | When to use |
|---|---|---|
options.merchantId | Partner onboarding | The preferred mode. collana pay acts as partner; your backend links the merchant account. |
options.clientId | Direct integration | Use your own PayPal app credentials directly. No partner onboarding required. |
Setting both or neither throws CONFLICTING_OPTIONS or MISSING_REQUIRED_OPTION respectively.
When combining PayPal:PayPalExpress and PayPal:PayLater on the same page, both entries must share the same clientId/merchantId, currencyCode, and pspAutoCaptureEnabled values — otherwise a CONFLICTING_OPTIONS error is thrown. See the provider pages for provider-specific constraints.
Payment Errors — onError
Runtime errors during the payment flow are surfaced through the onError callback as a PaymentError object. These are not thrown — the SDK calls your callback so you can display a user-friendly message and allow retry.
ts
onError: (identity, error) => {
console.error(error.code, error.message);
// error.providerProtocolType and error.paymentMethodType are also available
},Common PaymentError codes:
| Code | Cause |
|---|---|
CREATE_ORDER_FAILED | onClick threw or the decoded payload was missing the order ID. |
APPROVE_REDIRECT_FAILED | The POST to returnUrl failed after payment approval. |
PAYPAL_ERROR | PayPal SDK reported an internal error. |
TEARDOWN_FAILED | The provider SDK could not be cleanly torn down. |
Because providers render via
Promise.allSettled, one provider'sonErrordoes not prevent other providers from rendering. Each failure triggers a separateonErrorcall.
