This commit is contained in:
root
2025-12-22 06:43:19 +01:00
parent a940d51475
commit 6f4ca75faf
25 changed files with 1350 additions and 221 deletions

View File

@@ -1,13 +1,37 @@
import { NextRequest, NextResponse } from 'next/server'
import pool from '@/lib/db'
// GET /api/buyers - Get all buyers
// GET /api/buyers - Get all buyers with referral counts
export async function GET(request: NextRequest) {
try {
const [rows] = await pool.execute(
'SELECT id, username, email, created_at FROM buyers ORDER BY created_at DESC'
`SELECT
b.id,
b.username,
b.email,
b.created_at,
COALESCE(ref_counts.referral_count, 0) as referral_count
FROM buyers b
LEFT JOIN (
SELECT referrer, COUNT(*) as referral_count
FROM referrals
GROUP BY referrer
) ref_counts ON b.id = ref_counts.referrer
ORDER BY b.created_at DESC`
)
return NextResponse.json(rows)
// Add access status for each buyer
const buyersWithAccess = (rows as any[]).map((buyer: any) => {
const referralCount = parseInt(buyer.referral_count) || 0
return {
...buyer,
referral_count: referralCount,
hasWholesaleAccess: referralCount >= 3,
hasInnerCircleAccess: referralCount >= 10
}
})
return NextResponse.json(buyersWithAccess)
} catch (error) {
console.error('Error fetching buyers:', error)
return NextResponse.json(

View File

@@ -3,6 +3,8 @@ import { cookies } from 'next/headers'
import pool from '@/lib/db'
import { getNowPaymentsConfig } from '@/lib/nowpayments'
import { ALLOWED_PAYMENT_CURRENCIES, isAllowedCurrency } from '@/lib/payment-currencies'
import { getCountryFromIp, calculateShippingFee } from '@/lib/geolocation'
import { getCurrencyForCountry, convertPriceForCountry } from '@/lib/currency'
// POST /api/payments/create-invoice - Create a NOWPayments payment
// Note: Endpoint name kept as "create-invoice" for backward compatibility
@@ -129,23 +131,38 @@ export async function POST(request: NextRequest) {
)
}
// Check if user has unlocked wholesale prices
const [referralRows] = await pool.execute(
'SELECT COUNT(*) as count FROM referrals WHERE referrer = ?',
[buyer_id]
)
const referralCount = (referralRows as any[])[0]?.count || 0
const isWholesaleUnlocked = referralCount >= 3
// Check if user has unlocked wholesale prices (use transaction connection to avoid connection leak)
const [referralRows] = await connection.execute(
'SELECT COUNT(*) as count FROM referrals WHERE referrer = ?',
[buyer_id]
)
const referralCount = (referralRows as any[])[0]?.count || 0
const isWholesaleUnlocked = referralCount >= 3
// Calculate price
// ppu is stored as integer where 1000 = $1.00, so divide by 1000 to get actual price
// Get country from IP to determine currency
const countryCode = await getCountryFromIp(request)
const currency = getCurrencyForCountry(countryCode)
// Calculate price in EUR (database stores prices in EUR)
// ppu is stored as integer where 1000 = 1.00 EUR, so divide by 1000 to get actual price
// Assuming ppu is per gram
const pricePerGram = drop.ppu / 1000
const priceToUse = isWholesaleUnlocked ? pricePerGram * 0.76 : pricePerGram
const priceAmount = size * priceToUse
const pricePerGramEur = drop.ppu / 1000
const priceToUseEur = isWholesaleUnlocked ? pricePerGramEur * 0.76 : pricePerGramEur
const priceAmountEur = size * priceToUseEur
// Convert price to user's currency (CHF for Swiss, EUR for others)
const priceAmount = convertPriceForCountry(priceAmountEur, countryCode)
// Calculate shipping fee (already in correct currency: CHF for CH, EUR for others)
const shippingFee = calculateShippingFee(countryCode)
// Add shipping fee to total price
const totalPriceAmount = priceAmount + shippingFee
// Round to 2 decimal places
const roundedPriceAmount = Math.round(priceAmount * 100) / 100
const roundedPriceAmount = Math.round(totalPriceAmount * 100) / 100
const roundedShippingFee = Math.round(shippingFee * 100) / 100
const roundedSubtotal = Math.round(priceAmount * 100) / 100
// Generate order ID
const orderId = `SALE-${Date.now()}-${drop_id}-${buyer_id}`
@@ -158,6 +175,10 @@ export async function POST(request: NextRequest) {
// Get NOWPayments config (testnet or production)
const nowPaymentsConfig = getNowPaymentsConfig()
// Use currency based on user location (CHF for Swiss, EUR for others)
// Override the default currency from config with user's currency
const priceCurrency = currency.toLowerCase()
// Calculate expiration time (10 minutes from now)
const expiresAt = new Date()
expiresAt.setMinutes(expiresAt.getMinutes() + 10)
@@ -180,7 +201,7 @@ export async function POST(request: NextRequest) {
},
body: JSON.stringify({
price_amount: roundedPriceAmount,
price_currency: nowPaymentsConfig.currency,
price_currency: priceCurrency, // CHF for Swiss users, EUR for others
pay_currency: payCurrency, // Required: crypto currency (btc, eth, etc)
order_id: orderId,
order_description: `${drop.item} - ${size}g`,
@@ -206,7 +227,7 @@ export async function POST(request: NextRequest) {
// payment.payment_id is the NOWPayments payment ID
await connection.execute(
'INSERT INTO pending_orders (payment_id, order_id, drop_id, buyer_id, buyer_data_id, size, price_amount, price_currency, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
[payment.payment_id, orderId, drop_id, buyer_id, buyer_data_id, size, roundedPriceAmount, nowPaymentsConfig.currency, expiresAt]
[payment.payment_id, orderId, drop_id, buyer_id, buyer_data_id, size, roundedPriceAmount, priceCurrency, expiresAt]
)
// Commit transaction - inventory is now reserved
@@ -220,8 +241,10 @@ export async function POST(request: NextRequest) {
pay_address: payment.pay_address, // Address where customer sends payment
pay_amount: payment.pay_amount, // Amount in crypto to pay
pay_currency: payment.pay_currency, // Crypto currency
price_amount: payment.price_amount, // Price in fiat
price_currency: payment.price_currency, // Fiat currency
price_amount: payment.price_amount, // Total price in fiat (includes shipping)
price_currency: payment.price_currency, // Fiat currency (CHF or EUR)
shipping_fee: roundedShippingFee, // Shipping fee in user's currency
subtotal: roundedSubtotal, // Product price without shipping in user's currency
order_id: orderId,
payin_extra_id: payment.payin_extra_id, // Memo/tag for certain currencies (XRP, XLM, etc)
expiration_estimate_date: payment.expiration_estimate_date, // When payment expires

View File

@@ -14,7 +14,17 @@ export async function GET(request: NextRequest) {
referralCount: 0,
isUnlocked: false,
referralsNeeded: 3,
referralsRemaining: 3
referralsRemaining: 3,
wholesaleTier: {
referralsNeeded: 3,
referralsRemaining: 3,
isUnlocked: false
},
innerCircleTier: {
referralsNeeded: 10,
referralsRemaining: 10,
isUnlocked: false
}
},
{ status: 200 }
)
@@ -29,15 +39,29 @@ export async function GET(request: NextRequest) {
)
const referralCount = (referralRows as any[])[0]?.count || 0
const isUnlocked = referralCount >= 3
const referralsNeeded = 3
const referralsRemaining = Math.max(0, referralsNeeded - referralCount)
const isWholesaleUnlocked = referralCount >= 3
const isInnerCircleUnlocked = referralCount >= 10
// Determine which tier to show
const wholesaleTier = {
referralsNeeded: 3,
referralsRemaining: Math.max(0, 3 - referralCount),
isUnlocked: isWholesaleUnlocked
}
const innerCircleTier = {
referralsNeeded: 10,
referralsRemaining: Math.max(0, 10 - referralCount),
isUnlocked: isInnerCircleUnlocked
}
return NextResponse.json({
referralCount,
isUnlocked,
referralsNeeded,
referralsRemaining,
isUnlocked: isWholesaleUnlocked, // Keep for backward compatibility
referralsNeeded: isWholesaleUnlocked ? innerCircleTier.referralsNeeded : wholesaleTier.referralsNeeded,
referralsRemaining: isWholesaleUnlocked ? innerCircleTier.referralsRemaining : wholesaleTier.referralsRemaining,
wholesaleTier,
innerCircleTier,
})
} catch (error) {
console.error('Error fetching referral status:', error)

View File

@@ -0,0 +1,27 @@
import { NextRequest, NextResponse } from 'next/server'
import { getCountryFromIp, calculateShippingFee } from '@/lib/geolocation'
import { getCurrencyForCountry } from '@/lib/currency'
// GET /api/shipping-fee - Get shipping fee based on user's IP location
export async function GET(request: NextRequest) {
try {
const countryCode = await getCountryFromIp(request)
const shippingFee = calculateShippingFee(countryCode)
const currency = getCurrencyForCountry(countryCode)
return NextResponse.json({
shipping_fee: shippingFee,
country_code: countryCode,
currency: currency,
})
} catch (error) {
console.error('Error calculating shipping fee:', error)
// Default to 40 EUR if detection fails
return NextResponse.json({
shipping_fee: 40,
country_code: null,
currency: 'EUR',
})
}
}