antiwork/gumroad · error · ChargeProcessorCardError
net_negative_seller_revenue
net_negative_seller_revenue
Error message
The purchase total is too small for us to process. Please add another item to your order or contact the creator.
What it means
Raised as ChargeProcessorCardError (code net_negative_seller_revenue) by StripeIntentChargeRouting.validate_seller_proceeds! before the PaymentIntent is submitted. For a direct (Stripe Connect) account the application fee must not consume the entire charge (seller proceeds >= 0 subunits); for a destination-charge account the transfer must be at least 1 subunit. If amount_cents - amount_for_gumroad_cents falls below that floor, the charge is rejected locally so Stripe never sees an intent with a zero/negative seller amount.
Source
Thrown at app/business/payments/charging/implementations/stripe/stripe_intent_charge_routing.rb:82
MINIMUM_DIRECT_CHARGE_SELLER_PROCEEDS_CENTS
elsif destination_charge_account?(merchant_account)
MINIMUM_DESTINATION_TRANSFER_AMOUNT_CENTS
else
return
end
seller_amount_cents = amount_cents - amount_for_gumroad_cents
return if seller_amount_cents >= minimum_seller_proceeds_cents
ErrorNotifier.notify(
"Charge rejected before Stripe submit: seller proceeds would be non-positive",
reference:,
charge_amount_cents: amount_cents,
gumroad_amount_cents: amount_for_gumroad_cents,
seller_amount_cents:,
currency:
)
raise ChargeProcessorCardError.new(PurchaseErrorCode::NET_NEGATIVE_SELLER_REVENUE, SELLER_PROCEEDS_ERROR_MESSAGE)
end
end
View on GitHub (pinned to afeacbd394)
Solutions
- Raise the effective charge total above the fee (buyer adds another item, or the creator raises the price / minimum price above the fee floor) — the message already steers buyers there.
- If this fires for legitimate prices, audit the fee computation feeding amount_for_gumroad_cents (fee calculation or rounding bug) using the ErrorNotifier payload (charge_amount_cents, gumroad_amount_cents, seller_amount_cents, currency, reference).
- For destination-charge sellers, remember the floor is 1 subunit of the intent's currency, not USD — a 1-subunit JPY total still fails because the fee must leave >= 1 subunit for the transfer.
- Do not relax the guard: Stripe itself rejects a destination transfer of 0, so bypassing it only moves the failure downstream to a harder Stripe error.
Example fix
# before: submitting the intent and letting the guard raise params = StripeIntentChargeRouting.fee_params(merchant_account:, amount_cents: 30, amount_for_gumroad_cents: 30, currency: "usd", reference:) # after: validate proceeds first and block at the price layer floor = merchant_account.is_a_stripe_connect_account? ? 0 : 1 raise PriceTooLow if amount_cents - amount_for_gumroad_cents < floor params = StripeIntentChargeRouting.fee_params(merchant_account:, amount_cents:, amount_for_gumroad_cents:, currency:, reference:)
Defensive patterns
Strategy: validation
Validate before calling
floor = merchant_account.is_a_stripe_connect_account? ? 0 : 1 usable = (amount_cents - amount_for_gumroad_cents) >= floor raise InvalidCartError, "total too small" unless usable
Prevention
- Enforce a minimum product/tip total at the price layer so fee >= total can never reach checkout.
- Unit-test fee_params with the smallest supported amounts per currency.
- Remember floors are subunit counts in the intent's currency, not USD amounts.
When it happens
Trigger: Calling StripeIntentChargeRouting.fee_params (both client-confirm and server-confirm checkout assemble fees through it) with a tiny amount_cents where the Gumroad fee equals or exceeds the total — e.g. a 30-cent product where the fee is 30+ cents, or a 100%-off-plus-fee combination on a destination-charge seller where the transfer would be 0 subunits.
Common situations: Products priced at the minimum (a few cents/currency subunits) with the fee floor exceeding the price; discounts or pay-what-you-want minimums that drop the total below the fee; zero-decimal or low-subunit currencies (JPY) where a 1-unit total is fully consumed by the fee; miscomputed amount_for_gumroad_cents during fee-model changes.
Related errors
- Something went wrong.
- ${responseData.error}
- #{missing.to_sentence} #{missing.length == 1 ? "is" : "are"}
- #{label} must include katakana characters.
- #{label} may only contain #{allowed_description}.
AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21).
Data as JSON: /api/errors/e00c827efb9b9432.
Report an issue: GitHub.