feat: ( dmoney ) create redirect page to dmoney

This commit is contained in:
Abubeker Yasin
2026-07-01 15:29:49 +03:00
parent 0015f645cf
commit 33d90df5a9
2 changed files with 104 additions and 0 deletions

View File

@@ -1,5 +1,9 @@
# API Configuration
NEXT_PUBLIC_API_URL=https://your-api-domain.com
# Allowlisted destination hosts for the /go D-Money redirect bounce page (comma-separated).
# The /go?url=... page only forwards to https hosts that match one of these (host or subdomain).
NEXT_PUBLIC_DMONEY_ALLOWED_HOSTS=d-money.dj
# GitHub Packages Token
GITHUB_PACKAGE_TOKEN=$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

View File

@@ -0,0 +1,100 @@
"use client";
import { Suspense, useEffect, useMemo } from "react";
import { useSearchParams } from "next/navigation";
import { Loader2, ShieldAlert, ExternalLink } from "lucide-react";
const ALLOWED_HOSTS = (
process.env.NEXT_PUBLIC_DMONEY_ALLOWED_HOSTS ?? "d-money.dj"
)
.split(",")
.map((h) => h.trim().toLowerCase())
.filter(Boolean);
/** True only for https URLs whose host is (or is a subdomain of) an allowlisted host. */
function isTrustedDMoneyUrl(raw: string | null): raw is string {
if (!raw) return false;
let parsed: URL;
try {
parsed = new URL(raw);
} catch {
return false;
}
if (parsed.protocol !== "https:") return false;
const host = parsed.hostname.toLowerCase();
return ALLOWED_HOSTS.some(
(allowed) => host === allowed || host.endsWith(`.${allowed}`),
);
}
const REDIRECT_DELAY_MS = 1000;
function RedirectView() {
const searchParams = useSearchParams();
const raw = searchParams.get("url");
const target = useMemo(() => (isTrustedDMoneyUrl(raw) ? raw : null), [raw]);
useEffect(() => {
if (!target) return;
const timer = setTimeout(() => {
window.location.replace(target);
}, REDIRECT_DELAY_MS);
return () => clearTimeout(timer);
}, [target]);
if (!target) {
return (
<div className="w-full max-w-sm text-center">
<div className="mx-auto mb-5 flex h-16 w-16 items-center justify-center rounded-full bg-red-100">
<ShieldAlert className="h-8 w-8 text-red-600" />
</div>
<h1 className="mb-2 text-xl font-bold text-gray-900">
Can&apos;t continue
</h1>
<p className="text-sm text-gray-500">
This link is missing a valid D-Money checkout address or points to an
untrusted destination. Please start the payment again from the app.
</p>
</div>
);
}
return (
<div className="w-full max-w-sm text-center">
<div className="mx-auto mb-5 flex h-16 w-16 items-center justify-center rounded-full bg-primary/10">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
</div>
<h1 className="mb-2 text-xl font-bold text-gray-900">
Redirecting to D-Money
</h1>
<p className="text-sm text-gray-500">
Taking you to the secure D-Money checkout to complete your payment
</p>
<a
href={target}
className="mt-6 inline-flex items-center justify-center gap-2 text-sm font-medium text-primary hover:underline"
>
Continue to D-Money
<ExternalLink className="h-4 w-4" />
</a>
</div>
);
}
export default function GoPage() {
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-white px-4">
<Suspense
fallback={
<Loader2 className="h-8 w-8 animate-spin text-primary" aria-label="Loading" />
}
>
<RedirectView />
</Suspense>
</div>
);
}