Skip to content

Feat/admin dispute resolution - #333

Merged
ericmt-98 merged 10 commits into
Micopay:mainfrom
Truphile:feat/admin-dispute-resolution
Aug 1, 2026
Merged

Feat/admin dispute resolution#333
ericmt-98 merged 10 commits into
Micopay:mainfrom
Truphile:feat/admin-dispute-resolution

Conversation

@Truphile

Copy link
Copy Markdown
Contributor

Summary of Work

Created branch micopay-protocol and implemented backend support for dispute
management and administrative dispute resolution.

1. Dispute Creation & Abuse Rate Limiting

• Added rate limiting config: Configured disputeRateLimitWindowMs (1 hr)
and disputeRateLimitMax (5 requests) in config.ts.
• Added dispute service & routes:
• Implemented recordTradeDispute in abuse.service.ts.
• Implemented POST /trades/:id/dispute in trade-safety.ts to let trade
participants report open disputes with evidence URLs and reasons.
• Commit: 1e55863: feat(disputes): add dispute data models, rate limits,
abuse service, and trade-safety route

2. Admin Open Dispute Listing (GET /admin/disputes)

• Admin authentication: Extended auth.middleware.ts with adminMiddleware to
protect admin routes and enforce is_admin permission checks and is_banned
block checks.
• Listing with trade context: Implemented listAdminDisputes in
admin.service.ts and route GET /admin/disputes in admin.ts.
• Returns open disputes populated with:
• Trade context: Amounts (amount_mxn, amount_stroops,
platform_fee_mxn), trade status, hashes, timestamps.
• Parties: Buyer, seller, and reporter accounts (id, username,
stellar_address, is_banned).
• Evidence & messages: Evidence URLs and full trade audit log trail.

• Commit: fc630f9: feat(admin): implement GET /admin/disputes with full
trade context, parties, and evidence

3. Dispute Resolution & Audit Logging (POST

/admin/disputes/:id/resolve)

• Resolution actions: Implemented resolveAdminDispute in admin.service.ts
and endpoint POST /admin/disputes/:id/resolve in admin.ts.
• refund_buyer: Closes dispute (resolved), updates trade status to
'refunded'.
• release_seller: Closes dispute (resolved), updates trade status to
'completed'.
• ban_party: Bans target party (is_banned = true), closes dispute, and
updates trade status.
• Audit trail & logging: Integrates audit.service.ts (logAuditEvent) for
system action auditing and audit-log.model.ts (insertTradeAuditEvent) for
trade lifecycle transitions.
• Verification tests: Added disputes.test.ts covering dispute creation,
rate-limiting, non-participant rejection, admin listing, refund, release,
party banning, and audit logging.
• Commit: 16a2750: feat(admin): implement POST /admin/disputes/:id/resolve
with audit logging, user banning, and dispute tests

closes #329

@drips-wave

drips-wave Bot commented Jul 23, 2026

Copy link
Copy Markdown

@Truphile Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@ericmt-98

Copy link
Copy Markdown
Collaborator

Revisión — bloqueante antes de mergear

Revisé el diff completo contra el alcance de #329. Dos hallazgos críticos:

1. La tabla disputes no existe en Postgres real

El código nuevo (admin.service.ts, abuse.service.ts) migra de trade_disputes (tabla real, creada en 20260528120000_abuse_controls.up.sql) a una tabla nueva disputes, pero este PR no incluye ninguna migración SQL para crearla. Solo se agregó disputes: [] al stub en memoria de schema.ts que usan los tests — por eso disputes.test.ts pasa, pero en producción (Postgres real) esto va a fallar con relation "disputes" does not exist en cuanto alguien abra una disputa.

Fix: agregar la migración up/down para disputes (o, más simple, seguir usando trade_disputes en vez de crear una tabla paralela).

2. resolveAdminDispute no mueve fondos on-chain y destruye la posibilidad de hacerlo después

El flujo real de completar/reembolsar un trade (completeTrade / refundTrade en trade.service.ts) desencripta el secreto y somete una transacción real a Stellar, guardando release_tx_hash. La resolución de disputa en este PR en cambio hace directo:

UPDATE trades SET status = 'completed'/'refunded', secret_enc = NULL, secret_nonce = NULL WHERE id = $2

Esto nunca toca la cadena — el escrow sigue bloqueado on-chain aunque la DB diga "completado"/"refunded". Y al borrar secret_enc/secret_nonce, ya no hay forma de reconstruir la transacción de liberación después. Resultado: el admin "resuelve" la disputa, pero los fondos quedan atorados on-chain de forma permanente — el mismo riesgo de fondos que #329 buscaba resolver, pero ahora sin salida.

Fix: la resolución de disputa debe invocar el flujo real de liberación/reembolso on-chain (reusar completeTrade/refundTrade o su lógica de submit de transacción), no solo actualizar el status en DB.

Menores (no bloqueantes)

  • Se quitó silenciosamente el chequeo de estado previo para abrir disputas (["locked","revealing","completed"]) y la llamada a assertCanOpenDispute desde la ruta, sin mencionarlo en la descripción del PR — vale la pena confirmar que es intencional.
  • El diff trae commiteados archivos .idea/ que deberían estar en .gitignore.

No recomiendo mergear hasta resolver los puntos 1 y 2.

Truphile and others added 2 commits July 23, 2026 17:27
… refund/release during resolution, restore pre-state checks, and untrack .idea files
@ericmt-98
ericmt-98 merged commit 2e0af50 into Micopay:main Aug 1, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Admin endpoint to resolve open trade disputes

2 participants