The SDK is designed for server-side use only. Here are patterns for popular frameworks.
Singleton Pattern (Recommended)
Create a shared SDK instance to avoid re-initializing on every request:
Code// lib/saligpay.ts import { SaligPay } from "saligpay-node"; let saligpayInstance: SaligPay | null = null; export function getSaligPay(): SaligPay { if (!saligpayInstance) { saligpayInstance = new SaligPay({ clientId: process.env.SALIGPAY_CLIENT_ID!, clientSecret: process.env.SALIGPAY_CLIENT_SECRET!, env: process.env.NODE_ENV === "production" ? "production" : "sandbox", }); } return saligpayInstance; }
React Router 7 (Remix)
Action: Create Checkout
Code// app/routes/checkout.tsx import type { ActionFunctionArgs } from "react-router"; import { redirect } from "react-router"; import { getSaligPay } from "~/lib/saligpay.server"; export async function action({ request }: ActionFunctionArgs) { const formData = await request.formData(); const planId = formData.get("planId") as string; const email = formData.get("email") as string; const saligpay = getSaligPay(); await saligpay.ensureAuthenticated(); const checkout = await saligpay.checkout.create({ externalId: `order-${Date.now()}`, amount: planId === "premium" ? 149900 : 49900, // ₱1,499 or ₱499 description: `${planId} Plan Subscription`, webhookUrl: `${process.env.APP_URL}/api/webhooks/saligpay`, returnUrl: `${process.env.APP_URL}/checkout/success`, contact: { name: formData.get("name") as string, email, }, metadata: { planId, userId: formData.get("userId") }, }); return redirect(checkout.checkoutUrl); } export default function CheckoutPage() { return ( <form method="post"> <input type="hidden" name="planId" value="premium" /> <input type="text" name="name" placeholder="Full Name" required /> <input type="email" name="email" placeholder="Email" required /> <button type="submit">Proceed to Payment</button> </form> ); }
Resource Route: Webhook Handler
Code// app/routes/api.webhooks.saligpay.ts import type { ActionFunctionArgs } from "react-router"; import { json } from "react-router"; import { getSaligPay } from "~/lib/saligpay.server"; import { db } from "~/lib/db.server"; export async function action({ request }: ActionFunctionArgs) { const saligpay = getSaligPay(); const body = await request.json(); try { const payload = saligpay.webhooks.constructEvent(body); switch (payload.status) { case "COMPLETED": await db.order.update({ where: { externalId: payload.externalId }, data: { status: "PAID", paidAt: new Date(), paymentMethod: payload.paymentMethod.type, }, }); break; case "FAILED": await db.order.update({ where: { externalId: payload.externalId }, data: { status: "FAILED" }, }); break; } return json({ received: true }); } catch (error) { console.error("Webhook error:", error); return json({ error: "Invalid webhook" }, { status: 400 }); } }
Next.js (App Router)
Server Action: Create Checkout
Code// app/actions/checkout.ts "use server"; import { redirect } from "next/navigation"; import { getSaligPay } from "@/lib/saligpay"; export async function createCheckout(formData: FormData) { const saligpay = getSaligPay(); await saligpay.ensureAuthenticated(); const checkout = await saligpay.checkout.create({ externalId: `order-${Date.now()}`, amount: Number(formData.get("amount")), description: formData.get("description") as string, webhookUrl: `${process.env.NEXT_PUBLIC_APP_URL}/api/webhooks/saligpay`, returnUrl: `${process.env.NEXT_PUBLIC_APP_URL}/checkout/success`, contact: { name: formData.get("name") as string, email: formData.get("email") as string, }, }); redirect(checkout.checkoutUrl); }
Route Handler: Webhook
Code// app/api/webhooks/saligpay/route.ts import { NextRequest, NextResponse } from "next/server"; import { getSaligPay } from "@/lib/saligpay"; import { prisma } from "@/lib/prisma"; export async function POST(request: NextRequest) { const saligpay = getSaligPay(); const body = await request.json(); try { const payload = saligpay.webhooks.constructEvent(body); if (payload.status === "COMPLETED") { await prisma.order.update({ where: { externalId: payload.externalId }, data: { status: "PAID", paidAt: new Date(), }, }); } return NextResponse.json({ received: true }); } catch (error) { console.error("Webhook error:", error); return NextResponse.json({ error: "Invalid webhook" }, { status: 400 }); } }
Server Component with Checkout Button
Code// app/checkout/page.tsx import { createCheckout } from "@/app/actions/checkout"; export default function CheckoutPage() { return ( <form action={createCheckout}> <input type="hidden" name="amount" value="10000" /> <input type="hidden" name="description" value="Premium Plan" /> <input type="text" name="name" placeholder="Full Name" required /> <input type="email" name="email" placeholder="Email" required /> <button type="submit">Pay ₱100.00</button> </form> ); }
Next.js (Pages Router)
API Route: Create Checkout
Code// pages/api/checkout/create.ts import type { NextApiRequest, NextApiResponse } from "next"; import { getSaligPay } from "@/lib/saligpay"; export default async function handler( req: NextApiRequest, res: NextApiResponse, ) { if (req.method !== "POST") { return res.status(405).json({ error: "Method not allowed" }); } const saligpay = getSaligPay(); await saligpay.ensureAuthenticated(); try { const { amount, description, name, email } = req.body; const checkout = await saligpay.checkout.create({ externalId: `order-${Date.now()}`, amount, description, webhookUrl: `${process.env.NEXT_PUBLIC_APP_URL}/api/webhooks/saligpay`, returnUrl: `${process.env.NEXT_PUBLIC_APP_URL}/checkout/success`, contact: { name, email }, }); return res.json({ checkoutUrl: checkout.checkoutUrl }); } catch (error) { console.error("Checkout error:", error); return res.status(500).json({ error: "Failed to create checkout" }); } }
API Route: Webhook Handler
Code// pages/api/webhooks/saligpay.ts import type { NextApiRequest, NextApiResponse } from "next"; import { getSaligPay } from "@/lib/saligpay"; import { prisma } from "@/lib/prisma"; export default async function handler( req: NextApiRequest, res: NextApiResponse, ) { if (req.method !== "POST") { return res.status(405).json({ error: "Method not allowed" }); } const saligpay = getSaligPay(); try { const payload = saligpay.webhooks.constructEvent(req.body); if (payload.status === "COMPLETED") { await prisma.order.update({ where: { externalId: payload.externalId }, data: { status: "PAID", paidAt: new Date() }, }); } return res.json({ received: true }); } catch (error) { console.error("Webhook error:", error); return res.status(400).json({ error: "Invalid webhook" }); } }
Hono
Code// src/index.ts import { Hono } from "hono"; import { SaligPay } from "saligpay-node"; const app = new Hono(); const saligpay = new SaligPay({ clientId: process.env.SALIGPAY_CLIENT_ID!, clientSecret: process.env.SALIGPAY_CLIENT_SECRET!, env: "sandbox", }); // Create checkout app.post("/checkout", async (c) => { await saligpay.ensureAuthenticated(); const { amount, description, name, email } = await c.req.json(); const checkout = await saligpay.checkout.create({ externalId: `order-${Date.now()}`, amount, description, webhookUrl: `${process.env.APP_URL}/webhooks/saligpay`, returnUrl: `${process.env.APP_URL}/success`, contact: { name, email }, }); return c.json({ checkoutUrl: checkout.checkoutUrl }); }); // Webhook handler app.post("/webhooks/saligpay", async (c) => { const body = await c.req.json(); try { const payload = saligpay.webhooks.constructEvent(body); if (payload.status === "COMPLETED") { console.log(`Payment ${payload.externalId} completed!`); } return c.json({ received: true }); } catch (error) { return c.json({ error: "Invalid webhook" }, 400); } }); export default app;
NestJS
Service
Code// src/saligpay/saligpay.service.ts import { Injectable, OnModuleInit } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import { SaligPay, CreateCheckoutOptions } from "saligpay-node"; @Injectable() export class SaligPayService implements OnModuleInit { private client: SaligPay; constructor(private configService: ConfigService) { this.client = new SaligPay({ clientId: this.configService.get("SALIGPAY_CLIENT_ID"), clientSecret: this.configService.get("SALIGPAY_CLIENT_SECRET"), env: this.configService.get("NODE_ENV") === "production" ? "production" : "sandbox", }); } async onModuleInit() { await this.client.authenticate(); } async createCheckout(options: CreateCheckoutOptions) { await this.client.ensureAuthenticated(); return this.client.checkout.create(options); } parseWebhook(body: unknown) { return this.client.webhooks.constructEvent(body); } }
Controller
Code// src/saligpay/saligpay.controller.ts import { Controller, Post, Body, Res, HttpStatus } from "@nestjs/common"; import { Response } from "express"; import { SaligPayService } from "./saligpay.service"; import { OrdersService } from "../orders/orders.service"; @Controller("webhooks") export class WebhooksController { constructor( private saligpayService: SaligPayService, private ordersService: OrdersService, ) {} @Post("saligpay") async handleWebhook(@Body() body: unknown, @Res() res: Response) { try { const payload = this.saligpayService.parseWebhook(body); if (payload.status === "COMPLETED") { await this.ordersService.markAsPaid(payload.externalId); } return res.status(HttpStatus.OK).json({ received: true }); } catch (error) { return res.status(HttpStatus.BAD_REQUEST).json({ error: "Invalid webhook", }); } } }
Elysia (Bun)
Code// src/index.ts import { Elysia } from "elysia"; import { SaligPay } from "saligpay-node"; const saligpay = new SaligPay({ clientId: process.env.SALIGPAY_CLIENT_ID!, clientSecret: process.env.SALIGPAY_CLIENT_SECRET!, env: "sandbox", }); const app = new Elysia() .post("/checkout", async ({ body }) => { await saligpay.ensureAuthenticated(); const checkout = await saligpay.checkout.create({ externalId: `order-${Date.now()}`, amount: body.amount, description: body.description, webhookUrl: `${process.env.APP_URL}/webhooks/saligpay`, returnUrl: `${process.env.APP_URL}/success`, contact: { name: body.name, email: body.email }, }); return { checkoutUrl: checkout.checkoutUrl }; }) .post("/webhooks/saligpay", async ({ body, set }) => { try { const payload = saligpay.webhooks.constructEvent(body); if (payload.status === "COMPLETED") { console.log(`Payment ${payload.externalId} completed!`); } return { received: true }; } catch { set.status = 400; return { error: "Invalid webhook" }; } }) .listen(3000); console.log(`Server running at ${app.server?.hostname}:${app.server?.port}`);
Last modified on
