Skip to content

Fix plugin payout currency mismatch for non-USD developers#395

Merged
simonhamp merged 1 commit into
mainfrom
bubbly-toad
Jun 9, 2026
Merged

Fix plugin payout currency mismatch for non-USD developers#395
simonhamp merged 1 commit into
mainfrom
bubbly-toad

Conversation

@simonhamp

Copy link
Copy Markdown
Member

Problem

Every plugin payout for a developer whose payout_currency is not USD was failing in production with:

The currency of source_transaction's balance transaction (usd) must be the same as the transfer currency (eur)

When a transfer is linked to a specific charge via source_transaction, Stripe requires the transfer's currency to match the source charge's currency. We were instead setting the transfer currency to the developer's payout_currency (e.g. EUR), while the source charge was in the customer's payment currency (USD) — so the two never matched and the payout was marked failed.

This affected every non-USD developer, not just a one-off.

Fix

StripeConnectService::processTransfer() now uses the source charge's currency as the transfer currency. Stripe handles FX to the connected account's preferred payout currency on the destination side.

  • Renamed getChargeIdFromPayout()getChargeDetailsFromPayout(), returning both the charge id and currency from the PaymentIntent (no extra Stripe round-trip).
  • Falls back to the developer's payout_currency when no payment intent is linked to the license.
  • Added currency to the success log for easier future debugging.

Tests

Added tests/Feature/Services/StripeConnectServiceTest.php:

  • EUR developer + USD charge → transfer created in usd with source_transaction set; payout marked transferred.
  • License without a payment intent → falls back to the developer's payout_currency and omits source_transaction.

Follow-ups (not in this PR)

  • Existing failed payout WIP: Homepage #2 is stuck at status = failed; it needs payouts:retry-failed --payout-id=2 (or a manual reset to pending) once this deploys.
  • The ProcessPayoutTransfer job's $tries = 3 is inert because processTransfer() swallows its own exceptions — the queue never sees a failure to retry.
  • PluginPayout has no failure_reason/failed_at/attempts columns, so failures are only visible in logs.

🤖 Generated with Claude Code

Use the source charge's currency for Stripe transfers instead of the
developer's payout currency. Stripe requires the transfer currency to
match the source_transaction's charge currency; FX to the connected
account is handled on the destination side. Previously every non-USD
developer's payouts failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@simonhamp simonhamp marked this pull request as ready for review June 9, 2026 15:16
@simonhamp simonhamp merged commit acf0e8e into main Jun 9, 2026
2 checks passed
@simonhamp simonhamp deleted the bubbly-toad branch June 9, 2026 15:58
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.

1 participant