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,
|
startCheckoutAction,
|
||||||
getPayTRTokenAction,
|
getPayTRTokenAction,
|
||||||
requestEnterpriseInquiryAction,
|
requestEnterpriseInquiryAction,
|
||||||
|
validateDiscountCodeAction,
|
||||||
} from "@/lib/appwrite/subscription-actions";
|
} 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";
|
import type { TenantPlan, PlanPeriod } from "@/lib/appwrite/schema";
|
||||||
|
|
||||||
const PLAN_ICONS: Record<string, React.ReactNode> = {
|
const PLAN_ICONS: Record<string, React.ReactNode> = {
|
||||||
@@ -69,14 +70,16 @@ export function UpgradeSection({
|
|||||||
|
|
||||||
function applyDiscount() {
|
function applyDiscount() {
|
||||||
if (!discountCode.trim()) return;
|
if (!discountCode.trim()) return;
|
||||||
const fraction = validateDiscountCode(discountCode);
|
startTransition(async () => {
|
||||||
if (fraction === null) {
|
try {
|
||||||
toast.error("Geçersiz indirim kodu.");
|
const fraction = await validateDiscountCodeAction(discountCode.trim());
|
||||||
setDiscountApplied(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setDiscountApplied(fraction);
|
setDiscountApplied(fraction);
|
||||||
toast.success(`İndirim kodu uygulandı: %${Math.round(fraction * 100)} indirim`);
|
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 {
|
function getDisplayPrice(plan: typeof plans[0]): number {
|
||||||
|
|||||||
@@ -166,6 +166,14 @@ export async function startPolarCheckoutAction(formData: FormData): Promise<void
|
|||||||
redirect(checkout.url);
|
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 ─────────────────────────────────────────────────────────────
|
// ── PayTR checkout ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export async function getPayTRTokenAction(formData: FormData): Promise<string> {
|
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 {
|
export function validateDiscountCode(code: string): number | null {
|
||||||
const normalized = code.trim().toUpperCase();
|
const normalized = code.trim().toUpperCase();
|
||||||
// NEXT_PUBLIC_DEV_DISCOUNT_CODE: forces price to 1 TL for testing
|
// DEV_DISCOUNT_CODE: server-only env var, forces price to 1 TL for testing
|
||||||
// Set in Coolify env vars — remove when done testing
|
const devCode = process.env.DEV_DISCOUNT_CODE?.trim().toUpperCase();
|
||||||
const devCode = process.env.NEXT_PUBLIC_DEV_DISCOUNT_CODE?.trim().toUpperCase();
|
|
||||||
if (devCode && normalized === devCode) return 1;
|
if (devCode && normalized === devCode) return 1;
|
||||||
return DISCOUNT_CODES[normalized] ?? null;
|
return DISCOUNT_CODES[normalized] ?? null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user