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:
-
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.
-
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.
-
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.
-
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.
-
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.
-
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).
-
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.
-
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.
-
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.
-
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.
-
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.
-
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
-
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.
-
Add beneficiary UserID → Stellar public key resolution before payout execution.
-
Add a trustline-readiness check for each beneficiary's account before attempting payment, returning a clear per-beneficiary error if missing.
-
Add idempotency keys for payout execution to prevent double-payment on retry.
-
Extend DistributionOutput/BeneficiaryAmount with execution status fields (TransactionHash, Status, FailureReason).
-
Implement batching (multi-operation transaction or sequential submission with fee-bump) for distributions with multiple beneficiaries.
-
Reduce the on-chain payout amount by TaxWithheld and record the withheld amount for tax reporting.
-
Add partial-failure handling so one beneficiary's failure doesn't silently drop or duplicate others' payouts.
-
Add a repository method to persist payout execution results (transaction hash, per-beneficiary status).
-
Add retry with backoff for transient RPC failures during payout submission, mirroring waitForTransaction in stellar_client.go.
-
Add structured logging for each payout attempt, including beneficiary, amount, and outcome.
-
Add unit tests covering successful multi-beneficiary payout, missing trustline, partial failure, and idempotent retry.
Acceptance Criteria
- A computed
DistributionOutput can be executed as real Stellar payment operations.
- Each beneficiary's
UserID resolves to a verified Stellar address before payout.
- Beneficiaries without a trustline produce a clear, isolated failure rather than blocking the whole distribution.
- Retrying a payout submission does not double-pay any beneficiary.
- Execution status (transaction hash, success/failure) is persisted per beneficiary.
TaxWithheld amounts are excluded from the on-chain transfer and recorded separately.
- Multi-beneficiary distributions are batched or sequenced without exceeding transaction limits.
- Partial failures are surfaced per beneficiary, not as one opaque distribution-level error.
- Unit tests cover success, missing trustline, partial failure, and idempotent retry.
- Payout submission failures are retried with backoff for transient RPC errors.
Directory to Work on:
project-portal/project-portal-backend/
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.goandproject-portal/project-portal-backend/internal/financing/payments/stellar_payments.go:Distributor.Computeis pure arithmetic only:distribution.gocalculatesPlatformFeeAmount,NetAmount, and per-BeneficiaryAmountviaround4, but nothing in the file (or package) ever submits a Stellar payment operation to move funds.No
txnbuild.Paymentconstruction: There is notxnbuild.Paymentortxnbuild.PathPaymentStrictSendoperation built anywhere ininternal/financing/payments, unlike theInvokeHostFunctionpattern already established ininternal/financing/tokenization/stellar_client.gofor minting.stellar_payments.goonly has classification helpers:IsStellarProviderandNormalizeAssetCodedecide 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.Beneficiary.UserIDhas no linked Stellar address resolution:Beneficiarycarries aUserID uuid.UUID, but there is no lookup fromUserIDto a verified Stellar public key/wallet address before a payout could be attempted.No idempotency for payout execution: If a payout submission function existed and were retried, there is no
deduplicationKeyor workflow-ID equivalent (like the one used in retirement operations) to prevent double-paying a beneficiary.No trustline precondition check for payout recipients: Since payouts would move a Stellar asset (e.g.
USDCviaNormalizeAssetCode), 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).DistributionOutputhas no execution/status fields:DistributionOutputonly carries computed amounts (PlatformFeeAmount,NetAmount,Beneficiaries) — there is noTransactionHash,Status, or per-beneficiary payout state to track execution.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.
TaxWithheldis carried but never acted on:Beneficiary.TaxWithheldandBeneficiaryAmount.TaxWithheldare computed/passed through but nothing reduces the actual on-chain payout amount by the withheld portion, nor records it for reporting.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.
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.
No test coverage for payout execution:
distribution.go(or any sibling file) has no_test.gocovering an actual Stellar payment submission path — only the pureComputemath would be testable today.Required Changes
Add a
PayoutExecutor(or similar) that takes aDistributionOutputand submits realtxnbuild.Paymentoperations for each beneficiary via the Stellar RPC/Horizon client, following the pattern instellar_client.go.Add beneficiary
UserID→ Stellar public key resolution before payout execution.Add a trustline-readiness check for each beneficiary's account before attempting payment, returning a clear per-beneficiary error if missing.
Add idempotency keys for payout execution to prevent double-payment on retry.
Extend
DistributionOutput/BeneficiaryAmountwith execution status fields (TransactionHash,Status,FailureReason).Implement batching (multi-operation transaction or sequential submission with fee-bump) for distributions with multiple beneficiaries.
Reduce the on-chain payout amount by
TaxWithheldand record the withheld amount for tax reporting.Add partial-failure handling so one beneficiary's failure doesn't silently drop or duplicate others' payouts.
Add a repository method to persist payout execution results (transaction hash, per-beneficiary status).
Add retry with backoff for transient RPC failures during payout submission, mirroring
waitForTransactioninstellar_client.go.Add structured logging for each payout attempt, including beneficiary, amount, and outcome.
Add unit tests covering successful multi-beneficiary payout, missing trustline, partial failure, and idempotent retry.
Acceptance Criteria
DistributionOutputcan be executed as real Stellar payment operations.UserIDresolves to a verified Stellar address before payout.TaxWithheldamounts are excluded from the on-chain transfer and recorded separately.Directory to Work on:
project-portal/project-portal-backend/