diff --git a/check_txs.js b/check_txs.js new file mode 100644 index 0000000..a8f2115 --- /dev/null +++ b/check_txs.js @@ -0,0 +1,16 @@ + +const { Client } = require('pg'); +const client = new Client({ connectionString: process.env.DATABASE_URL }); + +async function checkSpecificTxs() { + await client.connect(); + const res = await client.query(` + SELECT id, source_ref_id, metadata + FROM transactions + WHERE source_ref_id IN ('TEST_ORDER_999', 'TEST_ORDER_1000') + `); + console.log(JSON.stringify(res.rows, null, 2)); + await client.end(); +} + +checkSpecificTxs(); diff --git a/check_wallets.js b/check_wallets.js new file mode 100644 index 0000000..84ac270 --- /dev/null +++ b/check_wallets.js @@ -0,0 +1,53 @@ + +const { Client } = require('pg'); +const { Connection, PublicKey } = require('@solana/web3.js'); + +async function debugSolanaPayments() { + const client = new Client({ connectionString: process.env.DATABASE_URL }); + await client.connect(); + + const connection = new Connection('https://api.devnet.solana.com', 'confirmed'); + + const res = await client.query(` + SELECT id, source_ref_id, amount, currency, metadata + FROM transactions + WHERE source_ref_id IN ('TEST_ORDER_999', 'TEST_ORDER_1000') + `); + + console.log(`Found ${res.rows.length} transactions for debugging.\n`); + + for (const tx of res.rows) { + const solAddr = tx.metadata?.wallets?.SOLANA?.address; + console.log(`TX: ${tx.id} | Ref: ${tx.source_ref_id}`); + console.log(`Amount: ${tx.amount} ${tx.currency}`); + + if (!solAddr) { + console.log("❌ No Solana wallet found in metadata."); + continue; + } + + console.log(`Wallet Address: ${solAddr}`); + + try { + const pubKey = new PublicKey(solAddr); + const balance = await connection.getBalance(pubKey); + console.log(`Balance: ${balance / 1e9} SOL`); + + // Also check common tokens + const USDT_MINT = 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'; // Placeholder for USDT on mainnet/devnet logic + // Since this is a check, let's just see transactions on that address + const signatures = await connection.getSignaturesForAddress(pubKey); + console.log(`Recent Activity: ${signatures.length} signatures found.`); + if (signatures.length > 0) { + console.log(`Latest Sig: ${signatures[0].signature}`); + } + } catch (err) { + console.error(`Error checking address: ${err.message}`); + } + console.log("-" . repeat(40)); + } + + await client.end(); +} + +debugSolanaPayments(); diff --git a/debug_verification.js b/debug_verification.js new file mode 100644 index 0000000..491e9a8 --- /dev/null +++ b/debug_verification.js @@ -0,0 +1,52 @@ + +const { Client } = require('pg'); +const { Connection, PublicKey } = require('@solana/web3.js'); + +async function debugVerification() { + const client = new Client({ connectionString: process.env.DATABASE_URL }); + await client.connect(); + const connection = new Connection('https://api.devnet.solana.com', 'confirmed'); + + const res = await client.query(` + SELECT id, source_ref_id, amount, currency, metadata + FROM transactions + WHERE source_ref_id IN ('TEST_ORDER_999', 'TEST_ORDER_1000') + `); + + for (const tx of res.rows) { + console.log(`\n--- Checking ${tx.source_ref_id} ---`); + const solAddr = tx.metadata?.wallets?.SOLANA?.address; + + // 1. Calculate Backend's expected amount + // Simple mock of logic in sync-worker + const amountTry = parseFloat(tx.amount); + // Let's assume SOL price is around 4000 TRY for this debug + const solPriceRes = await fetch('https://api.binance.com/api/v3/ticker/price?symbol=SOLUSDT'); + const solPriceData = await solPriceRes.json(); + const usdtTryRes = await fetch('https://api.binance.com/api/v3/ticker/price?symbol=USDTTRY'); + const usdtTryData = await usdtTryRes.json(); + + const priceSolTry = parseFloat(solPriceData.price) * parseFloat(usdtTryData.price); + const rawExpected = amountTry / priceSolTry; + const expectedWithTolerance = (rawExpected * 0.98).toFixed(6); + + console.log(`Amount: ${amountTry} TRY`); + console.log(`Current SOL Price: ~${priceSolTry.toFixed(2)} TRY`); + console.log(`Backend Minimum Expected: ${expectedWithTolerance} SOL`); + + // 2. Check Actual Balance + const pubKey = new PublicKey(solAddr); + const balance = await connection.getBalance(pubKey); + const actualSol = balance / 1e9; + console.log(`Actual Wallet Balance: ${actualSol} SOL`); + + if (actualSol >= parseFloat(expectedWithTolerance)) { + console.log("✅ VERIFICATION SHOULD PASS!"); + } else { + console.log("❌ VERIFICATION FAILED: Insufficient balance for tolerance."); + } + } + await client.end(); +} + +debugVerification(); diff --git a/lib/sync-worker.ts b/lib/sync-worker.ts index 2b4c5f2..e3ae630 100644 --- a/lib/sync-worker.ts +++ b/lib/sync-worker.ts @@ -41,109 +41,132 @@ export async function syncPendingPayments() { for (const tx of pendingTxs) { try { const metadata = tx.metadata || {}; - const intentNetwork = metadata.intent_network || 'POLYGON'; - const intentToken = metadata.intent_token || 'USDT'; - - console.log(`[SyncWorker] Syncing TX ${tx.id} | Intent: ${intentNetwork}/${intentToken}`); - - // Re-use logic from crypto-sweep but optimized for background const wallets = metadata.wallets || {}; - const tempWalletConfig = wallets[intentNetwork] || wallets['EVM']; - if (!tempWalletConfig) continue; - - const depositAddress = typeof tempWalletConfig === 'string' ? tempWalletConfig : tempWalletConfig.address; - const depositPrivateKey = tempWalletConfig.privateKey; - - if (!depositPrivateKey) continue; + const scanningMatrix = [ + { network: 'SOLANA', tokens: ['SOL', 'USDT', 'USDC'] }, + { network: 'POLYGON', tokens: ['MATIC', 'USDT', 'USDC'] }, + { network: 'TRON', tokens: ['TRX', 'USDT', 'USDC'] } + ]; - const cryptoEngine = new CryptoEngine(intentNetwork); - - // Get expected amount logic - let expectedCryptoAmount = tx.amount.toString(); - try { - const coinIdMap: Record = { - 'SOL': 'solana', 'USDC': 'usd-coin', 'USDT': 'tether', 'TRX': 'tron', 'BTC': 'bitcoin' - }; - const coinId = coinIdMap[intentToken] || 'solana'; - const priceUrl = `https://api.coingecko.com/api/v3/simple/price?ids=${coinId}&vs_currencies=usd,try`; - const priceRes = await fetch(priceUrl); - const priceData = await priceRes.json(); - const currencyKey = (tx.currency || 'TRY').toLowerCase(); - const priceInCurrency = priceData[coinId][currencyKey] || priceData[coinId]['usd']; - - if (priceInCurrency) { - const rawExpected = parseFloat(tx.amount) / priceInCurrency; - expectedCryptoAmount = (rawExpected * 0.98).toFixed(6); - } - } catch (e) {} - - // Verify - const verification = await cryptoEngine.verifyPayment(depositAddress, expectedCryptoAmount, intentToken); - - if (verification.success) { - console.log(`[SyncWorker] Payment DETECTED for TX ${tx.id}. Sweeping...`); - - let platformAddress = sysSettings.evm; - if (intentNetwork === 'SOLANA') platformAddress = sysSettings.sol; - else if (intentNetwork === 'TRON') platformAddress = sysSettings.tron; - else if (intentNetwork === 'BITCOIN') platformAddress = sysSettings.btc; - - // Sweep - const sweepResult = await cryptoEngine.sweepFunds(depositPrivateKey, platformAddress, intentToken); - - if (sweepResult.success) { - // Update Balances & Transaction (Same as crypto-sweep logic) - const merchantResult = await db.query('SELECT * FROM merchants WHERE id = $1', [tx.merchant_id]); - const merchant = merchantResult.rows[0]; - const feePercent = merchant?.fee_percent !== undefined && merchant?.fee_percent !== null ? parseFloat(merchant.fee_percent) : sysSettings.fee; - - const grossAmount = parseFloat(tx.amount); - const feeAmount = (grossAmount * feePercent) / 100; - const merchantNetCredit = grossAmount - feeAmount; - - const cryptoAmount = parseFloat(expectedCryptoAmount); - const cryptoFee = (cryptoAmount * feePercent) / 100; - const cryptoNetCredit = cryptoAmount - cryptoFee; - - // Execute DB updates in transaction if possible, or sequential - await db.query(`UPDATE merchants SET available_balance = available_balance + $1 WHERE id = $2`, [merchantNetCredit, tx.merchant_id]); - - await db.query(` - INSERT INTO merchant_balances (merchant_id, network, token, balance, total_gross) - VALUES ($1, $2, $3, $4, $5) - ON CONFLICT (merchant_id, network, token) - DO UPDATE SET - balance = merchant_balances.balance + $4, - total_gross = merchant_balances.total_gross + $5 - `, [tx.merchant_id, intentNetwork, intentToken, cryptoNetCredit, cryptoAmount]); - - await db.query(` - UPDATE transactions - SET status = 'succeeded', paid_network = $2, paid_token = $3, paid_amount_crypto = $4 - WHERE id = $1`, [tx.id, intentNetwork, intentToken, expectedCryptoAmount]); - - // Webhook - if (tx.callback_url) { - fetch(tx.callback_url, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - status: 'success', - txId: tx.stripe_pi_id, - orderRef: tx.source_ref_id, - hashes: { txHash: sweepResult.txHash } - }) - }).catch(() => {}); + if (metadata.intent_network && metadata.intent_token) { + const intentIdx = scanningMatrix.findIndex(m => m.network === metadata.intent_network); + if (intentIdx > -1) { + const intentItem = scanningMatrix.splice(intentIdx, 1)[0]; + const tokenIdx = intentItem.tokens.indexOf(metadata.intent_token); + if (tokenIdx > -1) { + intentItem.tokens.splice(tokenIdx, 1); + intentItem.tokens.unshift(metadata.intent_token); } - - results.push({ id: tx.id, status: 'synced', txHash: sweepResult.txHash }); + scanningMatrix.unshift(intentItem); } - } else { - results.push({ id: tx.id, status: 'no_payment' }); } + + let foundPayment = false; + + for (const scan of scanningMatrix) { + if (foundPayment) break; + + const networkId = scan.network; + const walletConfig = wallets[networkId] || (networkId === 'POLYGON' ? wallets['EVM'] : null); + + if (!walletConfig) continue; + + const depositAddress = typeof walletConfig === 'string' ? walletConfig : walletConfig.address; + const depositPrivateKey = walletConfig.privateKey; + + if (!depositPrivateKey) continue; + + // Create engine with explicit network and ensured config + const cryptoEngine = new CryptoEngine(networkId); + + for (const tokenSymbol of scan.tokens) { + if (foundPayment) break; + + console.log(`[SyncWorker] Checking TX ${tx.id.slice(0,8)} | Network: ${networkId} | Token: ${tokenSymbol} | Address: ${depositAddress}`); + + let expectedCryptoAmount = tx.amount.toString(); + try { + const coinIdMap: Record = { + 'SOL': 'solana', 'MATIC': 'matic-network', 'POLYGON': 'matic-network', + 'USDC': 'usd-coin', 'USDT': 'tether', 'TRX': 'tron', 'BTC': 'bitcoin' + }; + const coinId = coinIdMap[tokenSymbol] || 'solana'; + const priceUrl = `https://api.coingecko.com/api/v3/simple/price?ids=${coinId}&vs_currencies=usd,try`; + const priceRes = await fetch(priceUrl); + const priceData = await priceRes.json(); + const currencyKey = (tx.currency || 'TRY').toLowerCase(); + const priceInCurrency = priceData[coinId][currencyKey] || priceData[coinId]['usd']; + + if (priceInCurrency) { + const rawExpected = parseFloat(tx.amount) / priceInCurrency; + expectedCryptoAmount = (rawExpected * 0.98).toFixed(6); + } + } catch (e) { + console.warn(`[SyncWorker] Price fetch failed for ${tokenSymbol}, using raw amount.`); + } + + const verification = await cryptoEngine.verifyPayment(depositAddress, expectedCryptoAmount, tokenSymbol); + + if (verification.success) { + console.log(`[SyncWorker] Found ${tokenSymbol} matching ~${expectedCryptoAmount} on ${networkId}`); + + let platformAddress = sysSettings.evm; + if (networkId === 'SOLANA') platformAddress = sysSettings.sol; + else if (networkId === 'TRON') platformAddress = sysSettings.tron; + + const sweepResult = await cryptoEngine.sweepFunds(depositPrivateKey, platformAddress, tokenSymbol); + + if (sweepResult.success) { + const merchantResult = await db.query('SELECT * FROM merchants WHERE id = $1', [tx.merchant_id]); + const merchant = merchantResult.rows[0]; + const feePercent = merchant?.fee_percent !== undefined && merchant?.fee_percent !== null ? parseFloat(merchant.fee_percent) : sysSettings.fee; + + const grossAmount = parseFloat(tx.amount); + const feeAmount = (grossAmount * feePercent) / 100; + const merchantNetCredit = grossAmount - feeAmount; + + const cryptoAmount = parseFloat(expectedCryptoAmount); + const cryptoFee = (cryptoAmount * feePercent) / 100; + const cryptoNetCredit = cryptoAmount - cryptoFee; + + await db.query(`UPDATE merchants SET available_balance = available_balance + $1 WHERE id = $2`, [merchantNetCredit, tx.merchant_id]); + + await db.query(` + INSERT INTO merchant_balances (merchant_id, network, token, balance, total_gross) + VALUES ($1, $2, $3, $4, $5) + ON CONFLICT (merchant_id, network, token) + DO UPDATE SET + balance = merchant_balances.balance + $4, + total_gross = merchant_balances.total_gross + $5 + `, [tx.merchant_id, networkId, tokenSymbol, cryptoNetCredit, cryptoAmount]); + + await db.query(` + UPDATE transactions + SET status = 'succeeded', paid_network = $2, paid_token = $3, paid_amount_crypto = $4 + WHERE id = $1`, [tx.id, networkId, tokenSymbol, expectedCryptoAmount]); + + if (tx.callback_url) { + fetch(tx.callback_url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + status: 'success', txId: tx.stripe_pi_id, + orderRef: tx.source_ref_id, hashes: { txHash: sweepResult.txHash } + }) + }).catch(() => {}); + } + + results.push({ id: tx.id, status: 'synced', network: networkId, token: tokenSymbol }); + foundPayment = true; + } + } + } + } + if (!foundPayment) results.push({ id: tx.id, status: 'no_payment' }); + } catch (err: any) { - console.error(`[SyncWorker] Error processing TX ${tx.id}:`, err.message); + console.error(`[SyncWorker] Error processing TX ${tx.id}:`, err); results.push({ id: tx.id, status: 'error', error: err.message }); } }