Skip to content

Task 2-2: Structured Error Payload cho Webhooks

Phase: 2 Priority: Low Module: marketplace Depends on: Không có Reference: docs/bountyhunter-blockchain-p2/details/feature-marketplace/SPEC.md

Background

Hiện tại webhook failure payload gửi về Backend có trường error là kết quả của error.toString() — raw JavaScript error message, không có cấu trúc. Backend phải parse string để hiểu lỗi, dễ dẫn đến sai sót và khó xử lý tự động. Cần thêm errorCode theo định nghĩa trước để Backend có thể phân loại và xử lý lỗi một cách programmatic.

Tasks

Note: Error code constants nên được định nghĩa trong marketplace.constants.ts (hoặc file constants chung). Khi thêm errorCode vào payload, cần đảm bảo backward compatible — error message vẫn giữ nguyên, chỉ thêm field errorCode. Phân tích blockchain error phải được làm trước khi map vào error code.

  • [ ] Định nghĩa error code constants trong marketplace.constants.ts:
    export const MARKETPLACE_ERROR_CODES = {
      // On-chain errors
      TX_REVERTED: 'MARKETPLACE_TX_REVERTED',
      INSUFFICIENT_GAS: 'MARKETPLACE_INSUFFICIENT_GAS',
      NFT_NOT_OWNED: 'MARKETPLACE_NFT_NOT_OWNED',
      NFT_ALREADY_LISTED: 'MARKETPLACE_NFT_ALREADY_LISTED',
      INVALID_PRICE: 'MARKETPLACE_INVALID_PRICE',
      // Network errors
      RPC_TIMEOUT: 'MARKETPLACE_RPC_TIMEOUT',
      NETWORK_UNAVAILABLE: 'MARKETPLACE_NETWORK_UNAVAILABLE',
      // Generic
      UNKNOWN_ERROR: 'MARKETPLACE_UNKNOWN_ERROR',
    } as const;
    
    export type MarketplaceErrorCode = typeof MARKETPLACE_ERROR_CODES[keyof typeof MARKETPLACE_ERROR_CODES];
    
  • [ ] Tạo helper parseBlockchainError(error: unknown): MarketplaceErrorCode:
    function parseBlockchainError(error: unknown): MarketplaceErrorCode {
      const message = error instanceof Error ? error.message : String(error);
      if (message.includes('reverted')) return MARKETPLACE_ERROR_CODES.TX_REVERTED;
      if (message.includes('insufficient funds')) return MARKETPLACE_ERROR_CODES.INSUFFICIENT_GAS;
      if (message.includes('not the owner')) return MARKETPLACE_ERROR_CODES.NFT_NOT_OWNED;
      if (message.includes('timeout')) return MARKETPLACE_ERROR_CODES.RPC_TIMEOUT;
      return MARKETPLACE_ERROR_CODES.UNKNOWN_ERROR;
    }
    
  • [ ] Cập nhật failure webhook payload trong notifyWebhookOrRetry (task-1-2) và trong từng handler:
    // Trước:
    await postWithRetry(webhookUrl, { status: 'FAILED', error: error.toString() });
    
    // Sau:
    await postWithRetry(webhookUrl, {
      status: 'FAILED',
      error: error instanceof Error ? error.message : String(error),
      errorCode: parseBlockchainError(error),
    });
    
  • [ ] Áp dụng cho tất cả failure webhook calls trong processor và service

Verification / Acceptance Criteria

  • [ ] Failure webhook payload có trường errorCode với giá trị là một trong MARKETPLACE_ERROR_CODES
  • [ ] error field vẫn là string (backward compatible với Backend hiện tại)
  • [ ] Simulate revert error từ EVM → errorCode = 'MARKETPLACE_TX_REVERTED'
  • [ ] Simulate timeout error → errorCode = 'MARKETPLACE_RPC_TIMEOUT'
  • [ ] Lỗi không xác định → errorCode = 'MARKETPLACE_UNKNOWN_ERROR'
  • [ ] TypeScript: errorCode có kiểu MarketplaceErrorCode (không phải string)
  • [ ] Compile không có lỗi

Files to Modify

  • src/marketplace/marketplace.constants.ts
  • src/marketplace/marketplace.processor.ts