Add percentage and fixed amount discount code types - #3727
Draft
dparker1005 wants to merge 4 commits into
Draft
Conversation
Discount codes now have a discount_type: the legacy set_price behavior (per-level price overrides, unchanged and still the default), percentage (e.g. 20% off), or fixed (e.g. $10 off). Formula codes store one rule on pmpro_discount_codes (discount_value, apply_to_initial, apply_to_recurring) shared across all eligible levels, and inherit the level's billing cycle, billing limit, trial, and expiration. Pricing is resolved through a new pmpro_get_discounted_level_for_code() in includes/discount-codes.php, now used by pmpro_getLevelAtCheckout(), the AJAX applydiscountcode service (per level, before multi-level summing), MemberOrder::getMembershipLevel(), and the 2Checkout INS handler. Percentage/fixed results are rounded with pmpro_round_price() and clamped at 0. The pmpro_discount_code_level filter still runs at the existing call sites after calculation, and a new pmpro_get_discounted_level_for_code filter runs inside the resolver. For formula codes, pmpro_discount_codes_levels keeps marking eligible levels and its pricing columns hold a snapshot of the calculated prices for backwards compatibility with code reading the table directly. Snapshots refresh on code save and on level save. The discount code edit screen gets Discount Type / Discount Amount / Applies To fields (per-level pricing collapses to a note for formula codes) plus Select All / Deselect All buttons for the levels list, the list table shows the discount rule, and PMPro_Discount_Code and the REST discount_code endpoint accept the new fields. Existing codes default to set_price with no migration needed (dbDelta, db version 3.9). Also fixes pmpro_get_discount_code() lookups to match code strings before IDs so fully numeric codes resolve correctly, and reuses the helper in place of duplicate raw code-lookup queries in checkout completion and the AJAX service. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ount-codes # Conflicts: # adminpages/discountcodes.php # includes/upgradecheck.php
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…he panel Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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 this does
Extends discount codes with formula-based discounts alongside the existing per-level price overrides. Each code now has a
discount_type:set_price— existing behavior, unchanged; remains the default. Existing codes need no migration.percentage— e.g. 20% off the level's regular pricing.fixed— e.g. $10 off the level's regular pricing.Formula codes store one rule on
pmpro_discount_codes(discount_value,apply_to_initial,apply_to_recurring) shared across all eligible levels, and inherit everything else — billing cycle, billing limit, trial, expiration — from the level. Sites that need custom structure plus a discount keep usingset_price.How pricing is resolved
New
pmpro_get_discounted_level_for_code( $level, $code )inincludes/discount-codes.phpis the single source of truth: it confirms the level is eligible viapmpro_discount_codes_levels, applies the rule to the level's current pricing, rounds withpmpro_round_price(), and clamps at $0. All four runtime paths that previously readpmpro_discount_codes_levelsas final pricing now use it:pmpro_getLevelAtCheckout()applydiscountcodeservice (resolved per level before the multi-level combined sum)MemberOrder::getMembershipLevel()(the order fallback used by IPN handlers)The
pmpro_discount_code_levelfilter still runs at its existing call sites after calculation, so documented filter-based extensions keep receiving final prices. A newpmpro_get_discounted_level_for_codefilter runs inside the resolver.Backwards compatibility for direct table readers
pmpro_discount_codes_levelsstill holds one row per eligible level for every code type. For formula codes, its pricing columns hold a snapshot of the calculated prices — refreshed on code save and on level save (pmpro_save_membership_level) — so add-ons reading the table directly see plausible, usually-exact prices instead of zeros. Checkout never trusts the snapshot; it always recalculates. This also means the existing gateway-compatibility checks evaluate correct cycle/trial values for formula codes with no changes.Admin / API
PMPro_Discount_CodeandPOST /pmpro/v1/discount_codeaccept the new fields.Also included
pmpro_get_discount_code()to match code strings before IDs so fully numeric codes (e.g. a code literally named2025) resolve correctly at checkout.pmpro_complete_checkout(),pmpro_getLevelAtCheckout(), and the AJAX service with the helper; the resolver now receives the already-fetched code row instead of refetching (once per level in multi-level checkout).Testing done (local site, Stripe-less flows)
set_pricecodes resolve identically (including thelevel_idpropertyMemberOrderdepends on).2+3→ $88 for 20% off $10 + $100).2025applies its set_price override at checkout after the lookup-order fix.Notes for reviewers
merge-subscription-delays-set-expiration-datesbranch touches the same per-level UI region ofadminpages/discountcodes.php; whichever lands second has a small mechanical conflict.pmpro_check_discount_code_level_for_gateway_compatibility()queriespmpro_discount_codes_levelsby anidcolumn that doesn't exist, so its check-all branch silently passes — worth a separate issue.@since TBDplaceholders to be filled at release.🤖 Generated with Claude Code