Add email-restricted discount codes and $0 order bypass
- discount_codes.allowed_emails: when set, code only valid for listed emails - Flat discounts now replace bundle discount (don't stack) - $0 orders skip all payment gateways, mark paid immediately, redirect to success - FREEDOM249: $249 flat off restricted to 4+ deficiency carriers Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
868b0eeca5
commit
6d441a5cc0
2 changed files with 45 additions and 2 deletions
|
|
@ -880,6 +880,35 @@ router.post("/api/v1/checkout/create-session", async (req, res) => {
|
|||
};
|
||||
const paymentMethodTypes = STRIPE_PAYMENT_METHOD_MAP[payment_method] ?? ["card"];
|
||||
|
||||
// ── $0 orders: skip all payment gateways, mark paid immediately ──────
|
||||
const effectiveTotal = base_cents - discount_cents;
|
||||
if (effectiveTotal <= 0 && base_cents > 0) {
|
||||
const zeroTable: Record<string, string> = {
|
||||
canada_crtc: "canada_crtc_orders",
|
||||
formation: "formation_orders",
|
||||
bundle: "bundle_orders",
|
||||
compliance: "compliance_orders",
|
||||
compliance_batch: "compliance_orders",
|
||||
};
|
||||
const zt = zeroTable[order_type];
|
||||
if (zt) {
|
||||
const whereCol = order_type === "compliance_batch" ? "batch_id" : "order_number";
|
||||
await pool.query(
|
||||
`UPDATE ${zt} SET payment_status = 'paid', payment_method = 'free', status = 'pending', surcharge_cents = 0, surcharge_pct = 0 WHERE ${whereCol} = $1`,
|
||||
[order_id],
|
||||
);
|
||||
}
|
||||
console.log(`[checkout] $0 order — skipping payment for ${order_type} ${order_id} (discount ${discount_cents} >= base ${base_cents})`);
|
||||
res.json({
|
||||
checkout_url: `${DOMAIN}/order/success?order_id=${order_id}&order_type=${order_type}&free=1`,
|
||||
session_id: null,
|
||||
total_cents: 0,
|
||||
surcharge_cents: 0,
|
||||
surcharge_pct: 0,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (payment_method === "crypto") {
|
||||
// Crypto orders bypass Stripe — create SHKeeper invoice via API
|
||||
const shkeeperUrl = process.env.SHKEEPER_URL || "http://127.0.0.1:5000";
|
||||
|
|
|
|||
|
|
@ -984,9 +984,20 @@ router.post("/api/v1/compliance-orders/batch", async (req, res) => {
|
|||
if (discount_code) {
|
||||
try {
|
||||
const disc = await pool.query(
|
||||
`SELECT discount_type, discount_value FROM discount_codes WHERE code = $1 AND active = true AND (expires_at IS NULL OR expires_at > now())`,
|
||||
`SELECT discount_type, discount_value, allowed_emails 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) {
|
||||
// If allowed_emails is set, verify the customer email is in the list
|
||||
const allowedEmails = disc.rows[0].allowed_emails as string[] | null;
|
||||
if (allowedEmails && allowedEmails.length > 0) {
|
||||
const customerEmail = (customer_email || "").toLowerCase().trim();
|
||||
if (!allowedEmails.map((e: string) => e.toLowerCase()).includes(customerEmail)) {
|
||||
// Silently skip — code exists but not valid for this email
|
||||
disc.rows.length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (disc.rows.length > 0) {
|
||||
const d = disc.rows[0] as Record<string, unknown>;
|
||||
const discType = d.discount_type as string;
|
||||
|
|
@ -1003,7 +1014,10 @@ router.post("/api/v1/compliance-orders/batch", async (req, res) => {
|
|||
promoDiscountCents = Math.round(afterBundle * discValue / 100);
|
||||
}
|
||||
} else if (discType === "flat" && discValue > 0) {
|
||||
promoDiscountCents = Math.min(discValue, discountableTotal - bundleDiscountCents);
|
||||
// Flat discount replaces bundle discount (don't stack)
|
||||
bundleDiscountPct = 0;
|
||||
bundleDiscountCents = 0;
|
||||
promoDiscountCents = Math.min(discValue, discountableTotal);
|
||||
}
|
||||
}
|
||||
} catch { /* discount_codes table may not exist */ }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue