mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
feat(antilopay): inject apay-tag meta on cabinet boot
Pairs with the bedolaga-bot commit that exposes
GET /cabinet/public/site-verification (JSON: { apay_tag: string | null }).
New useSiteVerification hook fires once on App mount, fetches the
configured tag value, and upserts <meta name='apay-tag' content='...'>
into document.head. When the bot returns null we proactively remove
any previously-rendered tag so toggling the env var off cleans up the
page.
Failure modes are silent — verification is best-effort and must
never block the cabinet from rendering. No admin UI field is needed:
the value lives in the bot's .env (ANTILOPAY_APAY_VERIFICATION_TAG),
matching how all other Antilopay credentials are configured.
This commit is contained in:
@@ -32,6 +32,7 @@ import { ErrorBoundary } from './components/ErrorBoundary';
|
|||||||
import { PermissionRoute } from '@/components/auth/PermissionRoute';
|
import { PermissionRoute } from '@/components/auth/PermissionRoute';
|
||||||
import { saveReturnUrl } from './utils/token';
|
import { saveReturnUrl } from './utils/token';
|
||||||
import { useAnalyticsCounters } from './hooks/useAnalyticsCounters';
|
import { useAnalyticsCounters } from './hooks/useAnalyticsCounters';
|
||||||
|
import { useSiteVerification } from './hooks/useSiteVerification';
|
||||||
// Auth pages - load immediately (small)
|
// Auth pages - load immediately (small)
|
||||||
import Login from './pages/Login';
|
import Login from './pages/Login';
|
||||||
import TelegramCallback from './pages/TelegramCallback';
|
import TelegramCallback from './pages/TelegramCallback';
|
||||||
@@ -229,6 +230,9 @@ function LegacySubscriptionRedirect() {
|
|||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
useAnalyticsCounters();
|
useAnalyticsCounters();
|
||||||
|
// Pulls site-verification tokens (Antilopay apay-tag etc.) from the bot
|
||||||
|
// backend and injects matching <meta> tags into document.head.
|
||||||
|
useSiteVerification();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
16
src/api/siteVerification.ts
Normal file
16
src/api/siteVerification.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { apiClient } from './client';
|
||||||
|
|
||||||
|
export interface SiteVerification {
|
||||||
|
apay_tag: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public, unauthenticated endpoint — payment-provider crawlers (Antilopay)
|
||||||
|
* need to be able to read these values to validate site ownership.
|
||||||
|
*/
|
||||||
|
export const siteVerificationApi = {
|
||||||
|
async get(): Promise<SiteVerification> {
|
||||||
|
const { data } = await apiClient.get<SiteVerification>('/cabinet/public/site-verification');
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
};
|
||||||
56
src/hooks/useSiteVerification.ts
Normal file
56
src/hooks/useSiteVerification.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { siteVerificationApi } from '../api/siteVerification';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the configured site-verification tokens from the bot backend
|
||||||
|
* and injects the matching `<meta>` tags into `document.head`.
|
||||||
|
*
|
||||||
|
* Used by Antilopay's verification flow (lk.antilopay.com → Проект →
|
||||||
|
* Верификация → Способ 1: мета-тег). Once the merchant copies the
|
||||||
|
* Antilopay-provided value into `ANTILOPAY_APAY_VERIFICATION_TAG`,
|
||||||
|
* the cabinet will start rendering `<meta name="apay-tag" content="...">`
|
||||||
|
* automatically — no rebuild required.
|
||||||
|
*
|
||||||
|
* The hook is no-op on backend errors (verification is a best-effort
|
||||||
|
* concern and must never block the cabinet from rendering).
|
||||||
|
*/
|
||||||
|
export function useSiteVerification(): void {
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false;
|
||||||
|
|
||||||
|
siteVerificationApi
|
||||||
|
.get()
|
||||||
|
.then((data) => {
|
||||||
|
if (cancelled) return;
|
||||||
|
if (data.apay_tag) {
|
||||||
|
upsertMetaTag('apay-tag', data.apay_tag);
|
||||||
|
} else {
|
||||||
|
removeMetaTag('apay-tag');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// Silent fail — verification meta is best-effort, doesn't block UI.
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
function upsertMetaTag(name: string, content: string): void {
|
||||||
|
let tag = document.head.querySelector<HTMLMetaElement>(`meta[name="${name}"]`);
|
||||||
|
if (!tag) {
|
||||||
|
tag = document.createElement('meta');
|
||||||
|
tag.setAttribute('name', name);
|
||||||
|
document.head.appendChild(tag);
|
||||||
|
}
|
||||||
|
tag.setAttribute('content', content);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeMetaTag(name: string): void {
|
||||||
|
const tag = document.head.querySelector<HTMLMetaElement>(`meta[name="${name}"]`);
|
||||||
|
if (tag) {
|
||||||
|
tag.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user