Gateway Interface
The PaymentGateway contract — method signatures, parameter types, return types, and how each method fits into the payment lifecycle.
Gateway Interface
Audience: Backend developers implementing or consuming payment gateways Scope: Interface contract, parameter types, return types, method responsibilities
The PaymentGateway Interface
Every payment gateway must implement this interface:
// apps/api/src/modules/payment/payment.interface.ts
export type InitiationType = "form_post" | "redirect" | "sdk";
export interface PaymentGateway {
readonly name: string;
initiate(params: InitiatePaymentParams): Promise<InitiatePaymentResult>;
verify(params: VerifyPaymentParams): Promise<VerifyPaymentResult>;
}initiate() — Start a Payment
Called when a user clicks "Pay". The gateway generates a signed payload that the frontend uses to send the user to the payment portal.
Parameters
export interface InitiatePaymentParams {
amountNpr: number; // Payment amount in Nepali Rupees
referenceId: number; // ID of the entity being paid for (e.g., subscription ID)
referenceType: string; // Entity type (e.g., "subscription", "order")
userId: string; // The paying user's ID
returnUrl: string; // Where the gateway should redirect after payment
}Return Type
export interface InitiatePaymentResult {
gatewayTransactionId: string; // Unique ID for tracking this transaction
initiationType: InitiationType; // How the frontend should start the payment
redirectUrl: string; // Gateway URL (form action or redirect target)
gatewayPayload: Record<string, string>; // Signed form fields / query params
}initiationType Values
| Value | Frontend Behavior | Example Gateways |
|---|---|---|
form_post | Build a hidden <form>, set action = redirectUrl, add gatewayPayload as hidden inputs, submit via POST | eSewa, ConnectIPS |
redirect | Append gatewayPayload as query params to redirectUrl, navigate via window.location.href | Fonepay |
sdk | Pass gatewayPayload values to the gateway's client-side SDK | Khalti (future) |
gatewayPayload — What Goes In Here
The payload contains the exact field names the gateway expects. For form-based gateways, these become the hidden form input names. Examples:
eSewa (form_post):
{
"amount": "500",
"tax_amount": "0",
"total_amount": "500",
"transaction_uuid": "42-1708000000000",
"product_code": "EPAYTEST",
"product_service_charge": "0",
"product_delivery_charge": "0",
"success_url": "https://myapp.com/payment/success",
"failure_url": "https://myapp.com/payment/failure",
"signed_field_names": "total_amount,transaction_uuid,product_code",
"signature": "kI4GnQx9..."
}Fonepay (redirect):
{
"PID": "MERCHANT123",
"MD": "P",
"PRN": "ref-42",
"AMT": "500",
"CRN": "NPR",
"DT": "02/22/2026",
"R1": "Subscription purchase",
"R2": "",
"RU": "https://myapp.com/payment/callback",
"DV": "a1b2c3d4e5..."
}ConnectIPS (form_post):
{
"MERCHANTID": "123",
"APPID": "app-001",
"APPNAME": "BullHouse",
"TXNID": "txn-42-1708000000000",
"TXNDATE": "22-02-2026",
"TXNCRNCY": "NPR",
"TXNAMT": "50000",
"REFERENCEID": "sub-42",
"REMARKS": "Subscription purchase",
"PARTICULARS": "",
"TOKEN": "sha256rsabase64..."
}The frontend doesn't need to know what these fields mean — it just iterates and builds the form/URL.
verify() — Confirm a Payment
Called when the user returns from the gateway. The gateway verifies the callback data is authentic and the payment was successful.
Parameters
export interface VerifyPaymentParams {
gatewayTransactionId: string; // The ID returned by initiate()
gatewayResponse: Record<string, unknown>; // Decoded callback data from the gateway
}The gatewayResponse is the raw data the frontend received from the gateway's redirect callback. For eSewa, this is the decoded base64 JSON from the success URL. For Fonepay, these are the callback query parameters. The frontend decodes and passes them through.
Return Type
export interface VerifyPaymentResult {
success: boolean; // Whether the payment was verified successfully
amountNpr: number; // The verified amount (used for cross-checking)
}What verify() Should Do
- Validate the response signature — recompute the HMAC/signature from the callback data using the secret key, compare with the received signature
- Call the gateway's server-side status API — never trust client-side callback data alone; always double-check with a server-to-server call
- Return the verified amount — the consumer (e.g., SubscriptionMobileService) can cross-check this against the expected amount
Security Requirements
| Requirement | Why |
|---|---|
| Always verify the callback signature | Callback data comes through the client — it could be tampered with |
| Always call the server-side status API | Provides independent confirmation that the payment actually happened |
| Never expose the secret key to the frontend | The backend signs payloads and verifies responses; the secret never leaves the server |
| Return the verified amount | Allows the consumer to detect amount mismatches (e.g., user paid less than expected) |
Full Lifecycle
Example: How SubscriptionMobileService Uses It
async initiatePurchase(userId, planId, gateway, returnUrl) {
// 1. Create the subscription in pending state
const result = await this.subscriptionService.createPendingPurchase(userId, planId);
// 2. Create a pending payment record
const paymentRecord = await this.paymentService.createPaymentRecord(
result.subscription.id,
"subscription",
userId,
result.plan.priceNpr,
gateway,
);
// 3. Ask the gateway to generate signed payload
const gatewayInstance = this.paymentService.getGateway(gateway);
const gatewayResult = await gatewayInstance.initiate({
amountNpr: result.plan.priceNpr,
referenceId: result.subscription.id,
referenceType: "subscription",
userId,
returnUrl,
});
// 4. Return everything the frontend needs
return {
subscription: result.subscription,
payment: paymentRecord,
gatewayTransactionId: gatewayResult.gatewayTransactionId,
initiationType: gatewayResult.initiationType,
redirectUrl: gatewayResult.redirectUrl,
gatewayPayload: gatewayResult.gatewayPayload,
};
}Browser Return Flow
Unified browser payment flow for subscriptions, orders, and future gateways, including the shared payment result page contract and frontend implementation guide.
eSewa ePay v2
Complete reference for the eSewa ePay v2 gateway integration — flow, signature generation, verification, test credentials, and implementation details.