Files
cbd420/app/api/referral-points/redeem/route.ts
2026-01-03 06:06:54 +00:00

218 lines
7.1 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { cookies } from 'next/headers'
import pool from '@/lib/db'
import { ALLOWED_PAYMENT_CURRENCIES, isAllowedCurrency } from '@/lib/payment-currencies'
// POST /api/referral-points/redeem - Redeem referral points to crypto
export async function POST(request: NextRequest) {
const connection = await pool.getConnection()
try {
const cookieStore = await cookies()
const buyerIdCookie = cookieStore.get('buyer_id')?.value
if (!buyerIdCookie) {
return NextResponse.json(
{ error: 'Authentication required' },
{ status: 401 }
)
}
const buyer_id = parseInt(buyerIdCookie, 10)
const body = await request.json()
const { points, crypto_currency, wallet_address } = body
// Validate required fields
if (!points || !crypto_currency || !wallet_address) {
return NextResponse.json(
{ error: 'Missing required fields: points, crypto_currency, wallet_address' },
{ status: 400 }
)
}
const pointsToRedeem = parseFloat(points)
const normalizedCryptoCurrency = crypto_currency.toLowerCase().trim()
const normalizedWalletAddress = wallet_address.trim()
// Validate points amount
if (isNaN(pointsToRedeem) || pointsToRedeem <= 0) {
return NextResponse.json(
{ error: 'Points must be a positive number' },
{ status: 400 }
)
}
// Validate crypto currency - only USDT (SOL) is allowed for redemption
if (normalizedCryptoCurrency !== 'usdtsol') {
return NextResponse.json(
{ error: 'Only USDT (SOL) is supported for point redemption' },
{ status: 400 }
)
}
// Basic wallet address validation (non-empty, reasonable length)
if (normalizedWalletAddress.length < 10 || normalizedWalletAddress.length > 255) {
return NextResponse.json(
{ error: 'Invalid wallet address format' },
{ status: 400 }
)
}
await connection.beginTransaction()
try {
// Get buyer's current points balance
const [buyerRows] = await connection.execute(
'SELECT referral_points FROM buyers WHERE id = ? FOR UPDATE',
[buyer_id]
)
const buyers = buyerRows as any[]
if (buyers.length === 0) {
await connection.rollback()
return NextResponse.json(
{ error: 'Buyer not found' },
{ status: 404 }
)
}
const currentPoints = parseFloat(buyers[0].referral_points) || 0
// Get redemption settings
const [settingsRows] = await connection.execute(
'SELECT setting_key, setting_value FROM referral_settings'
)
const settings = settingsRows as any[]
const pointsToCryptoChf = parseFloat(
settings.find(s => s.setting_key === 'points_to_crypto_chf')?.setting_value || '100'
)
const minRedemptionPoints = parseFloat(
settings.find(s => s.setting_key === 'min_redemption_points')?.setting_value || '1000'
)
// Validate minimum redemption amount
if (pointsToRedeem < minRedemptionPoints) {
await connection.rollback()
return NextResponse.json(
{ error: `Minimum redemption is ${minRedemptionPoints} points` },
{ status: 400 }
)
}
// Validate user has enough points
if (currentPoints < pointsToRedeem) {
await connection.rollback()
return NextResponse.json(
{ error: 'Insufficient points' },
{ status: 400 }
)
}
// Calculate CHF value of points
const chfValue = pointsToRedeem / pointsToCryptoChf
// TODO: Get current crypto exchange rate
// For now, we'll use a placeholder that would need to be replaced with actual exchange rate API
// This should fetch the current rate from an exchange API (e.g., CoinGecko, Binance, etc.)
const cryptoExchangeRate = await getCryptoExchangeRate(normalizedCryptoCurrency, 'chf')
if (!cryptoExchangeRate) {
await connection.rollback()
return NextResponse.json(
{ error: 'Failed to fetch exchange rate. Please try again later.' },
{ status: 500 }
)
}
// Calculate crypto amount
const cryptoAmount = chfValue / cryptoExchangeRate
// Deduct points from buyer's balance
const newBalance = currentPoints - pointsToRedeem
await connection.execute(
'UPDATE buyers SET referral_points = ? WHERE id = ?',
[newBalance, buyer_id]
)
// Create redemption record
const [redemptionResult] = await connection.execute(
`INSERT INTO point_redemptions
(buyer_id, points, crypto_currency, wallet_address, crypto_amount, status)
VALUES (?, ?, ?, ?, ?, 'pending')`,
[buyer_id, pointsToRedeem, normalizedCryptoCurrency, normalizedWalletAddress, cryptoAmount]
)
const redemptionId = (redemptionResult as any).insertId
// Record transaction
await connection.execute(
`INSERT INTO referral_point_transactions
(buyer_id, points, type, description)
VALUES (?, ?, 'redeemed', ?)`,
[
buyer_id,
pointsToRedeem,
`Points redeemed to ${normalizedCryptoCurrency.toUpperCase()} (Redemption #${redemptionId})`
]
)
await connection.commit()
return NextResponse.json({
success: true,
redemption_id: redemptionId,
points_redeemed: pointsToRedeem,
crypto_currency: normalizedCryptoCurrency,
crypto_amount: cryptoAmount,
chf_value: chfValue,
new_balance: newBalance,
message: 'Redemption request created successfully. Your crypto will be sent within 24-48 hours.'
})
} catch (error) {
await connection.rollback()
throw error
}
} catch (error: any) {
console.error('Error redeeming points:', error)
return NextResponse.json(
{ error: error.message || 'Failed to redeem points' },
{ status: 500 }
)
} finally {
connection.release()
}
}
// Helper function to get crypto exchange rate
// TODO: Replace with actual exchange rate API integration
async function getCryptoExchangeRate(crypto: string, fiat: string): Promise<number | null> {
try {
// Placeholder: In production, this should call a real exchange rate API
// Examples: CoinGecko, Binance, Coinbase, etc.
// For now, return a mock rate (this should be replaced)
// In production, you would do something like:
// const response = await fetch(`https://api.coingecko.com/api/v3/simple/price?ids=${crypto}&vs_currencies=${fiat}`)
// const data = await response.json()
// return data[crypto][fiat]
// Mock rates (CHF per 1 unit of crypto) - REPLACE WITH REAL API
const mockRates: Record<string, number> = {
'btc': 85000,
'eth': 2500,
'sol': 100,
'xrp': 0.6,
'bnbbsc': 300,
'usdterc20': 0.9, // Approximate CHF per USDT
'usdtsol': 0.9, // Approximate CHF per USDT on Solana
}
return mockRates[crypto.toLowerCase()] || null
} catch (error) {
console.error('Error fetching exchange rate:', error)
return null
}
}