nomadweb/frontend/src/components/ebook/BuyEbook.tsx
eric ff7b721acb feat: NomadCNA community layer, social matching, payments, and ebook
Port meetups, discussions, gigs, dating/chat, VIP pay (ZPay/XorPay), MiroTalk live, digital academy, and ebook reader. Add persistent community_store, git-first deploy docs, and env template for production secrets.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-30 09:38:13 -05:00

51 lines
1.7 KiB
TypeScript

'use client';
import { useState } from 'react';
import { trackEvent } from '@/lib/ebook/analytics';
import { getFeatures } from '@/lib/ebook/features';
import { useT } from '@/components/ebook/LocaleProvider';
export function BuyEbook({ compact = false }: { compact?: boolean }) {
const t = useT();
const [busy, setBusy] = useState(false);
const [error, setError] = useState('');
const productName = getFeatures().payment.checkout.productName;
const buttonText = getFeatures().payment.checkout.buttonText;
const buy = async () => {
setError('');
setBusy(true);
trackEvent('buy_click', { from: compact ? 'reader' : 'cover' });
try {
const res = await fetch('/api/creem/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
});
const data = (await res.json()) as { ok?: boolean; checkoutUrl?: string; error?: string };
if (!res.ok || !data.checkoutUrl) {
throw new Error(data.error || t('payment.checkoutError'));
}
window.location.href = data.checkoutUrl;
} catch (err) {
setError(err instanceof Error ? err.message : t('payment.checkoutFailed'));
setBusy(false);
}
};
return (
<section className={`buy-ebook ${compact ? 'is-compact' : ''}`}>
<h2 className="buy-ebook-title">{t('payment.buyGet', { name: productName })}</h2>
<p className="buy-ebook-sub">{t('payment.buySub')}</p>
<button
type="button"
className="buy-ebook-btn"
disabled={busy}
onClick={buy}
>
{busy ? t('payment.openingCheckout') : buttonText}
</button>
{error ? <p className="buy-ebook-error">{error}</p> : null}
</section>
);
}