130 lines
3.8 KiB
TypeScript
130 lines
3.8 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { db } from '@/lib/db';
|
|
import { validateApiKey } from '@/lib/api-auth';
|
|
import { CryptoEngine } from '@/lib/crypto-engine';
|
|
import { PaymentProviderFactory } from '@/lib/payment-providers';
|
|
|
|
/**
|
|
* Public API for Merchants to create a payment session
|
|
* POST /api/v1/checkout
|
|
* Header: x-api-key: YOUR_API_KEY
|
|
*/
|
|
export async function POST(req: NextRequest) {
|
|
try {
|
|
const apiKey = req.headers.get('x-api-key');
|
|
const merchant = await validateApiKey(apiKey);
|
|
|
|
if (!merchant) {
|
|
return NextResponse.json(
|
|
{ error: 'Unauthorized. Invalid API Key.' },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
const {
|
|
amount,
|
|
currency = 'TRY',
|
|
order_id,
|
|
callback_url,
|
|
customer_name,
|
|
customer_phone,
|
|
success_url,
|
|
cancel_url
|
|
} = await req.json();
|
|
|
|
if (!amount) {
|
|
return NextResponse.json(
|
|
{ error: 'Amount is required.' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// 1. Determine provider
|
|
const provider = merchant.payment_provider || 'stripe';
|
|
const useMock = process.env.NEXT_PUBLIC_USE_MOCK_PAYMENTS === 'true';
|
|
|
|
let clientSecret = '';
|
|
let providerTxId = '';
|
|
let nextAction = 'none';
|
|
let redirectUrl = '';
|
|
|
|
// Generate Temporary Wallets for Crypto fallback
|
|
const evmWallet = await CryptoEngine.createTemporaryWallet('POLYGON');
|
|
const solWallet = await CryptoEngine.createTemporaryWallet('SOLANA');
|
|
|
|
const cryptoWallets = {
|
|
EVM: { address: evmWallet.address, privateKey: evmWallet.privateKey },
|
|
SOLANA: { address: solWallet.address, privateKey: solWallet.privateKey }
|
|
};
|
|
|
|
if (useMock) {
|
|
clientSecret = 'mock_secret_' + Math.random().toString(36).substring(7);
|
|
providerTxId = clientSecret;
|
|
} else {
|
|
// Use Factory to create intent based on provider (Stripe, etc.)
|
|
const intent = await PaymentProviderFactory.createIntent(provider, {
|
|
amount,
|
|
currency,
|
|
merchantId: merchant.id,
|
|
refId: order_id,
|
|
customerName: customer_name,
|
|
customerPhone: customer_phone,
|
|
callbackUrl: callback_url || success_url,
|
|
providerConfig: merchant.provider_config
|
|
});
|
|
|
|
clientSecret = intent.clientSecret;
|
|
providerTxId = intent.providerTxId;
|
|
nextAction = intent.nextAction || 'none';
|
|
redirectUrl = intent.redirectUrl || '';
|
|
}
|
|
|
|
// 2. Insert Transaction into DB
|
|
const txResult = await db.query(`
|
|
INSERT INTO transactions (
|
|
amount, currency, status, stripe_pi_id, source_ref_id,
|
|
customer_name, customer_phone, callback_url, merchant_id,
|
|
provider, metadata
|
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
RETURNING id
|
|
`, [
|
|
amount, currency, 'pending', providerTxId, order_id,
|
|
customer_name, customer_phone, callback_url || success_url, merchant.id,
|
|
provider, JSON.stringify({
|
|
nextAction,
|
|
redirectUrl,
|
|
wallets: cryptoWallets,
|
|
success_url,
|
|
cancel_url
|
|
})
|
|
]);
|
|
|
|
const txId = txResult.rows[0].id;
|
|
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
|
|
|
|
// 3. Return response with checkout URL
|
|
return NextResponse.json({
|
|
success: true,
|
|
data: {
|
|
id: txId,
|
|
amount,
|
|
currency,
|
|
order_id,
|
|
checkout_url: `${baseUrl}/checkout?session_id=${txId}`,
|
|
status: 'pending',
|
|
wallets: {
|
|
EVM: evmWallet.address,
|
|
SOLANA: solWallet.address
|
|
}
|
|
}
|
|
});
|
|
|
|
} catch (error: any) {
|
|
console.error('Public API Error:', error);
|
|
return NextResponse.json(
|
|
{ error: 'Internal Server Error' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|