fix(transaction): surface refund processor fee in balance.refund event metadata - #14166
Closed
detail-app[bot] wants to merge 3 commits into
Closed
detail-app[bot] wants to merge 3 commits into
detail-app[bot] wants to merge 3 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
OpenAPI ChangesNo changes detected in the OpenAPI schema. |
Yopi
requested changes
Sep 9, 2026
| "tax_country": payment_transaction.tax_country, | ||
| "tax_state": payment_transaction.tax_state, | ||
| "fee": 0, | ||
| "fee": sum(-fee.amount for fee in transaction_fees), |
Contributor
There was a problem hiding this comment.
This isn't entirely correct, because it will deduct the fees Polar take as well, which is not something you will get back
Contributor
Author
There was a problem hiding this comment.
Addressed in 3c72f09. The filter now explicitly excludes any fee without a processor_fee_type (i.e. platform fees), ensuring only Stripe's refund processing fee is counted in the fee metadata field.
detail-app
Bot
force-pushed
the
detail/bug-fix/fix-transaction-surface-refund-processor-fee-in-ba-7a3477
branch
from
September 9, 2026 14:06
87805bf to
7ffece5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Detail bug report: View on Detail
Summary
Related Issue: polarsource/feedback#432
Surface the computed Stripe refund processor fee in the
balance.refundsystem event metadata so the merchant-facingGET /v1/events/andGET /v1/metrics/(net revenue) endpoints report it correctly.What
server/polar/transaction/service/refund.py: inRefundTransactionService.create, replaced the hardcoded"fee": 0in theBalanceRefundMetadatawith"fee": sum(-fee.amount for fee in transaction_fees).server/tests/transaction/service/test_refund.py: addedtest_valid_surfaces_processor_fee_in_event_metadatatoTestCreate, which overrides the autousecreate_refund_feesmock to return aprocessor_feeTransactionwithamount=-100and asserts the emittedbalance.refundevent'suser_metadata["fee"] == 100.Why
createalready computes the refund processor fees one statement earlier viaprocessor_fee_transaction_service.create_refund_fees(...)and stores them onrefund_transaction.incurred_transactions, but then builds the event metadata with a hardcoded"fee": 0, discarding the in-scopetransaction_fees.The dispute sibling (
dispute.py) surfaces its computed fee with the samesum(-fee.amount for fee in …)pattern; refund never matched it after the disputefeefield was retconned to processor-fee semantics ine01437f1b1. As a result, whenever Stripe reports a non-zero fee on a refund's balance transaction (cross-border / currency-conversion refunds, or accounts on plans that charge a fixed refund fee), the merchant'snet_revenue/net_cumulative_revenuemetrics were overstated by that fee amount (the Tinybirdmetrics_revenuepipe subtractsCOALESCE(e.fee, 0), which was always0), and the wrongfee: 0was returned directly via the events API. For the common domestic refund where Stripe reportsfee=0, the corrected formula is a no-op.How
Mirrored the dispute path exactly:
sum(-fee.amount for fee in transaction_fees), wheretransaction_feesis the list already computed and attached torefund_transaction.incurred_transactions. Eachprocessor_feeTransaction'samountis-balance_transaction.fee, so negating and summing yieldsbalance_transaction.fee—0for domestic refunds and the real fee otherwise. No schema or migration change is required:BalanceRefundMetadata.feeis already typedintand the downstream consumers (tinybird.pop("fee"),COALESCE(e.fee, 0), events API serialization) already handle a non-zeroint.The
revertpath's literal"fee": 0is left unchanged — there is no reversal-fee producer today, so that0is correct.Checklist
uv run task lint && uv run task lint_types)Testing
test_valid_surfaces_processor_fee_in_event_metadataoverrides thecreate_refund_feesmock to return aprocessor_feeTransactionwithamount=-100, drivesrefund_transaction_service.create, and assertsbalance.refundeventuser_metadata["fee"] == 100. Confirmed bi-directionally: it FAILS against the buggy hardcoded"fee": 0(assert 0 == 100) and passes with the fix.tests/transaction/service/suite (78 tests),ruff format --check+ruff checkon the repo (1711 files), mypy across all 1631 source files, and the custom AST lints (lint_check) are clean.tinybirdco/tinybird-localcontainer and thetbCLI (installed via the sametinybird.co/install.shstep CI uses), the Tinybird metrics suite (tests/metrics/test_tinybird_metrics.py, 18 tests), the metrics service suite (tests/metrics/test_service.py, 100 tests), and the events suite (tests/event/, 142 tests) all pass, confirming theCOALESCE(e.fee, 0)subtraction path and events API serialization remain correct with a non-zerofee.Automatic Fixes PRs can be configured here.