Refactor: Fully migrated to direct PostgreSQL, implemented Public API v1, fixed Vercel deployment conflicts, and updated documentation
This commit is contained in:
@@ -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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user