feat: add new status in db
This commit is contained in:
@@ -5,3 +5,4 @@ dist
|
|||||||
.gitignore
|
.gitignore
|
||||||
*.log
|
*.log
|
||||||
README.md
|
README.md
|
||||||
|
swagger.yaml
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@ node_modules/
|
|||||||
dist/
|
dist/
|
||||||
.env
|
.env
|
||||||
*.log
|
*.log
|
||||||
|
swagger.yaml
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
import pg from "pg";
|
import pg from 'pg';
|
||||||
import type { DatabaseSecret } from "../secrets/VaultClient.js";
|
import type {DatabaseSecret} from '../secrets/VaultClient.js';
|
||||||
|
|
||||||
const { Pool } = pg;
|
const { Pool } = pg;
|
||||||
|
|
||||||
export type PaymentStatus =
|
export type PaymentStatus =
|
||||||
| "pending"
|
| 'pending'
|
||||||
| "usdt_delivered"
|
| 'web3_processing'
|
||||||
| "web3_hash_error"
|
| 'usdt_delivered'
|
||||||
| "web3_balance_problem"
|
| 'web3_hash_error'
|
||||||
|
| 'web3_balance_problem'
|
||||||
| string;
|
| string;
|
||||||
|
|
||||||
export interface PaymentRecord {
|
export interface PaymentRecord {
|
||||||
@@ -15,6 +16,11 @@ export interface PaymentRecord {
|
|||||||
web3_transaction_hash: string | null;
|
web3_transaction_hash: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PaymentClaimResult {
|
||||||
|
claimed: boolean;
|
||||||
|
payment: PaymentRecord | null;
|
||||||
|
}
|
||||||
|
|
||||||
export class PostgresClient {
|
export class PostgresClient {
|
||||||
private readonly pool: pg.Pool;
|
private readonly pool: pg.Pool;
|
||||||
|
|
||||||
@@ -32,12 +38,12 @@ export class PostgresClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ping(): Promise<void> {
|
async ping(): Promise<void> {
|
||||||
await this.pool.query("SELECT 1");
|
await this.pool.query('SELECT 1');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOrderUsdtAmount(orderId: string): Promise<string | null> {
|
async getOrderUsdtAmount(orderId: string): Promise<string | null> {
|
||||||
const result = await this.pool.query<{ usdt_amount: string }>(
|
const result = await this.pool.query<{ usdt_amount: string }>(
|
||||||
"SELECT usdt_amount::text AS usdt_amount FROM orders WHERE id = $1",
|
'SELECT usdt_amount::text AS usdt_amount FROM orders WHERE id = $1',
|
||||||
[orderId]
|
[orderId]
|
||||||
);
|
);
|
||||||
return result.rows[0]?.usdt_amount ?? null;
|
return result.rows[0]?.usdt_amount ?? null;
|
||||||
@@ -45,7 +51,7 @@ export class PostgresClient {
|
|||||||
|
|
||||||
async getUserEthWalletAddress(userId: string): Promise<string | null> {
|
async getUserEthWalletAddress(userId: string): Promise<string | null> {
|
||||||
const result = await this.pool.query<{ address: string }>(
|
const result = await this.pool.query<{ address: string }>(
|
||||||
"SELECT address FROM wallets WHERE user_id = $1 AND chain = 'ETH' LIMIT 1",
|
`SELECT address FROM wallets WHERE user_id = $1 AND chain = 'ETH' LIMIT 1`,
|
||||||
[userId]
|
[userId]
|
||||||
);
|
);
|
||||||
return result.rows[0]?.address ?? null;
|
return result.rows[0]?.address ?? null;
|
||||||
@@ -53,15 +59,44 @@ export class PostgresClient {
|
|||||||
|
|
||||||
async getPaymentByOrderId(orderId: string): Promise<PaymentRecord | null> {
|
async getPaymentByOrderId(orderId: string): Promise<PaymentRecord | null> {
|
||||||
const result = await this.pool.query<PaymentRecord>(
|
const result = await this.pool.query<PaymentRecord>(
|
||||||
"SELECT status, web3_transaction_hash FROM payments WHERE order_id = $1",
|
'SELECT status, web3_transaction_hash FROM payments WHERE order_id = $1',
|
||||||
[orderId]
|
[orderId]
|
||||||
);
|
);
|
||||||
return result.rows[0] ?? null;
|
return result.rows[0] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async claimPaymentForTransfer(orderId: string): Promise<PaymentClaimResult> {
|
||||||
|
const claimed = await this.pool.query<PaymentRecord>(
|
||||||
|
`UPDATE payments
|
||||||
|
SET status = 'web3_processing',
|
||||||
|
updated_at = now()
|
||||||
|
WHERE order_id = $1
|
||||||
|
AND web3_transaction_hash IS NULL
|
||||||
|
AND status NOT IN ('web3_processing', 'usdt_delivered', 'web3_hash_error', 'web3_balance_problem')
|
||||||
|
RETURNING status, web3_transaction_hash`,
|
||||||
|
[orderId]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (claimed.rows[0]) {
|
||||||
|
return {
|
||||||
|
claimed: true,
|
||||||
|
payment: claimed.rows[0]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
claimed: false,
|
||||||
|
payment: await this.getPaymentByOrderId(orderId)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async setPaymentTxHash(orderId: string, txHash: string): Promise<void> {
|
async setPaymentTxHash(orderId: string, txHash: string): Promise<void> {
|
||||||
await this.pool.query(
|
await this.pool.query(
|
||||||
"UPDATE payments SET web3_transaction_hash = $2, updated_at = now() WHERE order_id = $1",
|
`UPDATE payments
|
||||||
|
SET web3_transaction_hash = $2,
|
||||||
|
updated_at = now()
|
||||||
|
WHERE order_id = $1
|
||||||
|
AND status = 'web3_processing'`,
|
||||||
[orderId, txHash]
|
[orderId, txHash]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -73,14 +108,19 @@ export class PostgresClient {
|
|||||||
web3_transaction_hash = $2,
|
web3_transaction_hash = $2,
|
||||||
paid_at = now(),
|
paid_at = now(),
|
||||||
updated_at = now()
|
updated_at = now()
|
||||||
WHERE order_id = $1`,
|
WHERE order_id = $1
|
||||||
|
AND status = 'web3_processing'`,
|
||||||
[orderId, txHash]
|
[orderId, txHash]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async markPaymentHashError(orderId: string): Promise<void> {
|
async markPaymentHashError(orderId: string): Promise<void> {
|
||||||
await this.pool.query(
|
await this.pool.query(
|
||||||
"UPDATE payments SET status = 'web3_hash_error', updated_at = now() WHERE order_id = $1",
|
`UPDATE payments
|
||||||
|
SET status = 'web3_hash_error',
|
||||||
|
updated_at = now()
|
||||||
|
WHERE order_id = $1
|
||||||
|
AND status = 'web3_processing'`,
|
||||||
[orderId]
|
[orderId]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -91,7 +131,8 @@ export class PostgresClient {
|
|||||||
SET status = 'web3_balance_problem',
|
SET status = 'web3_balance_problem',
|
||||||
web3_transaction_hash = $2,
|
web3_transaction_hash = $2,
|
||||||
updated_at = now()
|
updated_at = now()
|
||||||
WHERE order_id = $1`,
|
WHERE order_id = $1
|
||||||
|
AND status = 'web3_processing'`,
|
||||||
[orderId, txHash]
|
[orderId, txHash]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,23 +28,15 @@ export class TransferOrchestrator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handle(message: CryptoTransferRequest, log: Logger): Promise<void> {
|
async handle(message: CryptoTransferRequest, log: Logger): Promise<void> {
|
||||||
const existing = await this.db.getPaymentByOrderId(message.order_id);
|
const claim = await this.db.claimPaymentForTransfer(message.order_id);
|
||||||
|
const existing = claim.payment;
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
log.error({ event: 'payment.missing' }, 'no payment row for order_id, ack');
|
log.error({ event: 'payment.missing' }, 'no payment row for order_id, ack');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existing.status === 'usdt_delivered') {
|
if (!claim.claimed) {
|
||||||
log.info({ event: 'payment.already_delivered' }, 'already delivered, publishing completion');
|
await this.handleAlreadyClaimed(message, existing, log);
|
||||||
await this.publishCompleted(message, log);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existing.status === 'web3_hash_error' || existing.status === 'web3_balance_problem') {
|
|
||||||
log.warn(
|
|
||||||
{ event: 'payment.terminal_state', status: existing.status },
|
|
||||||
'skip - payment already in terminal failure state'
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,6 +190,39 @@ export class TransferOrchestrator {
|
|||||||
'published crypto.transfer.completed'
|
'published crypto.transfer.completed'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async handleAlreadyClaimed(
|
||||||
|
message: CryptoTransferRequest,
|
||||||
|
existing: { status: string; web3_transaction_hash: string | null },
|
||||||
|
log: Logger
|
||||||
|
): Promise<void> {
|
||||||
|
if (existing.status === 'usdt_delivered') {
|
||||||
|
log.info({ event: 'payment.already_delivered' }, 'already delivered, publishing completion');
|
||||||
|
await this.publishCompleted(message, log);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing.status === 'web3_processing') {
|
||||||
|
log.warn(
|
||||||
|
{ event: 'payment.already_processing', tx_hash: existing.web3_transaction_hash },
|
||||||
|
'skip - payment is already claimed by another worker'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing.status === 'web3_hash_error' || existing.status === 'web3_balance_problem') {
|
||||||
|
log.warn(
|
||||||
|
{ event: 'payment.terminal_state', status: existing.status },
|
||||||
|
'skip - payment already in terminal failure state'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.warn(
|
||||||
|
{ event: 'payment.not_claimed', status: existing.status, tx_hash: existing.web3_transaction_hash },
|
||||||
|
'skip - payment was not claimed for transfer'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user