PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.14.2
Yatra – Travel Booking & Tour Operator Software v3.0.14.2
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
yatra / resources / js / lib / invoice-download.ts

invoice-download.ts in Yatra – Travel Booking & Tour Operator Software 3.0.14.2, at resources/js/lib/invoice-download.ts

120 lines 3.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Admin invoice download helper.
3 *
4 * Calls the public invoice REST endpoint with the WordPress admin nonce + cookie,
5 * so administrators can trigger an invoice PDF download from the admin UI without
6 * any extra permission/login round-trip. The same endpoint is used on the
7 * customer account page (with the same auth pattern), and supports an HMAC
8 * `invoice_token` for guests on the booking confirmation page.
9 */
10
11 interface AdminRestConfig {
12 base: string;
13 nonce: string;
14 }
15
16 function getAdminRestConfig(): AdminRestConfig {
17 const w = window as unknown as {
18 yatraAdmin?: { restUrl?: string; nonce?: string };
19 };
20 const raw = w?.yatraAdmin?.restUrl || "/wp-json";
21 const base = raw.endsWith("/") ? raw.slice(0, -1) : raw;
22 const nonce = w?.yatraAdmin?.nonce || "";
23 return { base, nonce };
24 }
25
26 function buildInvoiceUrl(base: string, paymentId: number): string {
27 const suffix = `/yatra/v1/payment/${paymentId}/invoice`;
28 const origin =
29 typeof window !== "undefined" ? window.location.origin : "http://localhost";
30
31 let u: URL;
32 try {
33 u = new URL(base, origin);
34 } catch {
35 return `${base.replace(/\/$/, "")}${suffix}?download=1`;
36 }
37
38 // Plain permalinks: /wp-json is exposed as ?rest_route=
39 if (u.searchParams.has("rest_route")) {
40 const route = (u.searchParams.get("rest_route") || "").replace(/\/$/, "");
41 u.searchParams.set("rest_route", `${route}${suffix}`);
42 u.searchParams.set("download", "1");
43 return u.toString();
44 }
45
46 u.pathname = (u.pathname || "").replace(/\/$/, "") + suffix;
47 u.searchParams.set("download", "1");
48 return u.toString();
49 }
50
51 async function readError(res: Response): Promise<string> {
52 let msg = res.statusText || "Request failed";
53 try {
54 const j = await res.json();
55 if (j?.message && typeof j.message === "string") {
56 msg = j.message;
57 }
58 } catch {
59 const t = await res.text().catch(() => "");
60 if (t && t.length < 300) {
61 msg = t;
62 }
63 }
64 return msg;
65 }
66
67 /**
68 * Download a payment invoice PDF as a file.
69 *
70 * Authentication is handled server-side via the admin REST nonce + cookie
71 * (administrators bypass the ownership check inside `download_invoice`).
72 */
73 export async function downloadAdminInvoice(paymentId: number): Promise<void> {
74 if (!paymentId || paymentId <= 0) {
75 throw new Error("Invalid payment ID");
76 }
77
78 const { base, nonce } = getAdminRestConfig();
79 if (!nonce) {
80 throw new Error("Missing REST nonce; please reload the page.");
81 }
82
83 const url = buildInvoiceUrl(base, paymentId);
84 const res = await fetch(url, {
85 method: "GET",
86 credentials: "include",
87 headers: {
88 "X-WP-Nonce": nonce,
89 Accept: "application/pdf, application/octet-stream, */*",
90 },
91 });
92
93 if (!res.ok) {
94 throw new Error(await readError(res));
95 }
96
97 const blob = await res.blob();
98 const dispo = res.headers.get("Content-Disposition");
99 let filename = `invoice-${paymentId}.pdf`;
100 if (dispo) {
101 const m = /filename\*?=(?:UTF-8'')?["']?([^";\n]+)/i.exec(dispo);
102 if (m?.[1]) {
103 try {
104 filename = decodeURIComponent(m[1].replace(/['"]/g, "").trim());
105 } catch {
106 filename = m[1].replace(/['"]/g, "").trim();
107 }
108 }
109 }
110
111 const objectUrl = URL.createObjectURL(blob);
112 const link = document.createElement("a");
113 link.href = objectUrl;
114 link.download = filename;
115 document.body.appendChild(link);
116 link.click();
117 document.body.removeChild(link);
118 URL.revokeObjectURL(objectUrl);
119 }
120