# yatra/3.0.17/resources/js/lib/invoice-download.ts

Yatra – Travel Booking &amp; Tour Operator Software, version 3.0.17. 152 lines.

- Page: https://pluginprobe.com/plugins/yatra/3.0.17/code/resources/js/lib/invoice-download.ts
- Raw: https://pluginprobe.com/plugins/yatra/3.0.17/raw/resources/js/lib/invoice-download.ts
- Modified: 2026-09-29T13:55:38+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/yatra/3.0.17/code/resources/js/lib/invoice-download.ts#L10-L20`.

```typescript
/**
 * Admin invoice download helper.
 *
 * Calls the public invoice REST endpoint with the WordPress admin nonce + cookie,
 * so administrators can trigger an invoice PDF download from the admin UI without
 * any extra permission/login round-trip. The same endpoint is used on the
 * customer account page (with the same auth pattern), and supports an HMAC
 * `invoice_token` for guests on the booking confirmation page.
 */

interface AdminRestConfig {
  base: string;
  nonce: string;
}

function getAdminRestConfig(): AdminRestConfig {
  const w = window as unknown as {
    yatraAdmin?: { restUrl?: string; nonce?: string };
  };
  const raw = w?.yatraAdmin?.restUrl || "/wp-json";
  const base = raw.endsWith("/") ? raw.slice(0, -1) : raw;
  const nonce = w?.yatraAdmin?.nonce || "";
  return { base, nonce };
}

function buildInvoiceUrl(base: string, suffix: string): string {
  const origin =
    typeof window !== "undefined" ? window.location.origin : "http://localhost";

  let u: URL;
  try {
    u = new URL(base, origin);
  } catch {
    return `${base.replace(/\/$/, "")}${suffix}?download=1`;
  }

  // Plain permalinks: /wp-json is exposed as ?rest_route=
  if (u.searchParams.has("rest_route")) {
    const route = (u.searchParams.get("rest_route") || "").replace(/\/$/, "");
    u.searchParams.set("rest_route", `${route}${suffix}`);
    u.searchParams.set("download", "1");
    return u.toString();
  }

  u.pathname = (u.pathname || "").replace(/\/$/, "") + suffix;
  u.searchParams.set("download", "1");
  return u.toString();
}

async function readError(res: Response): Promise<string> {
  let msg = res.statusText || "Request failed";
  try {
    const j = await res.json();
    if (j?.message && typeof j.message === "string") {
      msg = j.message;
    }
  } catch {
    const t = await res.text().catch(() => "");
    if (t && t.length < 300) {
      msg = t;
    }
  }
  return msg;
}

/**
 * Fetch an invoice PDF from `suffix` and save it, falling back to
 * `fallbackName` when the response carries no Content-Disposition.
 *
 * Authentication is handled server-side via the admin REST nonce + cookie
 * (administrators bypass the ownership check inside the endpoint).
 */
async function downloadInvoicePdf(
  suffix: string,
  fallbackName: string,
): Promise<void> {
  const { base, nonce } = getAdminRestConfig();
  if (!nonce) {
    throw new Error("Missing REST nonce; please reload the page.");
  }

  const url = buildInvoiceUrl(base, suffix);
  const res = await fetch(url, {
    method: "GET",
    credentials: "include",
    headers: {
      "X-WP-Nonce": nonce,
      Accept: "application/pdf, application/octet-stream, */*",
    },
  });

  if (!res.ok) {
    throw new Error(await readError(res));
  }

  const blob = await res.blob();
  const dispo = res.headers.get("Content-Disposition");
  let filename = fallbackName;
  if (dispo) {
    const m = /filename\*?=(?:UTF-8'')?["']?([^";\n]+)/i.exec(dispo);
    if (m?.[1]) {
      try {
        filename = decodeURIComponent(m[1].replace(/['"]/g, "").trim());
      } catch {
        filename = m[1].replace(/['"]/g, "").trim();
      }
    }
  }

  const objectUrl = URL.createObjectURL(blob);
  const link = document.createElement("a");
  link.href = objectUrl;
  link.download = filename;
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
  URL.revokeObjectURL(objectUrl);
}

/**
 * Download a payment invoice (receipt for one recorded payment).
 */
export async function downloadAdminInvoice(paymentId: number): Promise<void> {
  if (!paymentId || paymentId <= 0) {
    throw new Error("Invalid payment ID");
  }

  await downloadInvoicePdf(
    `/yatra/v1/payment/${paymentId}/invoice`,
    `invoice-${paymentId}.pdf`,
  );
}

/**
 * Download a booking invoice — the whole booking rather than a single
 * payment, so it also works for bookings with no payment recorded yet
 * (bank transfer, pay later), where it renders as a pro-forma invoice with
 * the gateway's payment instructions.
 */
export async function downloadAdminBookingInvoice(
  bookingId: number,
): Promise<void> {
  if (!bookingId || bookingId <= 0) {
    throw new Error("Invalid booking ID");
  }

  await downloadInvoicePdf(
    `/yatra/v1/booking/${bookingId}/invoice`,
    `invoice-booking-${bookingId}.pdf`,
  );
}

```
