feat: improve payments
This commit is contained in:
parent
371eeffd38
commit
7b8f693710
|
|
@ -22,7 +22,7 @@ export async function migrate() {
|
|||
coin TEXT NOT NULL,
|
||||
amount_crypto TEXT NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
derivation_index INTEGER NOT NULL UNIQUE,
|
||||
derivation_index INTEGER NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
paid_at TIMESTAMPTZ,
|
||||
|
|
@ -52,5 +52,9 @@ export async function migrate() {
|
|||
|
||||
await sql`ALTER TABLE payments ADD COLUMN IF NOT EXISTS receipt_html TEXT`;
|
||||
|
||||
// Derivation index should be unique per coin, not globally
|
||||
await sql`DROP INDEX IF EXISTS payments_derivation_index_key`;
|
||||
await sql`CREATE UNIQUE INDEX IF NOT EXISTS idx_payments_coin_derivation ON payments(coin, derivation_index)`;
|
||||
|
||||
console.log("Pay DB ready");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,15 @@ async function refreshMaps() {
|
|||
|
||||
// ── Core logic: one place for all state transitions ─────────────────
|
||||
|
||||
async function recordTx(paymentId: number, txid: string, amount: number, confirmed: boolean) {
|
||||
async function recordTx(paymentId: number, address: string, txid: string, amount: number, confirmed: boolean) {
|
||||
// Verify the payment exists and the address matches — prevents stale in-memory state
|
||||
// from attributing transactions to the wrong payment
|
||||
const [payment] = await sql`
|
||||
SELECT id FROM payments WHERE id = ${paymentId} AND address = ${address}
|
||||
AND status IN ('pending', 'underpaid', 'confirming')
|
||||
`;
|
||||
if (!payment) return;
|
||||
|
||||
const [ins] = await sql`
|
||||
INSERT INTO payment_txs (payment_id, txid, amount, confirmed)
|
||||
VALUES (${paymentId}, ${txid}, ${amount.toFixed(8)}, ${confirmed})
|
||||
|
|
@ -111,7 +119,7 @@ async function handleTxEvent(event: any) {
|
|||
if (txValue <= 0) continue;
|
||||
|
||||
console.log(`SSE: tx ${txHash} for payment ${payment.id}: +${txValue} ${payment.coin}`);
|
||||
await recordTx(payment.id, txHash, txValue, false);
|
||||
await recordTx(payment.id, payment.address, txHash, txValue, false);
|
||||
txidToPayment.set(txHash, payment.id);
|
||||
await evaluatePayment(payment.id);
|
||||
return;
|
||||
|
|
@ -197,7 +205,7 @@ export async function checkPayments() {
|
|||
// Sync txs from address API
|
||||
for (const tx of info.in ?? []) {
|
||||
if (!tx.txid) continue;
|
||||
await recordTx(payment.id, tx.txid, Number(tx.amount ?? 0), tx.block != null);
|
||||
await recordTx(payment.id, payment.address, tx.txid, Number(tx.amount ?? 0), tx.block != null);
|
||||
}
|
||||
await evaluatePayment(payment.id);
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -89,9 +89,9 @@ export const routes = new Elysia()
|
|||
// Crypto amount with 8 decimal precision
|
||||
const amountCrypto = (amountUsd / rate).toFixed(8);
|
||||
|
||||
// Get next derivation index
|
||||
// Get next derivation index for this coin
|
||||
const [{ next_index }] = await sql`
|
||||
SELECT COALESCE(MAX(derivation_index), -1) + 1 as next_index FROM payments
|
||||
SELECT COALESCE(MAX(derivation_index), -1) + 1 as next_index FROM payments WHERE coin = ${coin}
|
||||
`;
|
||||
|
||||
// Derive address
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ nuke_db() {
|
|||
fi
|
||||
echo "[nuke-db] Dropping all tables on database-eu-central..."
|
||||
$SSH $DB_HOST bash << 'REMOTE'
|
||||
sudo -u postgres psql -d pingql -c "DROP TABLE IF EXISTS payments, pings, api_keys, monitors, accounts CASCADE;"
|
||||
sudo -u postgres psql -d pingql -c "DROP TABLE IF EXISTS payment_txs, ping_bodies, payments, pings, api_keys, monitors, accounts CASCADE;"
|
||||
echo "All tables dropped"
|
||||
REMOTE
|
||||
echo "[nuke-db] Done. Tables will be recreated on next API/web restart."
|
||||
|
|
|
|||
Loading…
Reference in New Issue