Add URL promo code pre-fill and fix discount stacking logic
- Checkout page reads ?code= or ?promo= from URL, pre-fills and locks the promo field, shows the promo discount in the summary instead of the 15% bundle discount - API: when a promo code % >= bundle %, replace the bundle discount entirely instead of stacking (e.g. MEMORIAL25 at 25% replaces the 15% bundle) - Also checks discount code expiration in the query Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5a07335b2f
commit
d39e10485f
2 changed files with 31 additions and 13 deletions
|
|
@ -975,25 +975,35 @@ router.post("/api/v1/compliance-orders/batch", async (req, res) => {
|
|||
const govFeeTotal = services.reduce((sum, s) => sum + (COMPLIANCE_SERVICES[s].gov_fee_cents || 0), 0);
|
||||
const subtotal = discountableTotal + nonDiscountableTotal + govFeeTotal;
|
||||
|
||||
// Bundle discount applies only to discountable services
|
||||
const bundleDiscountPct = discountableServices.filter(s => COMPLIANCE_SERVICES[s].price_cents > 0).length >= 2 ? 15 : 0;
|
||||
const bundleDiscountCents = Math.round(discountableTotal * bundleDiscountPct / 100);
|
||||
// Bundle discount applies only to discountable services (2+ services = 15%)
|
||||
let bundleDiscountPct = discountableServices.filter(s => COMPLIANCE_SERVICES[s].price_cents > 0).length >= 2 ? 15 : 0;
|
||||
let bundleDiscountCents = Math.round(discountableTotal * bundleDiscountPct / 100);
|
||||
|
||||
// Referral/promo code discount also applies only to discountable services
|
||||
// Referral/promo code discount — replaces bundle discount if promo is >= bundle %
|
||||
let promoDiscountCents = 0;
|
||||
if (discount_code) {
|
||||
try {
|
||||
const disc = await pool.query(
|
||||
`SELECT discount_pct, discount_flat_cents FROM discount_codes WHERE code = $1 AND active = true`,
|
||||
`SELECT discount_type, discount_value FROM discount_codes WHERE code = $1 AND active = true AND (expires_at IS NULL OR expires_at > now())`,
|
||||
[discount_code.toUpperCase().trim()],
|
||||
);
|
||||
if (disc.rows.length > 0) {
|
||||
const d = disc.rows[0] as Record<string, unknown>;
|
||||
const discountableAfterBundle = discountableTotal - bundleDiscountCents;
|
||||
if ((d.discount_pct as number) > 0) {
|
||||
promoDiscountCents = Math.round(discountableAfterBundle * (d.discount_pct as number) / 100);
|
||||
} else if ((d.discount_flat_cents as number) > 0) {
|
||||
promoDiscountCents = Math.min(d.discount_flat_cents as number, discountableAfterBundle);
|
||||
const discType = d.discount_type as string;
|
||||
const discValue = d.discount_value as number;
|
||||
if (discType === "percent" && discValue > 0) {
|
||||
// If promo % >= bundle %, replace bundle discount entirely (don't stack)
|
||||
if (discValue >= bundleDiscountPct) {
|
||||
bundleDiscountPct = 0;
|
||||
bundleDiscountCents = 0;
|
||||
promoDiscountCents = Math.round(discountableTotal * discValue / 100);
|
||||
} else {
|
||||
// Smaller promo stacks on top of bundle
|
||||
const afterBundle = discountableTotal - bundleDiscountCents;
|
||||
promoDiscountCents = Math.round(afterBundle * discValue / 100);
|
||||
}
|
||||
} else if (discType === "flat" && discValue > 0) {
|
||||
promoDiscountCents = Math.min(discValue, discountableTotal - bundleDiscountCents);
|
||||
}
|
||||
}
|
||||
} catch { /* discount_codes table may not exist */ }
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@ function usd(c){return "$"+(c/100).toLocaleString("en-US",{minimumFractionDigits
|
|||
var params=new URLSearchParams(window.location.search);
|
||||
var rawKeys=(params.get("services")||"").split(",").map(function(s){return s.trim()}).filter(Boolean);
|
||||
var frn=params.get("frn")||"";
|
||||
var promoFromUrl=params.get("code")||params.get("promo")||"";
|
||||
var slugs=[]; var seen={};
|
||||
rawKeys.forEach(function(k){var s=CHECK_TO_SLUG[k]||k; if(SLUG_META[s]&&!seen[s]){slugs.push(s);seen[s]=1}});
|
||||
if(slugs.indexOf("fcc-499a")>=0&&slugs.indexOf("fcc-499a-499q")>=0) slugs.splice(slugs.indexOf("fcc-499a"),1);
|
||||
|
|
@ -326,15 +327,19 @@ function renderServices() {
|
|||
var svcTotal=discountableTotal+nonDiscountableTotal;
|
||||
var subtotal=svcTotal+govTotal;
|
||||
var discountableSlugs = selectedSlugs.filter(function(s){ var m=SLUG_META[s]||{}; return m.price > 0 && !m.nodiscount; });
|
||||
var hasDisc=discountableSlugs.length>=2;
|
||||
var hasPromo=!!promoFromUrl;
|
||||
var hasDisc=!hasPromo && discountableSlugs.length>=2;
|
||||
var disc=hasDisc?Math.round(discountableTotal*0.15):0;
|
||||
var promoDisc=hasPromo?Math.round(discountableTotal*0.25):0;
|
||||
|
||||
var summary=document.createElement("div");
|
||||
summary.className="mt-4 p-4 bg-gray-50 border border-gray-200 rounded-lg";
|
||||
var rows='<div class="flex justify-between text-sm mb-1"><span>'+selectedSlugs.length+' service'+(selectedSlugs.length>1?'s':'')+'</span><span>'+usd(svcTotal)+'</span></div>';
|
||||
if(govTotal>0) rows+='<div class="flex justify-between text-sm text-gray-500 mb-1"><span>Government filing fees (passthrough)</span><span>'+usd(govTotal)+'</span></div>';
|
||||
if(hasDisc) rows+='<div class="flex justify-between text-sm text-green-700 mb-1"><span>Bundle discount (15%)</span><span>-'+usd(disc)+'</span></div>';
|
||||
rows+='<div class="flex justify-between font-bold text-base border-t border-gray-300 pt-2 mt-1"><span>Total</span><span>'+usd(subtotal-disc)+'</span></div>';
|
||||
if(hasPromo) rows+='<div class="flex justify-between text-sm text-green-700 mb-1"><span>Memorial Day discount (25%) — code '+promoFromUrl.toUpperCase()+'</span><span>-'+usd(promoDisc)+'</span></div>';
|
||||
else if(hasDisc) rows+='<div class="flex justify-between text-sm text-green-700 mb-1"><span>Bundle discount (15%)</span><span>-'+usd(disc)+'</span></div>';
|
||||
var totalDisc=hasPromo?promoDisc:disc;
|
||||
rows+='<div class="flex justify-between font-bold text-base border-t border-gray-300 pt-2 mt-1"><span>Total</span><span>'+usd(subtotal-totalDisc)+'</span></div>';
|
||||
summary.innerHTML=rows;
|
||||
listEl.appendChild(summary);
|
||||
|
||||
|
|
@ -397,6 +402,9 @@ function renderServices() {
|
|||
'</form>';
|
||||
listEl.appendChild(actions);
|
||||
|
||||
// Pre-fill promo code from URL
|
||||
if(promoFromUrl){var promoEl=document.getElementById("pw-promo");if(promoEl){promoEl.value=promoFromUrl.toUpperCase();promoEl.readOnly=true;promoEl.style.background="#f0fdf4";promoEl.style.borderColor="#86efac";}}
|
||||
|
||||
// Batch checkout handler
|
||||
document.getElementById("pw-batch-form").addEventListener("submit", function(e){
|
||||
e.preventDefault();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue