Pre-apply Stripe coupons to product prices#431
Merged
Conversation
Add an optional Stripe coupon ID to product prices so admins can offer a discount (e.g. subscriber pricing for the Masterclass) without creating a separate Stripe price. When a buyer's resolved price carries a coupon, it is pre-applied to the checkout session via `discounts`. - Add nullable stripe_coupon_id column to product_prices - Course and cart checkout pre-apply the coupon (dropping the manual promo code box, which Stripe disallows alongside discounts) - Course pages now display the real price from the database per user, with the discounted amount derived from the coupon's Stripe config (cached), replacing the hardcoded 199/299 values - Tie-break equal-amount prices by tier priority so a coupon-bearing subscriber price wins over an equal regular price - Filament pricing form/table expose the coupon field Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an optional stripe_price_id to product prices. When set, the amount and currency are locked in the admin and synced from Stripe on every save, and checkout charges that Stripe price instead of an ad-hoc amount (course checkout falls back to the legacy env price IDs when unset). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
simonhamp
marked this pull request as ready for review
July 16, 2026 16:43
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.
What & why
Lets admins offer a discount on a product (e.g. subscriber pricing for the Masterclass) by attaching an existing Stripe coupon to a product price — no need to create a separate Stripe price. When a buyer's resolved price carries a coupon, it is pre-applied to the Stripe checkout session so the discount happens automatically.
This started from wanting to give subscribers ~$100 off the Masterclass without duplicating prices in Stripe.
Changes
stripe_coupon_idcolumn onproduct_prices.CartController+course.checkoutroute): when the resolved price has a coupon, the session is created withdiscounts => [['coupon' => ...]]. The full price is charged as the line item and Stripe deducts the discount itself. Because Stripe rejectsallow_promotion_codesalongsidediscounts, the manual promo-code box is dropped on those sessions only. Multiple coupons in one cart → first applied, rest logged./courseand the/dashboard/courseLivewire component) now show the real price from the database per user. The discounted amount is derived from the coupon's actual Stripe configuration (amount_off/percent_off), fetched and cached for 10 minutes, with a safe fallback to full price if the coupon can't be resolved. This replaces the previously hardcoded 199/299 values.Product::getBestPriceForUser()now tie-breaks equal amounts by tier priority, so a coupon-bearing subscriber price wins over an equal-priced regular row.Admin usage
Notes / caveats
Testing
CheckoutCouponTest(9 tests): tier resolution,amount_off/percent_offdisplay math, coupon-fetch-failure fallback, and both checkout flows with/without a coupon.CoursePageTestandCourseContentTestto assert DB-driven and subscriber-discounted pricing.pintclean.🤖 Generated with Claude Code