Skip to content

Multi-Recipient Payment Endpoint with Per-Transfer Status Tracking #21

Description

@grantfox-oss

Overview

Fintech products and payroll use cases frequently need to send value to many recipients in a single operation — disbursing salaries, paying out merchants, or distributing rewards. While POST /v1/transfers/batch exists in the API reference, the underlying batch processing logic — fan-out queueing, per-transfer status tracking, partial failure handling, and a batch-level summary endpoint — is not yet implemented. This issue covers the full batch transfer feature from queue worker to API response.

What needs to be built

internal/transfer/ — Batch service layer

  • BatchTransfer domain type in internal/domain/ — fields: id, org_id, status (pending | processing | completed | partially_failed | failed), total_count, succeeded_count, failed_count, created_at, settled_at

  • POST /v1/transfers/batch — accepts up to 100 transfer objects in one call:

    • Validates each transfer item (source wallet, destination, asset, amount) before any are enqueued — reject the entire batch if any item fails validation
    • Creates a batch_transfers record and individual transactions records in a single DB transaction
    • Enqueues one Asynq task per transfer with the batch ID attached
    • Returns 202 Accepted with batch_id and per-item transfer_id list immediately
  • GET /v1/transfers/batch/:id — returns batch-level summary: status, total/succeeded/failed counts, and the full per-transfer status list

  • Background worker: processes each transfer task independently; on completion updates the parent batch aggregate counts atomically; marks batch as completed or partially_failed once all items settle
    Webhooks

  • Emit batch.completed webhook when all transfers in a batch have settled

  • Emit batch.partially_failed when at least one transfer failed and at least one succeeded

  • Each individual transfer still emits transfer.settled / transfer.failed as normal
    db/migrations/

  • New migration: batch_transfers table with the fields above

  • Add batch_id nullable foreign key column to existing transactions table

Acceptance criteria

  • Batch of 100 transfers is accepted, enqueued, and all 100 are individually trackable via GET /v1/transfers/:id
  • If any item in the request body fails validation, the entire batch is rejected with a per-item error list — no partial enqueuing
  • Batch status correctly reflects partial failure when a subset of transfers fail on-chain
  • GET /v1/transfers/batch/:id is consistent with the sum of individual transfer statuses
  • batch.completed webhook fires exactly once when the last transfer in the batch settles
  • All batch DB writes use transactions — no inconsistent state if the API process crashes mid-batch

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial CampaignCampaign: Official CampaignenhancementNew feature or requestgood first issueGood for newcomershelp wantedExtra attention is needed

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions