Reusable Stripe-backed discount codes (the promo generator) and the truly-one-time Doozer+ 50%-off-for-life offer shown on claim. Money-safety, single-fire, and how they map to Stripe coupons.
Two things, one file (includes/promo-codes.php), one Stripe primitive (coupons). Shipped v2.2.411-413. All Stripe calls go through billingStripeRequest(); creating a coupon is config, not a charge — money only moves when someone completes checkout.
Admin: Revenue → Promo Codes (admin-promo-codes.php). CLI: dz promo do=create|list|void|lifetime. promoCodeCreate($opts) creates a real Stripe coupon + promotion_code (typeable at checkout) + a local promo_codes row. Four shapes, extensible:
| kind × duration | Stripe | meaning |
|---|---|---|
| percent · forever | percent_off + duration=forever |
X% off every renewal, FOR LIFE |
| percent · once | percent_off + duration=once |
X% off first period, regular after |
| amount · once | amount_off (cents) + duration=once |
$X off first period, regular after |
| amount · forever | amount_off + duration=forever |
$X off every renewal, FOR LIFE |
(repeating + N periods also supported.) One-time-use by default (max_redemptions=1). promoCodeVoid() deactivates the Stripe promo code + local row. Codes apply to any subscription SKU (default premium_yearly). The manual "someone begs" recovery code is just kind=percent value=50 duration=forever → e.g. PLUS50LIFE.
When someone CLAIMS their site is the perfect moment. On ?ajax=claim_code_redeem success (index.php), if the business has never been shown the offer, attach it; the redeem page (redeem-code.php → showClaimOffer) shows Doozer+ 50% off for LIFE, $49.50/yr forever with exactly two buttons:
?ajax=claim_offer_accept → billingCreateCheckoutSession('premium_yearly', …, $coupon)with the lifetime coupon promoLifetimeCoupon() (percent_off:50, duration:forever, deterministic id doozer-plus-50-forever-<mode>) auto-applied. Marks the offer accepted.
?ajax=claim_offer_decline → marks declined. Gone for good.Truly one-time: business_offers (UNIQUE (business_id, offer_key)); promoBusinessOfferShow() uses INSERT IGNORE and returns true only the FIRST time — so a 2nd scan, a resend, or a revisit never re-shows it. No code, no take-backs. Doozer+ ONLY (never Keeper); all future upgrades at regular price. The admin recovery code is the deliberate escape hatch for someone whose card failed.
billingCreateCheckoutSession($sku,$user,$s,$c,$extraMeta,$coupon='') — a non-empty $coupon auto-applies discounts[0][coupon] and drops allow_promotion_codes (Stripe rejects both).
Related: billing, claim codes, implicit verification, [[feedback_right_offer_right_audience]].