fix(billing): validate discount code via server action, use DEV_DISCOUNT_CODE server-only env var
- validateDiscountCodeAction: new server action, reads DEV_DISCOUNT_CODE at runtime (not baked into client bundle like NEXT_PUBLIC_ vars) - applyDiscount(): calls server action instead of client-side check - Removes validateDiscountCode client-side import from upgrade-section
This commit is contained in:
@@ -11,8 +11,9 @@ import {
|
||||
startCheckoutAction,
|
||||
getPayTRTokenAction,
|
||||
requestEnterpriseInquiryAction,
|
||||
validateDiscountCodeAction,
|
||||
} from "@/lib/appwrite/subscription-actions";
|
||||
import { PLAN_CATALOG, PLAN_RANK, planPriceDisplay, planPrice, validateDiscountCode } from "@/lib/appwrite/subscription-types";
|
||||
import { PLAN_CATALOG, PLAN_RANK, planPriceDisplay, planPrice } from "@/lib/appwrite/subscription-types";
|
||||
import type { TenantPlan, PlanPeriod } from "@/lib/appwrite/schema";
|
||||
|
||||
const PLAN_ICONS: Record<string, React.ReactNode> = {
|
||||
@@ -69,14 +70,16 @@ export function UpgradeSection({
|
||||
|
||||
function applyDiscount() {
|
||||
if (!discountCode.trim()) return;
|
||||
const fraction = validateDiscountCode(discountCode);
|
||||
if (fraction === null) {
|
||||
toast.error("Geçersiz indirim kodu.");
|
||||
setDiscountApplied(null);
|
||||
return;
|
||||
}
|
||||
setDiscountApplied(fraction);
|
||||
toast.success(`İndirim kodu uygulandı: %${Math.round(fraction * 100)} indirim`);
|
||||
startTransition(async () => {
|
||||
try {
|
||||
const fraction = await validateDiscountCodeAction(discountCode.trim());
|
||||
setDiscountApplied(fraction);
|
||||
toast.success(`İndirim kodu uygulandı: %${Math.round(fraction * 100)} indirim`);
|
||||
} catch {
|
||||
toast.error("Geçersiz indirim kodu.");
|
||||
setDiscountApplied(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getDisplayPrice(plan: typeof plans[0]): number {
|
||||
|
||||
@@ -166,6 +166,14 @@ export async function startPolarCheckoutAction(formData: FormData): Promise<void
|
||||
redirect(checkout.url);
|
||||
}
|
||||
|
||||
// ── Discount code validation (server-side, reads DEV_DISCOUNT_CODE env var) ────
|
||||
|
||||
export async function validateDiscountCodeAction(code: string): Promise<number> {
|
||||
const fraction = validateDiscountCode(code);
|
||||
if (fraction === null) throw new Error("Geçersiz indirim kodu.");
|
||||
return fraction;
|
||||
}
|
||||
|
||||
// ── PayTR checkout ─────────────────────────────────────────────────────────────
|
||||
|
||||
export async function getPayTRTokenAction(formData: FormData): Promise<string> {
|
||||
|
||||
@@ -15,9 +15,8 @@ export const DISCOUNT_CODES: Record<string, number> = {
|
||||
|
||||
export function validateDiscountCode(code: string): number | null {
|
||||
const normalized = code.trim().toUpperCase();
|
||||
// NEXT_PUBLIC_DEV_DISCOUNT_CODE: forces price to 1 TL for testing
|
||||
// Set in Coolify env vars — remove when done testing
|
||||
const devCode = process.env.NEXT_PUBLIC_DEV_DISCOUNT_CODE?.trim().toUpperCase();
|
||||
// DEV_DISCOUNT_CODE: server-only env var, forces price to 1 TL for testing
|
||||
const devCode = process.env.DEV_DISCOUNT_CODE?.trim().toUpperCase();
|
||||
if (devCode && normalized === devCode) return 1;
|
||||
return DISCOUNT_CODES[normalized] ?? null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user