The WebhookPayloadSchemas export provides TypeScript type definitions for all webhook event payloads sent by PayNow.
Overview
Webhook schemas are automatically extracted from the PayNow OpenAPI specification and provide type safety for handling webhook events in your application.
import type { WebhookPayloadSchemas } from 'paynow' ;
type SubscriptionRenewed = WebhookPayloadSchemas [ 'ON_SUBSCRIPTION_RENEWED' ];
type OrderCompleted = WebhookPayloadSchemas [ 'ON_ORDER_COMPLETED' ];
type RefundIssued = WebhookPayloadSchemas [ 'ON_REFUND' ];
Using Webhook Schemas
Typing Webhook Handlers
Use WebhookPayloadSchemas to type your webhook event handlers:
import type { WebhookPayloadSchemas } from 'paynow' ;
import express from 'express' ;
const app = express ();
app . use ( express . json ());
app . post ( '/webhooks/paynow' , ( req , res ) => {
const payload = req . body ;
switch ( payload . event_type ) {
case 'ON_ORDER_COMPLETED' : {
const event = payload as WebhookPayloadSchemas [ 'ON_ORDER_COMPLETED' ];
console . log ( `Order ${ event . body . id } completed` );
console . log ( `Total: ${ event . body . total_amount } ${ event . body . currency } ` );
break ;
}
case 'ON_SUBSCRIPTION_RENEWED' : {
const event = payload as WebhookPayloadSchemas [ 'ON_SUBSCRIPTION_RENEWED' ];
console . log ( `Subscription ${ event . body . id } renewed` );
console . log ( `Billing cycle: ${ event . body . billing_cycle_sequence } ` );
break ;
}
case 'ON_REFUND' : {
const event = payload as WebhookPayloadSchemas [ 'ON_REFUND' ];
console . log ( `Refund issued for checkout ${ event . body . checkout_id } ` );
break ;
}
}
res . status ( 200 ). send ( 'OK' );
});
Type-Safe Event Processing
Create type-safe webhook processors:
import type { WebhookPayloadSchemas } from 'paynow' ;
type WebhookHandler < T extends keyof WebhookPayloadSchemas > = (
payload : WebhookPayloadSchemas [ T ]
) => Promise < void >;
const handleOrderCompleted : WebhookHandler < 'ON_ORDER_COMPLETED' > = async ( payload ) => {
const { event_id , body } = payload ;
// Process the order
await fulfillOrder ({
orderId: body . id ,
customerId: body . customer_id ,
items: body . lines ,
total: body . total_amount
});
// Log the event
console . log ( `Processed order completion event ${ event_id } ` );
};
const handleSubscriptionRenewed : WebhookHandler < 'ON_SUBSCRIPTION_RENEWED' > = async ( payload ) => {
const { body } = payload ;
// Update subscription status
await updateSubscriptionStatus ({
subscriptionId: body . id ,
billingCycle: body . billing_cycle_sequence ,
nextBillingDate: body . current_period_end
});
};
Creating a Webhook Router
Build a type-safe webhook router:
import type { WebhookPayloadSchemas } from 'paynow' ;
type WebhookEventType = keyof WebhookPayloadSchemas ;
class WebhookRouter {
private handlers = new Map < WebhookEventType , ( payload : any ) => Promise < void >>();
on < T extends WebhookEventType >(
eventType : T ,
handler : ( payload : WebhookPayloadSchemas [ T ]) => Promise < void >
) {
this . handlers . set ( eventType , handler );
return this ;
}
async handle ( payload : any ) {
const handler = this . handlers . get ( payload . event_type );
if ( handler ) {
await handler ( payload );
}
}
}
// Usage
const router = new WebhookRouter ();
router
. on ( 'ON_ORDER_COMPLETED' , async ( payload ) => {
// payload is properly typed as WebhookPayloadSchemas['ON_ORDER_COMPLETED']
console . log ( `Order ${ payload . body . id } completed` );
})
. on ( 'ON_SUBSCRIPTION_RENEWED' , async ( payload ) => {
// payload is properly typed as WebhookPayloadSchemas['ON_SUBSCRIPTION_RENEWED']
console . log ( `Subscription ${ payload . body . id } renewed` );
})
. on ( 'ON_REFUND' , async ( payload ) => {
// payload is properly typed as WebhookPayloadSchemas['ON_REFUND']
console . log ( `Refund issued` );
});
app . post ( '/webhooks/paynow' , async ( req , res ) => {
await router . handle ( req . body );
res . status ( 200 ). send ( 'OK' );
});
Webhook Event Types
The following webhook events are available:
Order Events
Triggered when an order is completed and payment is successful
Subscription Events
ON_SUBSCRIPTION_ACTIVATED
Triggered when a subscription becomes active
Triggered when a subscription is renewed for a new billing cycle
Triggered when a subscription is canceled
Trial Events
Triggered when a trial period starts
Triggered when a trial period ends successfully
Triggered when a trial is canceled
Delivery Events
Triggered when a delivery item is added to a customer’s inventory
ON_DELIVERY_ITEM_ACTIVATED
Triggered when a delivery item is activated
Triggered when a delivery item is used/consumed
Triggered when a delivery item is renewed
Triggered when a delivery item expires
Triggered when a delivery item is revoked
Payment Events
Triggered when a refund is issued
Triggered when a chargeback is initiated
Triggered when a chargeback is closed
Other Events
ON_DISCORD_ACCOUNT_LINKED_TO_CHECKOUT
Triggered when a Discord account is linked to a checkout
Common Webhook Payload Structure
All webhook payloads share a common structure:
The type of webhook event (e.g., ON_ORDER_COMPLETED)
The unique Flake ID of the webhook event for idempotency
The event-specific payload data
Example: ON_SUBSCRIPTION_RENEWED Payload
{
"event_type" : "ON_SUBSCRIPTION_RENEWED" ,
"event_id" : "411486491630370816" ,
"body" : {
"id" : "411486491630370816" ,
"store_id" : "411486491630370816" ,
"customer_id" : "411486491630370816" ,
"customer" : { /* CustomerDTO */ },
"checkout" : { /* CheckoutDTO */ },
"billing_cycle_sequence" : 2 ,
"billing_email" : "[email protected] " ,
"subtotal_amount" : 8999 ,
"tax_amount" : 1350 ,
"discount_amount" : 0 ,
"total_amount" : 10349 ,
"currency" : "USD" ,
"interval_value" : 1 ,
"interval_scale" : "month" ,
"product_id" : "411486491630370816" ,
"product_version_id" : "411486491630370816" ,
"product_name" : "Premium Subscription" ,
"product_image_url" : "https://example.com/image.jpg" ,
"product" : { /* ProductDTO */ },
"billing_country" : "US" ,
"initial_subtotal_amount" : 8999 ,
"initial_tax_amount" : 1350 ,
"initial_discount_amount" : 1500 ,
"initial_giftcard_usage_amount" : 0 ,
"initial_total_amount" : 8849 ,
"customer_ip" : "127.0.0.1" ,
"current_period_start" : "2024-03-01T00:00:00Z" ,
"current_period_end" : "2024-06-01T00:00:00Z" ,
"created_at" : "2024-03-01T11:00:00Z" ,
"active_at" : "2024-03-01T11:05:00Z" ,
"checkout_id" : "411486491630370816" ,
"checkout_line_id" : "411486491630370816" ,
"status" : "active"
}
}
The WebhookPayloadSchemas type is derived from webhook request bodies:
import type { webhooks as Webhooks } from './generated/webhooks' ;
import type { ExtractJsonRequestBodies } from './types' ;
export type WebhookPayloadSchemas = ExtractJsonRequestBodies < Webhooks >;
The ExtractJsonRequestBodies utility type extracts the application/json request body type from each webhook definition, providing clean types for each event.
Best Practices
Use the event_id field to implement idempotent webhook processing and prevent duplicate event handling. const processedEvents = new Set < string >();
async function handleWebhook ( payload : any ) {
if ( processedEvents . has ( payload . event_id )) {
return ; // Already processed
}
// Process the event
await processEvent ( payload );
// Mark as processed
processedEvents . add ( payload . event_id );
}
Use type guards for runtime validation: function isOrderCompletedEvent (
payload : any
) : payload is WebhookPayloadSchemas [ 'ON_ORDER_COMPLETED' ] {
return payload . event_type === 'ON_ORDER_COMPLETED' ;
}
if ( isOrderCompletedEvent ( payload )) {
// TypeScript knows payload is ON_ORDER_COMPLETED
console . log ( payload . body . total_amount );
}
Always return a 200 response to acknowledge receipt, even if processing fails: app . post ( '/webhooks' , async ( req , res ) => {
try {
await processWebhook ( req . body );
} catch ( error ) {
// Log error but still acknowledge
console . error ( 'Webhook processing failed:' , error );
// Add to retry queue if needed
}
res . status ( 200 ). send ( 'OK' );
});
Webhook Configuration Learn how to configure webhooks in your store
Management Schemas Type definitions for Management API
Storefront Schemas Type definitions for Storefront API