Overview
The PayNow SDK provides two specialized clients, each designed for different use cases:
Storefront Client : For public-facing store operations
Management Client : For administrative store management
Both clients share the same underlying architecture, providing a consistent developer experience with full TypeScript support.
Client Types
Storefront Client
Management Client
The Storefront Client handles customer-facing operations like browsing products and creating orders. import { createStorefrontClient } from '@paynow-gg/typescript-sdk' ;
const storefront = createStorefrontClient (
'your-store-id' ,
'optional-customer-token'
);
// Access operations through grouped methods
const store = await storefront . store . getStorefrontStore ();
const products = await storefront . products . listProducts ();
Available Operation Groups
store - Store information and configuration
products - Product catalog and variants
orders - Order creation and management
customers - Customer profiles and authentication
The Management Client handles administrative operations requiring API key authentication. import { createManagementClient } from '@paynow-gg/typescript-sdk' ;
const management = createManagementClient (
'your-store-id' ,
'your-api-key'
);
// Access management operations
const product = await management . products . createProduct ({
data: { name: 'New Product' , price: 1999 }
});
Available Operation Groups
products - Create and manage products
orders - View and manage orders
customers - Manage customer accounts
analytics - Access store analytics
Proxy-Based Method Routing
The SDK uses JavaScript Proxy objects to dynamically route method calls to the correct API endpoints. This enables a clean, intuitive API without hardcoding every possible operation.
How It Works
// From src/client.ts:110-159
return new Proxy ({} as GroupedOperations < T >, {
get : ( _ , prop : string ) => {
return new Proxy (
{},
{
get : ( _ , methodName : string ) => {
const prefix = prop . charAt ( 0 ). toUpperCase () + prop . slice ( 1 );
const method = methodName . charAt ( 0 ). toUpperCase () + methodName . slice ( 1 );
const operationId = ` ${ prefix } _ ${ method } ` ;
return async ( config ?: any ) => {
const mapping = operationMappings [ operationId ];
if ( ! mapping ) {
throw new Error ( `Operation ${ operationId } not found` );
}
// ... URL construction and request execution
};
},
},
);
},
});
Method Name Mapping
When you call storefront.products.listProducts(), the SDK:
Takes the group name: products
Takes the method name: listProducts
Converts to operation ID: Products_ListProducts
Looks up the HTTP method and path from operation mappings
Executes the request
This pattern means you get autocomplete and type safety for all operations without bloating the SDK bundle size.
Type Safety
The SDK provides comprehensive TypeScript types derived from the OpenAPI specification.
Operation Groups
Operations are automatically grouped based on their naming convention:
// From src/client.ts:72-88
type GroupedOperations < T extends Record < string , any >> = {
[ K in keyof T as K extends ` ${ infer Prefix } _ ${ string } `
? Uncapitalize < Prefix >
: never ] : {
[ Op in keyof T as Op extends ` ${ Capitalize < K extends ` ${ infer P } _ ${ string } ` ? P : never > } _ ${ infer Method } `
? Uncapitalize < Method >
: never ] : Op extends keyof T
? AllOptional < MapToAxiosConfig < T [ Op ]>> extends true
? ( config ?: MapToAxiosConfig < T [ Op ]>) => Promise < AxiosResponse < SuccessType < T [ Op ]>>>
: ( config : MapToAxiosConfig < T [ Op ]>) => Promise < AxiosResponse < SuccessType < T [ Op ]>>>
: never ;
};
};
This complex type transformation provides:
Grouped operation methods (e.g., products.list, products.get)
Optional vs required config parameters
Correct return types for each operation
The SDK automatically extracts the success response type from OpenAPI specs:
// From src/client.ts:7-17
type SuccessType < T > = T extends {
responses : { 200 : { content : { "application/json" : infer R } } };
}
? R
: T extends {
responses : { 201 : { content : { "application/json" : infer R } } };
}
? R
: T extends { responses : { 204 : any } }
? void
: never ;
This means when you call an operation, TypeScript knows the exact shape of response.data:
// TypeScript knows the exact type of store
const response = await storefront . store . getStorefrontStore ();
const store = response . data ; // Fully typed!
Request Configuration
Each operation method accepts a configuration object that maps OpenAPI parameters to Axios config:
// From src/client.ts:27-64
type MapToAxiosConfig < T extends { parameters ?: any ; requestBody ?: any }> = Omit <
AxiosRequestConfig ,
"params" | "headers" | "data" | "url"
> &
( T [ "parameters" ] extends { query ?: infer Q }
? Q extends never
? { params ?: any }
: Q extends undefined
? { params ?: any }
: HasRequiredKeys < Q > extends true
? { params : Q }
: { params ?: Q }
: { params ?: any }) & {
headers ?: T [ "parameters" ] extends { header ?: infer H }
? H extends never
? AxiosRequestConfig [ "headers" ]
: H extends undefined
? AxiosRequestConfig [ "headers" ]
: H & AxiosRequestConfig [ "headers" ]
: AxiosRequestConfig [ "headers" ];
} // ... and more for data and path parameters
Using the Config Object
Query Parameters
Path Parameters
Request Body
Combined
// List products with filtering
const response = await storefront . products . listProducts ({
params: {
category: 'games' ,
limit: 10 ,
},
});
Path Replacement
The SDK handles dynamic path parameters automatically:
// From src/client.ts:141-146
if ( config ?. path ) {
url = url . replace (
/{ ( \w + ) }/ g ,
( _ , key ) => config . path [ key ] ?? `{ ${ key } }` ,
);
}
When you provide a path object, the SDK replaces {paramName} placeholders in the URL template with actual values.
Axios Integration
The SDK is built on top of Axios, giving you access to all Axios features:
const storefront = createStorefrontClient (
'your-store-id' ,
'customer-token' ,
{
timeout: 5000 ,
headers: { 'X-Custom-Header' : 'value' },
validateStatus : ( status ) => status < 500 ,
}
);
All responses are Axios responses, so you can access:
response.data - The response body
response.status - HTTP status code
response.headers - Response headers
response.config - Request configuration
Default Configuration
The SDK applies sensible defaults to all clients:
// From src/client.ts:96-108
const client = axios . create ({
baseURL: "https://api.paynow.gg" ,
... options ,
headers: {
"Content-Type" : "application/json" ,
Accept: "application/json" ,
... options ?. headers ,
... defaultHeaders ,
},
paramsSerializer: {
indexes: null ,
},
});
The paramsSerializer.indexes: null setting ensures array parameters are serialized as key=value1&key=value2 instead of key[0]=value1&key[1]=value2.
Next Steps
Authentication Learn about customer tokens and API keys
Error Handling Handle API errors gracefully with type-safe helpers