Refactor: Fully migrated to direct PostgreSQL, implemented Public API v1, fixed Vercel deployment conflicts, and updated documentation

This commit is contained in:
mstfyldz
2026-03-12 21:54:57 +03:00
parent 321f25a15c
commit 515d513c1f
29 changed files with 1002 additions and 675 deletions

View File

@@ -12,54 +12,81 @@ import Link from 'next/link';
function CheckoutContent() {
const searchParams = useSearchParams();
const amount = parseFloat(searchParams.get('amount') || '100');
const currency = searchParams.get('currency') || 'TL';
const refId = searchParams.get('ref_id') || 'SEC-99231-TX';
const callbackUrl = searchParams.get('callback_url') || '/';
const merchantId = searchParams.get('merchant_id') || null;
const sessionId = searchParams.get('session_id');
const amountParam = parseFloat(searchParams.get('amount') || '0');
const currencyParam = searchParams.get('currency') || 'TL';
const refIdParam = searchParams.get('ref_id') || 'TX-DEFAULT';
const callbackUrlParam = searchParams.get('callback_url') || '/';
const merchantIdParam = searchParams.get('merchant_id') || null;
const [clientSecret, setClientSecret] = useState<string | null>(null);
const [paymentData, setPaymentData] = useState<any>(null);
const [error, setError] = useState<string | null>(null);
const [paymentMethod, setPaymentMethod] = useState<'card' | 'crypto'>('card');
const [paymentMethod, setPaymentMethod] = useState<'card' | 'crypto'>('crypto');
const [merchantName, setMerchantName] = useState<string>('Yükleniyor...');
const [displayAmount, setDisplayAmount] = useState<number>(0);
const [displayCurrency, setDisplayCurrency] = useState<string>('TRY');
const isMock = process.env.NEXT_PUBLIC_USE_MOCK_PAYMENTS === 'true';
useEffect(() => {
if (amount <= 0) {
setError('Geçersiz işlem tutarı.');
return;
}
async function initializeCheckout() {
try {
if (sessionId) {
// Fetch data from existing transaction session
const res = await fetch(`/api/transactions/${sessionId}/details`);
const data = await res.json();
if (data.error) {
setError(data.error);
return;
}
fetch('/api/create-payment-intent', {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
amount,
currency,
ref_id: refId,
callback_url: callbackUrl,
merchant_id: merchantId
}),
})
.then((res) => res.json())
.then((data) => {
if (data.error) {
setError(data.error);
} else {
setClientSecret(data.clientSecret);
setPaymentData(data);
// Auto-redirect if it's a redirect action
setClientSecret(data.clientSecret);
setMerchantName(data.merchant_name);
setDisplayAmount(data.amount);
setDisplayCurrency(data.currency);
if (data.nextAction === 'redirect' && data.redirectUrl) {
setTimeout(() => {
window.location.href = data.redirectUrl;
}, 2000); // 2 second delay to show the message
}, 2000);
}
} else if (amountParam > 0) {
// Legacy flow: create on the fly
const res = await fetch('/api/create-payment-intent', {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
amount: amountParam,
currency: currencyParam,
ref_id: refIdParam,
callback_url: callbackUrlParam,
merchant_id: merchantIdParam
}),
});
const data = await res.json();
if (data.error) {
setError(data.error);
} else {
setClientSecret(data.clientSecret);
setPaymentData(data);
setDisplayAmount(amountParam);
setDisplayCurrency(currencyParam);
setMerchantName('Yetkili Satıcı');
}
} else {
setError('Geçersiz işlem parametreleri.');
}
})
.catch(() => setError('Ödeme başlatılamadı. Lütfen tekrar deneyin.'));
}, [amount, currency, refId, callbackUrl]);
} catch (err) {
setError('Ödeme başlatılamadı. Lütfen tekrar deneyin.');
}
}
initializeCheckout();
}, [sessionId, amountParam, currencyParam, refIdParam, callbackUrlParam, merchantIdParam]);
if (error) {
return (
@@ -121,7 +148,7 @@ function CheckoutContent() {
<div className="pt-8 border-t border-white/10 flex flex-col sm:flex-row sm:items-center gap-8">
<div>
<p className="text-gray-400 text-[10px] font-bold uppercase tracking-wider mb-1">Satıcı</p>
<p className="text-white font-medium text-sm">Ayris Digital Media INC.</p>
<p className="text-white font-medium text-sm">{merchantName}</p>
</div>
<div>
<p className="text-gray-400 text-[10px] font-bold uppercase tracking-wider mb-1">Destek</p>
@@ -141,8 +168,8 @@ function CheckoutContent() {
</div>
) : (
<div className="w-full max-w-lg">
{/* Payment Method Selector */}
<div className="flex bg-gray-100 p-1.5 rounded-2xl mb-8">
{/* Payment Method Selector (Hidden for now, only Crypto active) */}
<div className="flex bg-gray-100 p-1.5 rounded-2xl mb-8 hidden">
<button
onClick={() => setPaymentMethod('card')}
className={`flex-1 flex items-center justify-center gap-2 py-3 rounded-xl text-[10px] font-black uppercase tracking-widest transition-all ${paymentMethod === 'card' ? 'bg-white text-gray-900 shadow-sm' : 'text-gray-400'}`}
@@ -158,13 +185,15 @@ function CheckoutContent() {
</div>
{paymentMethod === 'crypto' ? (
<CryptoCheckout
amount={amount}
currency={currency}
txId={paymentData?.clientSecret || 'TX-8231'}
<CryptoCheckout
amount={displayAmount}
currency={displayCurrency}
txId={paymentData?.id || 'TX-DYNAMIC'}
wallets={paymentData?.wallets}
onSuccess={(hash) => {
setTimeout(() => {
window.location.href = `${callbackUrl}?status=success&tx_hash=${hash}`;
const url = paymentData?.callback_url || '/';
window.location.href = `${url}${url.includes('?') ? '&' : '?'}status=success&tx_hash=${hash}`;
}, 2000);
}}
/>
@@ -188,13 +217,13 @@ function CheckoutContent() {
</button>
</div>
) : isMock ? (
<MockCheckoutForm amount={amount} currency={currency} callbackUrl={callbackUrl} clientSecret={clientSecret} refId={refId} />
<MockCheckoutForm amount={displayAmount} currency={displayCurrency} callbackUrl={paymentData?.callback_url || '/'} clientSecret={clientSecret} refId={paymentData?.ref_id || 'REF'} />
) : paymentData?.provider === 'stripe' ? (
<Elements stripe={getStripe()} options={{ clientSecret, appearance: { theme: 'stripe' } }}>
<CheckoutForm
amount={amount}
currency={currency}
callbackUrl={callbackUrl}
amount={displayAmount}
currency={displayCurrency}
callbackUrl={paymentData?.callback_url || '/'}
piId={clientSecret.split('_secret')[0]}
/>
</Elements>
@@ -209,10 +238,10 @@ function CheckoutContent() {
)}
<div className="mt-8 flex justify-center lg:justify-start">
<Link href={callbackUrl} className="flex items-center gap-2 text-sm font-semibold text-gray-500 hover:text-gray-900 transition translate-x-0 hover:-translate-x-1 duration-200">
<ArrowLeft size={16} />
Mağazaya Dön
</Link>
<Link href={paymentData?.callback_url || '/'} className="flex items-center gap-2 text-sm font-semibold text-gray-500 hover:text-gray-900 transition translate-x-0 hover:-translate-x-1 duration-200">
<ArrowLeft size={16} />
Mağazaya Dön
</Link>
</div>
</div>
)}