Skip to main content

Overview

The PayNow SDK provides built-in error handling utilities to help you distinguish between PayNow API errors and other types of failures. All API errors follow a consistent structure defined by the PayNow API specification.

PayNowError Type

The SDK exports a specialized PayNowError type that represents errors returned by the PayNow API:
// From src/errors.ts:4-6
export type PayNowError = AxiosError<components["schemas"]["PayNowError"]> & {
  response: AxiosResponse<components["schemas"]["PayNowError"]>;
};
This type extends Axios’s AxiosError with PayNow-specific error schema information and guarantees that a response property exists.

Error Structure

PayNow API errors have the following structure:
{
  status: number;      // HTTP status code
  code: string;        // PayNow error code
  message: string;     // Human-readable error message
}

Type Guard: isPayNowError

The SDK provides a type guard function to safely check if an error is a PayNow API error:
import { isPayNowError } from '@paynow-gg/typescript-sdk';

Implementation

// From src/errors.ts:8-18
export function isPayNowError(error: unknown): error is PayNowError {
  return (
    error instanceof AxiosError &&
    !!error.response &&
    typeof error.response.data === "object" &&
    error.response.data !== null &&
    "status" in error.response.data &&
    "code" in error.response.data &&
    "message" in error.response.data
  );
}
This function validates that:
  1. The error is an Axios error
  2. A response exists (not a network/timeout error)
  3. The response data is an object
  4. The object contains status, code, and message fields

Basic Error Handling

Here’s how to handle errors in your application:
import { createStorefrontClient, isPayNowError } from '@paynow-gg/typescript-sdk';

const storefront = createStorefrontClient('your-store-id');

try {
  const response = await storefront.products.getProduct({
    path: { productId: 'invalid-id' },
  });
  
  console.log(response.data);
} catch (error) {
  if (isPayNowError(error)) {
    // This is a PayNow API error
    console.error('PayNow API Error:', {
      status: error.response.data.status,
      code: error.response.data.code,
      message: error.response.data.message,
    });
  } else {
    // This is some other error (network, timeout, etc.)
    console.error('Unexpected error:', error);
  }
}

Error Handling Patterns

try {
  const response = await storefront.orders.createOrder({
    data: { productId: '123', quantity: 1 },
  });
  
  return response.data;
} catch (error) {
  if (isPayNowError(error)) {
    throw new Error(`Failed to create order: ${error.response.data.message}`);
  }
  throw error;
}

Non-PayNow Errors

Not all errors are PayNow API errors. You may encounter:

Network Errors

try {
  const response = await storefront.store.getStorefrontStore();
} catch (error) {
  if (error instanceof AxiosError && !error.response) {
    // Network error - no response from server
    console.error('Network error:', error.message);
  }
}

Timeout Errors

const storefront = createStorefrontClient('store-id', undefined, {
  timeout: 5000, // 5 second timeout
});

try {
  const response = await storefront.products.listProducts();
} catch (error) {
  if (error instanceof AxiosError && error.code === 'ECONNABORTED') {
    console.error('Request timed out');
  }
}

Request Cancellation

import axios from 'axios';

const controller = new AbortController();

const storefront = createStorefrontClient('store-id');

try {
  const response = await storefront.products.listProducts({
    signal: controller.signal,
  });
} catch (error) {
  if (axios.isCancel(error)) {
    console.log('Request cancelled');
  }
}

// Cancel the request
controller.abort();

Error Response Structure

When isPayNowError returns true, you can safely access:
if (isPayNowError(error)) {
  // All of these are guaranteed to exist and be typed correctly
  const status = error.response.data.status;     // number
  const code = error.response.data.code;         // string
  const message = error.response.data.message;   // string
  
  // Standard Axios error properties
  const httpStatus = error.response.status;      // number
  const headers = error.response.headers;        // object
  const config = error.config;                   // AxiosRequestConfig
}

Best Practices

Always use isPayNowError() before accessing error properties. This provides type safety and prevents runtime errors:
// ✅ Good
if (isPayNowError(error)) {
  console.log(error.response.data.code);
}

// ❌ Bad - TypeScript doesn't know the error structure
console.log(error.response.data.code);
Don’t assume all errors are PayNow errors. Network issues, timeouts, and other failures can occur:
try {
  await client.products.listProducts();
} catch (error) {
  if (isPayNowError(error)) {
    // Handle PayNow API error
  } else if (error instanceof AxiosError) {
    // Handle other Axios errors (network, timeout, etc.)
  } else {
    // Handle unexpected errors
  }
}
Include relevant error information when logging:
if (isPayNowError(error)) {
  console.error('PayNow API Error:', {
    status: error.response.data.status,
    code: error.response.data.code,
    message: error.response.data.message,
    url: error.config?.url,
    method: error.config?.method,
  });
}
Don’t expose raw API error messages to end users. Map error codes to user-friendly messages:
const userFriendlyMessage = (error: PayNowError): string => {
  const errorMessages: Record<string, string> = {
    'PRODUCT_NOT_FOUND': 'This product is no longer available.',
    'INSUFFICIENT_STOCK': 'This item is out of stock.',
    'INVALID_PAYMENT': 'Payment failed. Please check your payment details.',
  };

  return errorMessages[error.response.data.code] 
    || 'Something went wrong. Please try again.';
};

Next Steps

Authentication

Understand how authentication works in the SDK

Client Architecture

Learn about the client structure and type safety

Build docs developers (and LLMs) love