Skip to content

Payment Verification

Only needed if you built your own result page. If you use the HGN hosted result page, verification is handled for you and you can skip this page.


What happens after a customer pays

  1. The customer clicks Pay in the widget.
  2. The widget creates a payment session against the configured gateway — CyberSource for card payments, Fonepay for QR payments.
  3. The top-level browser window navigates to the gateway's hosted payment page.
  4. The customer completes the card payment or scans the QR code.
  5. The gateway redirects the customer to your configured return URL, or to the HGN hosted fallback page.
  6. That result page receives status and paymentRef query parameters.
https://your-agency.com/booking-complete?status=success&paymentRef=ORD-2026-000058
Parameter Values
status success, failed, cancelled, or expired
paymentRef The order reference, used for verification

The booking exists before payment

The booking is created server-side before payment is attempted. A successful payment transitions the booking status. If payment fails, the booking stays pending payment and the customer can retry — a failed payment does not lose their work.


Never trust the query string

This is a security requirement, not a suggestion

Anyone can type ?status=success into their address bar and load your result page. If your page shows a confirmation based on that parameter alone, you will confirm bookings that were never paid for.

Always verify server-side using paymentRef before showing a final confirmation.

Use the verified response — never the query string — to drive anything that matters: confirmation screens, emails, releasing inventory, or updating your own records.


The verification endpoint

GET https://book.himalayanguardian.com/api/public/book/payment-status/{paymentRef}

Call this from your server, not from browser JavaScript, and use its response to decide what the customer sees.


Examples

app.get("/booking-complete", async (req, res) => {
  const { paymentRef } = req.query;

  if (!paymentRef) {
    return res.status(400).render("error");
  }

  // Verify server-side. The `status` query param is NOT trusted.
  const verify = await fetch(
    `https://book.himalayanguardian.com/api/public/book/payment-status/${encodeURIComponent(paymentRef)}`
  );

  if (!verify.ok) {
    // Could not confirm — show a neutral "we're checking" page,
    // never a confirmation.
    return res.render("pending", { paymentRef });
  }

  const result = await verify.json();

  switch (result.status) {
    case "success":
      await fulfilBooking(paymentRef);   // emails, records, inventory
      return res.render("success", { paymentRef });
    case "failed":
      return res.render("failed", { paymentRef });
    case "cancelled":
      return res.render("cancelled", { paymentRef });
    case "expired":
      return res.render("expired", { paymentRef });
    default:
      return res.render("pending", { paymentRef });
  }
});
app/booking-complete/page.tsx
export default async function BookingComplete({
  searchParams,
}: {
  searchParams: Promise<{ paymentRef?: string }>;
}) {
  const { paymentRef } = await searchParams;
  if (!paymentRef) return <ErrorState />;

  // Runs on the server. Never trust searchParams.status.
  const res = await fetch(
    `https://book.himalayanguardian.com/api/public/book/payment-status/${encodeURIComponent(paymentRef)}`,
    { cache: "no-store" }
  );

  if (!res.ok) return <PendingState paymentRef={paymentRef} />;

  const { status } = await res.json();

  if (status === "success") return <SuccessState paymentRef={paymentRef} />;
  if (status === "failed") return <FailedState paymentRef={paymentRef} />;
  if (status === "cancelled") return <CancelledState paymentRef={paymentRef} />;
  if (status === "expired") return <ExpiredState paymentRef={paymentRef} />;

  return <PendingState paymentRef={paymentRef} />;
}
booking-complete.php
<?php
$paymentRef = $_GET['paymentRef'] ?? null;

if (!$paymentRef) {
    http_response_code(400);
    exit('Missing payment reference');
}

// Verify server-side. $_GET['status'] is NOT trusted.
$url = 'https://book.himalayanguardian.com/api/public/book/payment-status/'
     . rawurlencode($paymentRef);

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($code !== 200) {
    include 'views/pending.php';   // never a confirmation
    exit;
}

$result = json_decode($body, true);

switch ($result['status'] ?? '') {
    case 'success':
        fulfil_booking($paymentRef);
        include 'views/success.php';
        break;
    case 'failed':    include 'views/failed.php';    break;
    case 'cancelled': include 'views/cancelled.php'; break;
    case 'expired':   include 'views/expired.php';   break;
    default:          include 'views/pending.php';
}
import requests
from flask import request, render_template

VERIFY_URL = "https://book.himalayanguardian.com/api/public/book/payment-status/{}"


@app.route("/booking-complete")
def booking_complete():
    payment_ref = request.args.get("paymentRef")
    if not payment_ref:
        return render_template("error.html"), 400

    # Verify server-side. request.args["status"] is NOT trusted.
    try:
        res = requests.get(VERIFY_URL.format(payment_ref), timeout=15)
        res.raise_for_status()
    except requests.RequestException:
        return render_template("pending.html", payment_ref=payment_ref)

    status = res.json().get("status")

    if status == "success":
        fulfil_booking(payment_ref)
        return render_template("success.html", payment_ref=payment_ref)

    return render_template(
        {
            "failed": "failed.html",
            "cancelled": "cancelled.html",
            "expired": "expired.html",
        }.get(status, "pending.html"),
        payment_ref=payment_ref,
    )

Designing the result page

Handle all four states. Customers reach failed and cancelled far more often than teams expect.

State What to show
success Confirmation, order reference, what happens next
failed Plain explanation and a clear way to retry. The booking is still pending, so retrying works
cancelled Acknowledge the customer backed out, offer a link back to the booking page
expired Explain the session timed out and offer to start again
Verification unavailable A neutral "we are confirming your payment" message. Never a confirmation

Always show the payment reference

Whatever the state, display paymentRef on the page. It is the first thing HGN support will ask for.


Next steps