Skip to content

Implement Stellar payout for revenue distribution #569

Description

@Oluwaseyi89

Summary

Implement Stellar payout for revenue distribution — Revenue distribution records are created with beneficiary splits but no Stellar payment operation is ever submitted to execute the payout.

Social Media Link

Let's collaborate on Discord. And ensure to star our repo.

Problem Statement

Confirmed in project-portal/project-portal-backend/internal/financing/payments/distribution.go and project-portal/project-portal-backend/internal/financing/payments/stellar_payments.go:

  1. Distributor.Compute is pure arithmetic only: distribution.go calculates PlatformFeeAmount, NetAmount, and per-Beneficiary Amount via round4, but nothing in the file (or package) ever submits a Stellar payment operation to move funds.

  2. No txnbuild.Payment construction: There is no txnbuild.Payment or txnbuild.PathPaymentStrictSend operation built anywhere in internal/financing/payments, unlike the InvokeHostFunction pattern already established in internal/financing/tokenization/stellar_client.go for minting.

  3. stellar_payments.go only has classification helpers: IsStellarProvider and NormalizeAssetCode decide whether a payout should go over Stellar and which asset code to use, but neither function (nor any other in the file) actually executes a transfer.

  4. Beneficiary.UserID has no linked Stellar address resolution: Beneficiary carries a UserID uuid.UUID, but there is no lookup from UserID to a verified Stellar public key/wallet address before a payout could be attempted.

  5. No idempotency for payout execution: If a payout submission function existed and were retried, there is no deduplicationKey or workflow-ID equivalent (like the one used in retirement operations) to prevent double-paying a beneficiary.

  6. No trustline precondition check for payout recipients: Since payouts would move a Stellar asset (e.g. USDC via NormalizeAssetCode), beneficiary accounts need an established trustline first — no such check exists (related to, but distinct from, the buyer-side trustline gap tracked separately for purchases).

  7. DistributionOutput has no execution/status fields: DistributionOutput only carries computed amounts (PlatformFeeAmount, NetAmount, Beneficiaries) — there is no TransactionHash, Status, or per-beneficiary payout state to track execution.

  8. No batching or fee-bump strategy for multi-beneficiary payouts: A distribution with many beneficiaries would need multiple payment operations (or a single multi-operation transaction); no such batching logic exists.

  9. TaxWithheld is carried but never acted on: Beneficiary.TaxWithheld and BeneficiaryAmount.TaxWithheld are computed/passed through but nothing reduces the actual on-chain payout amount by the withheld portion, nor records it for reporting.

  10. No error handling for partial payout failure: If some beneficiaries in a distribution succeed and others fail (e.g. missing trustline), there is no compensation or partial-failure tracking mechanism.

  11. No repository persistence of payout execution results: There is no method to save a payout's transaction hash, per-beneficiary confirmation, or failure reason back to the database once execution is attempted.

  12. No test coverage for payout execution: distribution.go (or any sibling file) has no _test.go covering an actual Stellar payment submission path — only the pure Compute math would be testable today.

Required Changes

  1. Add a PayoutExecutor (or similar) that takes a DistributionOutput and submits real txnbuild.Payment operations for each beneficiary via the Stellar RPC/Horizon client, following the pattern in stellar_client.go.

  2. Add beneficiary UserID → Stellar public key resolution before payout execution.

  3. Add a trustline-readiness check for each beneficiary's account before attempting payment, returning a clear per-beneficiary error if missing.

  4. Add idempotency keys for payout execution to prevent double-payment on retry.

  5. Extend DistributionOutput/BeneficiaryAmount with execution status fields (TransactionHash, Status, FailureReason).

  6. Implement batching (multi-operation transaction or sequential submission with fee-bump) for distributions with multiple beneficiaries.

  7. Reduce the on-chain payout amount by TaxWithheld and record the withheld amount for tax reporting.

  8. Add partial-failure handling so one beneficiary's failure doesn't silently drop or duplicate others' payouts.

  9. Add a repository method to persist payout execution results (transaction hash, per-beneficiary status).

  10. Add retry with backoff for transient RPC failures during payout submission, mirroring waitForTransaction in stellar_client.go.

  11. Add structured logging for each payout attempt, including beneficiary, amount, and outcome.

  12. Add unit tests covering successful multi-beneficiary payout, missing trustline, partial failure, and idempotent retry.

Acceptance Criteria

  1. A computed DistributionOutput can be executed as real Stellar payment operations.
  2. Each beneficiary's UserID resolves to a verified Stellar address before payout.
  3. Beneficiaries without a trustline produce a clear, isolated failure rather than blocking the whole distribution.
  4. Retrying a payout submission does not double-pay any beneficiary.
  5. Execution status (transaction hash, success/failure) is persisted per beneficiary.
  6. TaxWithheld amounts are excluded from the on-chain transfer and recorded separately.
  7. Multi-beneficiary distributions are batched or sequenced without exceeding transaction limits.
  8. Partial failures are surfaced per beneficiary, not as one opaque distribution-level error.
  9. Unit tests cover success, missing trustline, partial failure, and idempotent retry.
  10. Payout submission failures are retried with backoff for transient RPC errors.

Directory to Work on:

project-portal/project-portal-backend/

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendThis issue is about building backend API services.ginThis issue is to be implemented with the `golang` `gin` framework for backend APIs.golangThis issue is to be implemented with `golang` programming language.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions