Shop It Docs
Developer ResourcesPayment

Fonepay PG Web Redirect

Complete reference for the Fonepay PG web redirect gateway integration — flow, signature generation, verification, env vars, and implementation details.

Fonepay PG Web Redirect

Audience: Backend developers, QA engineers Scope: Fonepay PG web redirect integration, HMAC-SHA512 signature, XML verification, test environment


Overview

Fonepay is a major Nepali payment gateway. The PG web redirect flow uses HMAC-SHA512 to sign a GET redirect URL. The backend generates the URL; the frontend navigates to it directly (no form POST).

PropertyValue
Gateway namefonepay
Initiation typeredirect
Signature algorithmHMAC-SHA512 (hex)
Amount formatNPR rupees (string, 2 decimal places)
Verification responseXML

Payment Flow


Initiation Signature

The backend computes DV as HMAC-SHA512 of the initiation parameters in a specific order:

message = `${PID},${MD},${PRN},${AMT},${CRN},${DT},${R1},${R2},${RU}`
DV = HMAC-SHA512(merchantSecret, message).hex()

Redirect URL Parameters

ParamDescriptionExample
PIDMerchant codeTEST_MERCHANT
MDPayment mode (always P)P
PRNUnique transaction referencefonepay-42-1750464000000
AMTAmount in NPR major unit3266.99
CRNCurrency (always NPR)NPR
DTDate in MM/DD/YYYY format06/21/2026
R1Remarks 1 (reference type)order
R2Remarks 2 (reference ID)42
DVHMAC-SHA512 signature (hex)a1b2c3d4...
RUCallback URLhttps://api.example.com/api/payments/redirect/1/success

Callback Parameters

After the user completes payment, Fonepay redirects to RU with these query params:

ParamDescriptionExample
PRNTransaction referencefonepay-42-1750464000000
PIDMerchant codeTEST_MERCHANT
PSPayment statuscompleted
RCResponse code00
UIDUnique transaction IDuid-abc-123
BCBank codeNMB
INIInitiatorfonepay
P_AMTPaid amount3266.99
R_AMTRefund amount0
DVHMAC-SHA512 signature (hex, uppercase)A1B2C3D4...

Callback DV Validation

Before calling the verification API, the backend validates the callback DV to detect tampering:

responseString = `${PRN},${PID},${PS},${RC},${UID},${BC},${INI},${P_AMT},${R_AMT}`
expectedDV = HMAC-SHA512(merchantSecret, responseString).hex().toUpperCase()

The comparison uses timingSafeEqual() to prevent timing attacks. If the DV does not match, the callback is rejected with PAYMENT_FONEPAY_CALLBACK_INVALID.


Verification Request

After validating the callback DV, the backend calls Fonepay's server-to-server verification endpoint:

dvString = `${PID},${AMT},${PRN},${BID},${UID}`
DV = HMAC-SHA512(merchantSecret, dvString).hex()
GET {FONEPAY_PG_URL}/api/merchantRequest/verificationMerchant?PRN=...&PID=...&BID=...&AMT=...&UID=...&DV=...

The request has a configurable timeout (default 10s) and retries up to 3 times with full-jitter exponential backoff on network errors and transient HTTP errors (502/503/504/408).


XML Verification Response

The verification endpoint returns XML:

<response>
    <success>true</success>
    <response_code>successful</response_code>
    <txnAmount>3266.99</txnAmount>
    <amount>3266.99</amount>
    <bankCode>NMB</bankCode>
    <initiator>fonepay</initiator>
    <message>Successful</message>
    <statusCode>200</statusCode>
    <uniqueId>uid-abc-123</uniqueId>
</response>

The payment is marked completed only when:

String(parsed.response.success).toLowerCase() === "true" &&
String(parsed.response.response_code).toLowerCase() === "successful"

The amount is extracted from txnAmount and converted from major unit string to minor unit (paisa) via majorUnitStringToMinorUnit().


Configuration

Environment Variables

VariableRequiredDescriptionExample
FONEPAY_PG_URLBase URL for Fonepay PG APIhttps://clientapi.fonepay.com
FONEPAY_PG_MERCHANT_CODEMerchant PID/code from Fonepay portalyour_merchant_code
FONEPAY_PG_MERCHANT_SECRETHMAC-SHA512 signing secretyour_merchant_secret
FONEPAY_VERIFY_TIMEOUT_MSoptionalVerification request timeout in ms10000 (default)

Test / Sandbox Environment

Fonepay provides sandbox credentials from the merchant portal. There are no publicly documented test credentials for the Fonepay sandbox. Contact Fonepay to obtain test merchant credentials.


Frontend Integration

When initiationType is "redirect", the frontend navigates directly to the returned URL:

if (data.initiationType === "redirect") {
  window.location.href = data.redirectUrl;
}

Fonepay is a GET redirect gateway (no form POST). The redirectUrl contains all signed parameters as query string values. The gatewayPayload is empty ({}) for redirect-type gateways.


Implementation Reference

Gateway file: apps/api/src/modules/payment/gateways/fonepay.gateway.ts

Key methods:

  • initiate() — generates PRN, computes HMAC-SHA512 DV, builds redirect URL with all query params
  • parseRedirectPayload() — validates callback DV using timingSafeEqual, extracts PRN as gatewayTransactionId
  • verify() — builds verification request DV, calls Fonepay verification endpoint with retry + timeout, parses XML response, returns { success, amountNpr }
  • retryFetch() — bounded retry helper (3 attempts, full-jitter exponential backoff)
  • computeHmac() — HMAC-SHA512 helper
  • safeEqual()timingSafeEqual wrapper with buffer length safety check

Security Checklist

  • HMAC-SHA512 signature computed server-side — secret key never sent to frontend
  • Callback DV validated via timingSafeEqual before any processing
  • Server-to-server verification call for independent confirmation
  • Bounded retry with exponential backoff on transient verification failures
  • Explicit timeout on all external HTTP calls (default 10s)
  • Secret key stored in environment variable (FONEPAY_PG_MERCHANT_SECRET), never hardcoded
  • No sensitive data logged (merchant secret never in log output)
  • Circuit breaker noted as tech-debt (tracked in feature plan)