@wtree/payload-ecommerce-coupon 3.77.2 → 3.77.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -1
- package/dist/collections/createReferralCodesCollection.d.ts.map +1 -1
- package/dist/collections/createReferralProgramsCollection.d.ts.map +1 -1
- package/dist/endpoints/applyCoupon.d.ts.map +1 -1
- package/dist/endpoints/partnerStats.d.ts.map +1 -1
- package/dist/endpoints/validateCoupon.d.ts.map +1 -1
- package/dist/hooks/recalculateCart.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +191 -64
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +191 -65
- package/dist/index.mjs.map +1 -1
- package/dist/types.d.ts +20 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utilities/calculateValues.d.ts +7 -1
- package/dist/utilities/calculateValues.d.ts.map +1 -1
- package/dist/utilities/sanitizePluginConfig.d.ts.map +1 -1
- package/dist/utilities/userRoles.d.ts +25 -0
- package/dist/utilities/userRoles.d.ts.map +1 -0
- package/package.json +2 -2
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["validateCouponCode"],"sources":["../src/collections/createCouponsCollection.ts","../src/collections/createReferralCodesCollection.ts","../src/collections/createReferralProgramsCollection.ts","../src/utilities/roundTo2.ts","../src/utilities/pricing.ts","../src/utilities/calculateValues.ts","../src/endpoints/applyCoupon.ts","../src/endpoints/partnerStats.ts","../src/endpoints/validateCoupon.ts","../src/hooks/recalculateCart.ts","../src/utilities/sanitizePluginConfig.ts","../src/plugin.ts","../src/client/hooks.ts","../src/utilities/getCartTotalWithDiscounts.ts","../src/utilities/recordCouponUsageForOrder.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nimport type { SanitizedCouponPluginOptions } from '../types'\n\nexport const createCouponsCollection = (\n pluginConfig: SanitizedCouponPluginOptions,\n): CollectionConfig => {\n const { collections, access, defaultCurrency, adminGroups } = pluginConfig\n\n return {\n slug: collections.couponsSlug,\n admin: {\n useAsTitle: 'code',\n defaultColumns: ['code', 'type', 'value', 'activeFrom', 'activeUntil'],\n group: adminGroups.couponsGroup,\n },\n access: {\n read: access.canUseCoupons || (() => true),\n create: access.isAdmin || (() => false),\n update: access.isAdmin || (() => false),\n delete: access.isAdmin || (() => false),\n },\n fields: [\n {\n name: 'code',\n type: 'text',\n required: true,\n unique: true,\n admin: {\n description: 'The coupon code that customers will enter',\n },\n },\n {\n name: 'description',\n type: 'text',\n admin: {\n description: 'Optional description for admin reference',\n },\n },\n {\n name: 'type',\n type: 'select',\n required: true,\n options: [\n { label: 'Percentage', value: 'percentage' },\n { label: 'Fixed Amount', value: 'fixed' },\n ],\n defaultValue: 'percentage',\n admin: {\n description: 'Whether this is a percentage or fixed amount discount',\n },\n },\n {\n name: 'value',\n type: 'number',\n required: true,\n admin: {\n description: `If percentage, 10 = 10%. If fixed, interpreted in ${defaultCurrency} (smallest currency units)`,\n step: 0.01,\n },\n },\n {\n name: 'maxDiscountAmount',\n type: 'number',\n admin: {\n description: `Maximum discount amount in ${defaultCurrency} (smallest currency unit). Leave empty for no cap.`,\n },\n },\n {\n name: 'usageLimit',\n type: 'number',\n admin: {\n description:\n 'Total times this coupon can be used across all customers. Empty = unlimited.',\n },\n },\n {\n name: 'perCustomerLimit',\n type: 'number',\n admin: {\n description: 'Times a single customer can use this coupon. Empty = unlimited.',\n },\n },\n {\n name: 'activeFrom',\n type: 'date',\n admin: {\n description:\n 'Coupon becomes active from this date. Leave empty for immediate activation.',\n },\n },\n {\n name: 'activeUntil',\n type: 'date',\n admin: {\n description: 'Coupon expires after this date. Leave empty for no expiration.',\n },\n },\n {\n name: 'minOrderValue',\n type: 'number',\n admin: {\n description: `Minimum order value required in ${defaultCurrency} (smallest currency units)`,\n },\n },\n {\n name: 'maxOrderValue',\n type: 'number',\n admin: {\n description: `Maximum order value allowed in ${defaultCurrency} (smallest currency units)`,\n },\n },\n {\n name: 'usageCount',\n type: 'number',\n defaultValue: 0,\n admin: {\n description: 'How many times this coupon has been used',\n readOnly: true,\n },\n },\n {\n name: 'createdBy',\n type: 'relationship',\n relationTo: 'users',\n admin: {\n readOnly: true,\n position: 'sidebar',\n },\n },\n ],\n hooks: {\n beforeChange: [\n ({ operation, req, data }) => {\n if (operation === 'create' && req.user) {\n data.createdBy = req.user.id\n }\n return data\n },\n ],\n },\n timestamps: true,\n }\n}\n","import type { CollectionConfig } from 'payload'\n\nimport type { SanitizedCouponPluginOptions } from '../types'\n\nexport const createReferralCodesCollection = (\n pluginConfig: SanitizedCouponPluginOptions,\n): CollectionConfig => {\n const { collections, access, adminGroups, defaultCurrency } = pluginConfig\n\n return {\n slug: collections.referralCodesSlug,\n admin: {\n useAsTitle: 'code',\n defaultColumns: ['code', 'partner', 'program', 'usageCount', 'isActive'],\n group: adminGroups.referralsGroup,\n },\n access: {\n read: ({ req }) => {\n // Partners can read their own codes, admins can read all\n const user = req?.user as\n | { id?: string; role?: string | string[]; roles?: string[] }\n | undefined\n if (!user) return false\n\n // Admin access\n if (\n user.role === 'admin' ||\n (Array.isArray(user.role) && user.role.includes('admin')) ||\n (Array.isArray(user.roles) && user.roles.includes('admin'))\n ) {\n return true\n }\n\n // Partner access - only their own codes\n if (\n user.role === 'partner' ||\n (Array.isArray(user.role) && user.role.includes('partner')) ||\n (Array.isArray(user.roles) && user.roles.includes('partner'))\n ) {\n return {\n partner: {\n equals: user.id,\n },\n }\n }\n\n // Default access from config\n return access.canUseReferrals ? access.canUseReferrals({ req } as any) : false\n },\n create: ({ req }) => {\n // Partners can create their own codes, admins can create any\n const user = req?.user as { role?: string | string[]; roles?: string[] } | undefined\n if (!user) return false\n\n if (\n user.role === 'admin' ||\n (Array.isArray(user.role) && user.role.includes('admin')) ||\n (Array.isArray(user.roles) && user.roles.includes('admin'))\n ) {\n return true\n }\n\n if (\n user.role === 'partner' ||\n (Array.isArray(user.role) && user.role.includes('partner')) ||\n (Array.isArray(user.roles) && user.roles.includes('partner'))\n ) {\n return true\n }\n\n return access.isAdmin ? access.isAdmin({ req } as any) : false\n },\n update: access.isAdmin || (() => false),\n delete: access.isAdmin || (() => false),\n },\n fields: [\n {\n name: 'code',\n type: 'text',\n required: true,\n unique: true,\n admin: {\n description: 'The referral code that customers will enter',\n },\n },\n {\n name: 'program',\n type: 'relationship',\n relationTo: collections.referralProgramsSlug,\n required: true,\n admin: {\n description: 'The referral program this code belongs to',\n },\n },\n {\n name: 'partner',\n type: 'relationship',\n relationTo: 'users',\n required: true,\n filterOptions: ({ data }) => {\n const user = data?.user as { role?: string | string[]; roles?: string[] } | undefined\n if (!user) return false\n // if (user.role === 'admin' || (Array.isArray(user.role) && user.role.includes('admin')) || (Array.isArray(user.roles) && user.roles.includes('admin'))) {\n // return true\n // }\n if (\n user.role === 'partner' ||\n (Array.isArray(user.role) && user.role.includes('partner')) ||\n (Array.isArray(user.roles) && user.roles.includes('partner'))\n ) {\n return true\n }\n return false\n },\n admin: {\n description: 'The partner who owns this referral code',\n },\n },\n {\n name: 'isActive',\n type: 'checkbox',\n defaultValue: true,\n admin: {\n description: 'Whether this referral code is currently active',\n },\n },\n {\n name: 'usageCount',\n type: 'number',\n defaultValue: 0,\n admin: {\n description: 'How many times this referral code has been used',\n readOnly: true,\n },\n },\n {\n name: 'usageLimit',\n type: 'number',\n admin: {\n description: 'Maximum times this code can be used. Empty = unlimited.',\n },\n },\n {\n name: 'expiresAt',\n type: 'date',\n admin: {\n description: 'When this referral code expires',\n },\n },\n {\n name: 'successfulReferralsCount',\n type: 'number',\n defaultValue: 0,\n admin: {\n description: 'Total count of successful referrals using this code',\n readOnly: true,\n },\n },\n {\n name: 'totalEarnings',\n type: 'number',\n defaultValue: 0,\n admin: {\n description: `Total earnings generated by this code in ${defaultCurrency}`,\n readOnly: true,\n },\n },\n {\n name: 'pendingEarnings',\n type: 'number',\n defaultValue: 0,\n admin: {\n description: `Pending earnings awaiting payout in ${defaultCurrency}`,\n readOnly: true,\n },\n },\n {\n name: 'paidEarnings',\n type: 'number',\n defaultValue: 0,\n admin: {\n description: `Total earnings paid out in ${defaultCurrency}`,\n readOnly: true,\n },\n },\n {\n name: 'metadata',\n type: 'json',\n admin: {\n description: 'Additional metadata for the referral code',\n position: 'sidebar',\n },\n },\n ],\n hooks: {\n beforeChange: [\n ({ operation, req, data }) => {\n // Auto-generate code if not provided\n if (operation === 'create' && !data.code && data.partner) {\n const timestamp = Date.now().toString(36)\n const random = Math.random().toString(36).substring(2, 8)\n data.code = `REF-${timestamp}-${random}`.toUpperCase()\n }\n\n // Auto-assign partner to current user if partner\n if (operation === 'create' && req.user) {\n const user = req.user as { id?: string; role?: string; roles?: string[] }\n if (\n user.role === 'partner' ||\n (Array.isArray(user.role) && user.role.includes('partner'))\n ) {\n data.partner = user.id\n }\n }\n\n return data\n },\n ],\n },\n timestamps: true,\n }\n}\n","import type { CollectionConfig } from 'payload'\n\nimport type { SanitizedCouponPluginOptions } from '../types'\n\ntype RuleData = {\n appliesTo?: 'all' | 'products' | 'segments' | 'categories'\n products?: unknown[]\n categories?: unknown[]\n tags?: unknown[]\n totalCommission?: { type?: 'fixed' | 'percentage'; value?: number; maxAmount?: number }\n partnerSplit?: number\n customerSplit?: number\n}\n\nfunction toNumber(value: unknown): number | null {\n return typeof value === 'number' && Number.isFinite(value) ? value : null\n}\n\nconst deriveCustomerSplit = (partnerSplit: unknown): number => {\n const partner = toNumber(partnerSplit)\n if (partner == null) return 0\n if (partner < 0) return 100\n if (partner > 100) return 0\n return 100 - partner\n}\n\nexport const createReferralProgramsCollection = (\n pluginConfig: SanitizedCouponPluginOptions,\n): CollectionConfig => {\n const { collections, access, defaultCurrency, adminGroups, referralConfig } = pluginConfig\n\n const beforeChange: NonNullable<CollectionConfig['hooks']>['beforeChange'] = [\n ({ data }: { data: Record<string, unknown> }) => {\n if (\n !data.commissionRules ||\n !Array.isArray(data.commissionRules) ||\n data.commissionRules.length === 0\n ) {\n throw new Error('At least one commission rule is required')\n }\n\n data.commissionRules = data.commissionRules.map(\n (rule: Record<string, unknown>, index: number) => {\n const r = rule as RuleData\n\n if (!r.totalCommission) {\n throw new Error(`Commission rule ${index + 1}: Total Commission is required`)\n }\n\n if (\n !r.totalCommission.type ||\n !['fixed', 'percentage'].includes(r.totalCommission.type)\n ) {\n throw new Error(\n `Commission rule ${index + 1}: Total Commission type must be fixed or percentage`,\n )\n }\n\n const totalValue = toNumber(r.totalCommission.value)\n if (totalValue == null || totalValue < 0) {\n throw new Error(\n `Commission rule ${index + 1}: Total Commission value must be a non-negative number`,\n )\n }\n if (r.totalCommission.type === 'percentage' && totalValue > 100) {\n throw new Error(\n `Commission rule ${index + 1}: Percentage Total Commission cannot exceed 100`,\n )\n }\n\n const maxAmount = toNumber(r.totalCommission.maxAmount)\n if (maxAmount != null && maxAmount < 0) {\n throw new Error(\n `Commission rule ${index + 1}: Max Amount must be a non-negative number`,\n )\n }\n\n const appliesTo = r.appliesTo ?? 'all'\n if (appliesTo === 'products' && (!r.products || r.products.length === 0)) {\n throw new Error(`Commission rule ${index + 1}: At least one product is required`)\n }\n\n if (\n (appliesTo === 'segments' || appliesTo === 'categories') &&\n (!r.categories || r.categories.length === 0) &&\n (!r.tags || r.tags.length === 0)\n ) {\n throw new Error(\n `Commission rule ${index + 1}: At least one category or tag is required`,\n )\n }\n\n const partnerSplit = toNumber(r.partnerSplit)\n if (partnerSplit == null || partnerSplit < 0 || partnerSplit > 100) {\n throw new Error(`Commission rule ${index + 1}: Partner Split must be between 0 and 100`)\n }\n\n const customerSplit = 100 - partnerSplit\n\n return {\n ...rule,\n appliesTo: appliesTo === 'categories' ? 'segments' : appliesTo,\n totalCommission: {\n type: r.totalCommission.type,\n value: totalValue,\n maxAmount: maxAmount ?? null,\n },\n partnerSplit,\n customerSplit,\n }\n },\n )\n\n return data\n },\n ]\n\n return {\n slug: collections.referralProgramsSlug,\n admin: {\n useAsTitle: 'name',\n defaultColumns: ['name', 'commissionRules', 'isActive'],\n group: adminGroups.referralsGroup,\n },\n access: {\n read: access.canUseReferrals || (() => true),\n create: access.isAdmin || (() => false),\n update: access.isAdmin || (() => false),\n delete: access.isAdmin || (() => false),\n },\n hooks: {\n beforeChange,\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n required: true,\n admin: {\n description: 'Name of the referral program for admin reference',\n },\n },\n {\n name: 'isActive',\n type: 'checkbox',\n defaultValue: true,\n admin: {\n description: 'Whether this referral program is currently active',\n },\n },\n {\n name: 'commissionRules',\n type: 'array',\n required: true,\n minRows: 1,\n admin: {\n description: 'Rules for referral commission and customer discount distribution.',\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n required: false,\n admin: { description: 'Optional rule label for admin clarity' },\n },\n {\n name: 'appliesTo',\n type: 'select',\n required: true,\n options: [\n { label: 'All Products', value: 'all' },\n { label: 'Specific Products', value: 'products' },\n { label: 'Categories and Tags', value: 'segments' },\n ],\n defaultValue: 'all',\n },\n {\n name: 'products',\n type: 'relationship',\n relationTo: 'products',\n hasMany: true,\n admin: {\n condition: (_: unknown, siblingData: { appliesTo?: string }) =>\n siblingData?.appliesTo === 'products',\n description: 'Products this rule applies to',\n },\n },\n {\n name: 'categories',\n type: 'relationship',\n relationTo: 'categories',\n hasMany: true,\n admin: {\n condition: (_: unknown, siblingData: { appliesTo?: string }) =>\n siblingData?.appliesTo === 'segments',\n description: 'Any matching category can activate this rule',\n },\n },\n {\n name: 'tags',\n type: 'relationship',\n relationTo: 'tags',\n hasMany: true,\n admin: {\n condition: (_: unknown, siblingData: { appliesTo?: string }) =>\n siblingData?.appliesTo === 'segments',\n description: 'Any matching tag can activate this rule',\n },\n },\n {\n name: 'totalCommission',\n type: 'group',\n admin: {\n description: 'Total commission pool to split between partner and customer',\n },\n fields: [\n {\n name: 'type',\n type: 'select',\n required: true,\n options: [\n { label: 'Fixed Amount', value: 'fixed' },\n { label: 'Percentage of Order', value: 'percentage' },\n ],\n defaultValue: 'percentage',\n },\n {\n name: 'value',\n type: 'number',\n required: true,\n min: 0,\n admin: {\n description: `Total commission value`,\n },\n },\n {\n name: 'maxAmount',\n type: 'number',\n min: 0,\n admin: {\n description: `Max commission cap per item in ${defaultCurrency}`,\n },\n },\n ],\n },\n {\n name: 'partnerSplit',\n type: 'number',\n required: true,\n min: 0,\n max: 100,\n defaultValue: referralConfig.defaultPartnerSplit,\n admin: {\n description: 'Percentage of total commission given to Partner (0-100)',\n },\n },\n {\n name: 'customerSplit',\n type: 'number',\n min: 0,\n max: 100,\n hooks: {\n beforeValidate: [\n ({ siblingData }: { siblingData?: { partnerSplit?: number } }) =>\n deriveCustomerSplit(siblingData?.partnerSplit),\n ],\n beforeChange: [\n ({ siblingData }: { siblingData?: { partnerSplit?: number } }) =>\n deriveCustomerSplit(siblingData?.partnerSplit),\n ],\n },\n admin: {\n readOnly: true,\n description: 'Auto-calculated from Partner Split (saved automatically)',\n },\n },\n ],\n },\n ],\n timestamps: true,\n }\n}\n","/**\n * Rounds a number to 2 decimal places (standard for monetary values).\n */\nexport function roundTo2(value: number): number {\n return Math.round(value * 100) / 100\n}\n","export const DEFAULT_PRICE_CURRENCY = 'AED'\n\ntype PriceEntity = {\n price?: number | null\n} & Record<string, unknown>\n\nfunction normalizeCurrencyCode(currencyCode?: string): string {\n if (!currencyCode) return DEFAULT_PRICE_CURRENCY\n return currencyCode.toUpperCase()\n}\n\nfunction readNumberField(entity: unknown, key: string): number | undefined {\n if (!entity || typeof entity !== 'object') return undefined\n const value = (entity as Record<string, unknown>)[key]\n return typeof value === 'number' ? value : undefined\n}\n\nexport function getPriceFieldKey(currencyCode: string): string {\n return `priceIn${normalizeCurrencyCode(currencyCode)}`\n}\n\nexport function readMoneyField(\n entity: PriceEntity | null | undefined,\n currencyCode: string,\n defaultCurrencyCode = DEFAULT_PRICE_CURRENCY,\n): number | undefined {\n if (!entity) return undefined\n\n const primaryField = getPriceFieldKey(currencyCode)\n const primary = readNumberField(entity, primaryField)\n if (typeof primary === 'number') return primary\n\n const fallbackField = getPriceFieldKey(defaultCurrencyCode)\n if (fallbackField !== primaryField) {\n const fallback = readNumberField(entity, fallbackField)\n if (typeof fallback === 'number') return fallback\n }\n\n return typeof entity.price === 'number' ? entity.price : undefined\n}\n\nexport function resolveMoneyField(\n entity: PriceEntity | null | undefined,\n currencyCode: string,\n defaultCurrencyCode = DEFAULT_PRICE_CURRENCY,\n): number {\n return readMoneyField(entity, currencyCode, defaultCurrencyCode) ?? 0\n}\n\nexport function getCartItemUnitPrice({\n item,\n product,\n variant,\n currencyCode,\n defaultCurrencyCode = DEFAULT_PRICE_CURRENCY,\n}: {\n item?: {\n price?: number | null\n unitPrice?: number | null\n } | null\n product?: PriceEntity | null\n variant?: PriceEntity | null\n currencyCode: string\n defaultCurrencyCode?: string\n}): number {\n if (typeof item?.price === 'number') return item.price\n if (typeof item?.unitPrice === 'number') return item.unitPrice\n if (variant) return resolveMoneyField(variant, currencyCode, defaultCurrencyCode)\n return resolveMoneyField(product, currencyCode, defaultCurrencyCode)\n}\n","import { roundTo2 } from './roundTo2'\nimport { getCartItemUnitPrice } from './pricing'\n\nexport function calculateCouponDiscount({ coupon, cartTotal }: { coupon: any; cartTotal: number }) {\n let discount = 0\n\n if (coupon.type === 'percentage') {\n discount = roundTo2((cartTotal * coupon.value) / 100)\n if (coupon.maxDiscountAmount != null && discount > coupon.maxDiscountAmount) {\n discount = roundTo2(coupon.maxDiscountAmount)\n }\n } else if (coupon.type === 'fixed') {\n discount = roundTo2(coupon.value)\n if (discount > cartTotal) {\n discount = roundTo2(cartTotal)\n }\n }\n\n return roundTo2(discount)\n}\n\nfunction relationId(value: any): string | number | null {\n if (value == null) return null\n if (typeof value === 'string' || typeof value === 'number') return value\n if (typeof value === 'object' && (typeof value.id === 'string' || typeof value.id === 'number')) {\n return value.id\n }\n return null\n}\n\nfunction normalizeIds(values: any[] | null | undefined): Array<string | number> {\n if (!Array.isArray(values)) return []\n return values.map(relationId).filter((v): v is string | number => v != null)\n}\n\nfunction getRuleSplits(rule: any): { partnerSplit: number; customerSplit: number } | null {\n const partnerRaw =\n typeof rule.partnerSplit === 'number'\n ? rule.partnerSplit\n : typeof rule.referrerSplit === 'number'\n ? rule.referrerSplit\n : null\n if (partnerRaw == null) return null\n\n const customerRaw =\n typeof rule.customerSplit === 'number'\n ? rule.customerSplit\n : typeof rule.refereeSplit === 'number'\n ? rule.refereeSplit\n : 100 - partnerRaw\n\n return {\n partnerSplit: partnerRaw,\n customerSplit: customerRaw,\n }\n}\n\nfunction calculateItemRewardByRule({\n rule,\n itemTotal,\n quantity,\n}: {\n rule: any\n itemTotal: number\n quantity: number\n}): { partner: number; customer: number } | null {\n // Shared model (v2)\n if (rule.totalCommission) {\n const splits = getRuleSplits(rule)\n if (!splits) return null\n\n let totalPot = 0\n if (rule.totalCommission.type === 'percentage') {\n totalPot = (itemTotal * rule.totalCommission.value) / 100\n } else {\n totalPot = rule.totalCommission.value * quantity\n }\n\n if (rule.totalCommission.maxAmount != null) {\n const maxPotForLine = rule.totalCommission.maxAmount * quantity\n if (totalPot > maxPotForLine) {\n totalPot = maxPotForLine\n }\n }\n\n return {\n partner: Math.floor((totalPot * splits.partnerSplit) / 100),\n customer: Math.floor((totalPot * splits.customerSplit) / 100),\n }\n }\n\n // Compatibility fallback for legacy direct rules during migration window.\n if (rule.referrerReward && rule.refereeReward) {\n let partner = 0\n if (rule.referrerReward.type === 'percentage') {\n partner = (itemTotal * rule.referrerReward.value) / 100\n } else {\n partner = rule.referrerReward.value * quantity\n }\n if (rule.referrerReward.maxReward != null && partner > rule.referrerReward.maxReward) {\n partner = rule.referrerReward.maxReward\n }\n\n let customer = 0\n if (rule.refereeReward.type === 'percentage') {\n customer = (itemTotal * rule.refereeReward.value) / 100\n } else {\n customer = rule.refereeReward.value * quantity\n }\n if (rule.refereeReward.maxReward != null && customer > rule.refereeReward.maxReward) {\n customer = rule.refereeReward.maxReward\n }\n\n return { partner, customer }\n }\n\n return null\n}\n\nfunction getItemCategoryIds(item: any): Array<string | number> {\n const productCategories = Array.isArray(item?.product?.categories)\n ? normalizeIds(item.product.categories)\n : []\n const singleCategory = relationId(item?.category ?? item?.product?.category)\n return [...productCategories, ...(singleCategory != null ? [singleCategory] : [])]\n}\n\nfunction getItemTagIds(item: any): Array<string | number> {\n return Array.isArray(item?.product?.tags) ? normalizeIds(item.product.tags) : []\n}\n\nfunction selectBestRuleForItem({\n rules,\n item,\n itemTotal,\n quantity,\n}: {\n rules: any[]\n item: any\n itemTotal: number\n quantity: number\n}): { rule: any; reward: { partner: number; customer: number } } | null {\n const productId = relationId(item.product)\n const itemCategoryIds = new Set(getItemCategoryIds(item))\n const itemTagIds = new Set(getItemTagIds(item))\n\n const productCandidates = rules.filter(\n (r: any) =>\n r.appliesTo === 'products' &&\n normalizeIds(r.products).some((id) => productId != null && id === productId),\n )\n\n const segmentCategoryCandidates = rules.filter((r: any) => {\n const isSegment = r.appliesTo === 'segments' || r.appliesTo === 'categories'\n if (!isSegment) return false\n return normalizeIds(r.categories).some((id) => itemCategoryIds.has(id))\n })\n\n const segmentTagCandidates = rules.filter((r: any) => {\n if (r.appliesTo !== 'segments') return false\n return normalizeIds(r.tags).some((id) => itemTagIds.has(id))\n })\n\n const allCandidates = rules.filter((r: any) => r.appliesTo === 'all')\n\n const levels = [productCandidates, segmentCategoryCandidates, segmentTagCandidates, allCandidates]\n const candidates = levels.find((level) => level.length > 0) ?? []\n if (!candidates.length) return null\n\n let best: { rule: any; reward: { partner: number; customer: number } } | null = null\n\n for (const rule of candidates) {\n const reward = calculateItemRewardByRule({ rule, itemTotal, quantity })\n if (!reward) continue\n\n if (!best) {\n best = { rule, reward }\n continue\n }\n\n if (reward.customer > best.reward.customer) {\n best = { rule, reward }\n continue\n }\n\n if (reward.customer === best.reward.customer && reward.partner > best.reward.partner) {\n best = { rule, reward }\n }\n }\n\n return best\n}\n\nexport function calculateCommissionAndDiscount({\n cartItems,\n program,\n currencyCode = 'AED',\n}: {\n cartItems: any[]\n program: any\n currencyCode?: string\n}): { partnerCommission: number; customerDiscount: number } {\n const rules = Array.isArray(program?.commissionRules) ? program.commissionRules : []\n\n if (!rules.length) {\n return { partnerCommission: 0, customerDiscount: 0 }\n }\n\n let totalPartnerCommission = 0\n let totalCustomerDiscount = 0\n\n for (const item of cartItems) {\n const product = typeof item.product === 'object' ? item.product : {}\n const variant = typeof item.variant === 'object' ? item.variant : {}\n\n const itemPrice = getCartItemUnitPrice({\n item,\n product,\n variant,\n currencyCode,\n })\n\n const quantity = item.quantity ?? 1\n const itemTotal = itemPrice * quantity\n\n const bestMatch = selectBestRuleForItem({\n rules,\n item: { ...item, product },\n itemTotal,\n quantity,\n })\n\n if (!bestMatch) continue\n\n totalPartnerCommission += bestMatch.reward.partner\n totalCustomerDiscount += bestMatch.reward.customer\n }\n\n return { partnerCommission: totalPartnerCommission, customerDiscount: totalCustomerDiscount }\n}\n","import type { Endpoint, PayloadHandler } from 'payload'\nimport type { SanitizedCouponPluginOptions } from '../types'\nimport {\n calculateCommissionAndDiscount,\n calculateCouponDiscount,\n} from '../utilities/calculateValues'\nimport { roundTo2 } from '../utilities/roundTo2'\n\ntype Args = {\n pluginConfig: SanitizedCouponPluginOptions\n}\n\n// Debug Capture\nconst globalDebugLogs: string[] = []\n\nconst getRelationId = (value: any): string | number | null => {\n if (value == null) return null\n if (typeof value === 'string' || typeof value === 'number') return value\n if (typeof value === 'object' && (typeof value.id === 'string' || typeof value.id === 'number')) {\n return value.id\n }\n return null\n}\n\nexport const applyCouponHandler =\n ({ pluginConfig }: Args): PayloadHandler =>\n async (req) => {\n globalDebugLogs.length = 0 // Reset logs\n const { payload } = req\n const { code: rawCode, cartID, customerEmail } = req.data || {}\n const code = typeof rawCode === 'string' ? rawCode.trim() : rawCode\n\n if (!code || !cartID) {\n return Response.json(\n {\n success: false,\n error: `${pluginConfig.enableReferrals ? 'Referral code' : 'Coupon code'} and cart ID are required`,\n },\n { status: 400 },\n )\n }\n\n try {\n // Find the cart first to check for existing codes\n const cartQuery = await payload.findByID({\n collection: 'carts',\n id: cartID,\n depth: 2,\n })\n\n if (!cartQuery) {\n return Response.json({ success: false, error: 'Cart not found' }, { status: 404 })\n }\n\n // Check if single code per cart is enforced\n if (pluginConfig.referralConfig.singleCodePerCart) {\n const hasExistingCoupon = cartQuery.appliedCoupon\n const hasExistingReferral = cartQuery.appliedReferralCode\n\n if (hasExistingCoupon || hasExistingReferral) {\n return Response.json(\n {\n success: false,\n error:\n 'A code has already been applied to this cart. Only one code can be used per order.',\n },\n { status: 400 },\n )\n }\n }\n\n if (pluginConfig.enableReferrals) {\n // Try referral code first\n const referralResult = await handleReferralCode({\n payload,\n code,\n cartID,\n cart: cartQuery,\n customerEmail,\n pluginConfig,\n })\n\n // If referral code not found and both systems allowed, try coupon\n if (\n !referralResult.ok &&\n referralResult.status === 404 &&\n pluginConfig.referralConfig.allowBothSystems\n ) {\n return await handleCouponCode({\n payload,\n code,\n cartID,\n cart: cartQuery,\n customerEmail,\n pluginConfig,\n })\n }\n\n return referralResult\n } else {\n // Coupon mode: handle coupons\n return await handleCouponCode({\n payload,\n code,\n cartID,\n cart: cartQuery,\n customerEmail,\n pluginConfig,\n })\n }\n } catch (error) {\n console.error('Code application error:', error)\n return Response.json({ success: false, error: 'Internal server error' }, { status: 500 })\n }\n }\n\n// Handle coupon application\nasync function handleCouponCode({\n payload,\n code,\n cartID,\n cart,\n customerEmail,\n pluginConfig,\n}: {\n payload: any\n code: string\n cartID: string\n cart: any\n customerEmail?: string\n pluginConfig: SanitizedCouponPluginOptions\n}) {\n // Find the coupon\n // Find the coupon (Case insensitive check: Exact -> Lower -> Upper)\n let couponQuery = await payload.find({\n collection: pluginConfig.collections.couponsSlug,\n where: {\n code: { equals: code },\n },\n limit: 1,\n })\n\n if (!couponQuery.docs.length) {\n couponQuery = await payload.find({\n collection: pluginConfig.collections.couponsSlug,\n where: {\n code: { equals: code.toLowerCase() },\n },\n limit: 1,\n })\n }\n\n if (!couponQuery.docs.length) {\n couponQuery = await payload.find({\n collection: pluginConfig.collections.couponsSlug,\n where: {\n code: { equals: code.toUpperCase() },\n },\n limit: 1,\n })\n }\n\n if (!couponQuery.docs.length) {\n return Response.json({ success: false, error: 'Invalid coupon code' }, { status: 404 })\n }\n\n const coupon = couponQuery.docs[0]\n\n // Check if coupon is active\n const now = new Date()\n const activeFrom = coupon.activeFrom ? new Date(coupon.activeFrom) : null\n const activeUntil = coupon.activeUntil ? new Date(coupon.activeUntil) : null\n\n if (activeFrom && now < activeFrom) {\n return Response.json({ success: false, error: 'Coupon is not yet active' }, { status: 400 })\n }\n\n if (activeUntil && now > activeUntil) {\n return Response.json({ success: false, error: 'Coupon has expired' }, { status: 400 })\n }\n\n // Check usage limits\n if (coupon.usageLimit && coupon.usageCount >= coupon.usageLimit) {\n return Response.json({ success: false, error: 'Coupon usage limit exceeded' }, { status: 400 })\n }\n\n // Per-customer limit: require customer email and count paid orders with this coupon for this customer\n if (coupon.perCustomerLimit != null && coupon.perCustomerLimit > 0) {\n const email = typeof customerEmail === 'string' ? customerEmail.trim() : ''\n if (!email) {\n return Response.json(\n { success: false, error: 'Customer email is required for this coupon.' },\n { status: 400 },\n )\n }\n const { ordersSlug, orderCustomerEmailField, orderPaymentStatusField, orderPaidStatusValue } =\n pluginConfig.orderIntegration\n const ordersQuery = await payload.find({\n collection: ordersSlug,\n where: {\n and: [\n { appliedCoupon: { equals: coupon.id } },\n { [orderCustomerEmailField]: { equals: email } },\n { [orderPaymentStatusField]: { equals: orderPaidStatusValue } },\n ],\n },\n limit: 0,\n })\n if (ordersQuery.totalDocs >= coupon.perCustomerLimit) {\n return Response.json(\n { success: false, error: 'You have reached the maximum uses for this coupon.' },\n { status: 400 },\n )\n }\n }\n\n // Check if coupon already applied to this cart\n if (getRelationId(cart.appliedCoupon) === coupon.id) {\n return Response.json(\n { success: false, error: 'Coupon already applied to this cart' },\n { status: 400 },\n )\n }\n\n // Calculate discount based on cart total\n const cartTotal = cart.subtotal || cart.total || 0\n\n // Check minimum order value\n if (coupon.minOrderValue && cartTotal < coupon.minOrderValue) {\n return Response.json(\n {\n success: false,\n error: `Minimum order value of ${coupon.minOrderValue} ${pluginConfig.defaultCurrency} required`,\n },\n { status: 400 },\n )\n }\n\n // Check maximum order value\n if (coupon.maxOrderValue && cartTotal > coupon.maxOrderValue) {\n return Response.json(\n {\n success: false,\n error: `Maximum order value of ${coupon.maxOrderValue} ${pluginConfig.defaultCurrency} exceeded`,\n },\n { status: 400 },\n )\n }\n\n const discountAmount = calculateCouponDiscount({ coupon, cartTotal })\n const total = roundTo2(Math.max(0, cartTotal - discountAmount))\n\n // Apply coupon to cart (usage is counted when order is placed via recordCouponUsageForOrder)\n await payload.update({\n collection: 'carts',\n id: cartID,\n data: {\n appliedCoupon: coupon.id,\n discountAmount,\n total,\n },\n })\n\n return Response.json({\n success: true,\n message: 'Coupon applied successfully',\n coupon: {\n code: coupon.code,\n type: coupon.type,\n value: coupon.value,\n },\n discount: discountAmount,\n currency: pluginConfig.defaultCurrency,\n debug: globalDebugLogs,\n })\n}\n\n// Handle referral code application\nasync function handleReferralCode({\n payload,\n code,\n cartID,\n cart,\n customerEmail: _customerEmail,\n pluginConfig,\n}: {\n payload: any\n code: string\n cartID: string\n cart: any\n customerEmail?: string\n pluginConfig: SanitizedCouponPluginOptions\n}) {\n // Find the referral code\n // Find the referral code (Case insensitive check: Exact -> Lower -> Upper)\n let referralQuery = await payload.find({\n collection: pluginConfig.collections.referralCodesSlug,\n where: {\n code: { equals: code },\n },\n limit: 1,\n depth: 1,\n })\n\n if (!referralQuery.docs.length) {\n referralQuery = await payload.find({\n collection: pluginConfig.collections.referralCodesSlug,\n where: {\n code: { equals: code.toLowerCase() },\n },\n limit: 1,\n depth: 1,\n })\n }\n\n if (!referralQuery.docs.length) {\n referralQuery = await payload.find({\n collection: pluginConfig.collections.referralCodesSlug,\n where: {\n code: { equals: code.toUpperCase() },\n },\n limit: 1,\n depth: 1,\n })\n }\n\n if (!referralQuery.docs.length) {\n return Response.json({ success: false, error: 'Invalid referral code' }, { status: 404 })\n }\n\n const referralCode = referralQuery.docs[0]\n\n // Check if referral code is active\n if (!referralCode.isActive) {\n return Response.json({ success: false, error: 'Referral code is not active' }, { status: 400 })\n }\n\n // Check expiration\n if (referralCode.expiresAt && new Date() > new Date(referralCode.expiresAt)) {\n return Response.json({ success: false, error: 'Referral code has expired' }, { status: 400 })\n }\n\n // Check usage limit\n if (referralCode.usageLimit && referralCode.usageCount >= referralCode.usageLimit) {\n return Response.json(\n { success: false, error: 'Referral code usage limit exceeded' },\n { status: 400 },\n )\n }\n\n // Get the referral program\n const programId =\n typeof referralCode.program === 'string' ? referralCode.program : referralCode.program?.id\n\n const program = await payload.findByID({\n collection: pluginConfig.collections.referralProgramsSlug,\n id: programId,\n })\n\n if (!program || !program.isActive) {\n return Response.json(\n { success: false, error: 'Referral program is not active' },\n { status: 400 },\n )\n }\n\n // Check if referral code already applied to this cart\n if (getRelationId(cart.appliedReferralCode) === referralCode.id) {\n return Response.json(\n { success: false, error: 'Referral code already applied to this cart' },\n { status: 400 },\n )\n }\n\n // Calculate commission and discount\n const cartTotal = cart.subtotal || cart.total || 0\n\n // Calculate based on commission rules\n const { partnerCommission, customerDiscount } = calculateCommissionAndDiscount({\n cartItems: cart.items || [],\n program,\n currencyCode: pluginConfig.defaultCurrency,\n })\n\n // Round commission and discount\n const roundedPartnerCommission = roundTo2(partnerCommission)\n const roundedCustomerDiscount = roundTo2(customerDiscount)\n const total = roundTo2(Math.max(0, cartTotal - roundedCustomerDiscount))\n\n // Apply referral to cart\n await payload.update({\n collection: 'carts',\n id: cartID,\n data: {\n appliedReferralCode: referralCode.id,\n partnerCommission: roundedPartnerCommission,\n customerDiscount: roundedCustomerDiscount,\n total,\n },\n })\n\n return Response.json({\n success: true,\n message: 'Referral code applied successfully',\n referralCode: {\n code: referralCode.code,\n },\n partnerCommission: roundedPartnerCommission,\n customerDiscount: roundedCustomerDiscount,\n currency: pluginConfig.defaultCurrency,\n debug: globalDebugLogs,\n })\n}\n\nexport const applyCouponEndpoint = ({ pluginConfig }: Args): Endpoint => ({\n path: pluginConfig.endpoints.applyCoupon,\n method: 'post',\n handler: applyCouponHandler({ pluginConfig }),\n})\n","import type { Endpoint, PayloadHandler } from 'payload'\n\nimport type { PartnerDashboardData, PartnerStats, SanitizedCouponPluginOptions } from '../types'\n\ntype Args = {\n pluginConfig: SanitizedCouponPluginOptions\n}\n\nexport const partnerStatsHandler =\n ({ pluginConfig }: Args): PayloadHandler =>\n async (req) => {\n const { payload, user } = req\n\n if (!user) {\n return Response.json({ success: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const typedUser = user as { id: string; role?: string | string[]; roles?: string[] }\n\n // Check if user is a partner\n const isPartner =\n typedUser.role === 'partner' ||\n (Array.isArray(typedUser.role) && typedUser.role?.includes('partner')) ||\n (Array.isArray(typedUser.roles) && typedUser.roles?.includes('partner'))\n\n const isAdmin =\n typedUser.role === 'admin' ||\n (Array.isArray(typedUser.role) && typedUser.role?.includes('admin')) ||\n (Array.isArray(typedUser.roles) && typedUser.roles?.includes('admin'))\n\n if (!isPartner && !isAdmin) {\n return Response.json({ success: false, error: 'Partner access required' }, { status: 403 })\n }\n\n try {\n // Get partner's referral codes\n const referralCodesQuery = await payload.find({\n collection: pluginConfig.collections.referralCodesSlug,\n where: {\n partner: { equals: typedUser.id },\n },\n limit: 100,\n })\n\n const referralCodes = referralCodesQuery.docs\n\n // Calculate stats\n let totalEarnings = 0\n let pendingEarnings = 0\n let paidEarnings = 0\n let totalReferrals = 0\n let successfulReferrals = 0\n\n const referralCodeData = referralCodes.map((code: any) => {\n totalEarnings += code.totalEarnings || 0\n pendingEarnings += code.pendingEarnings || 0\n paidEarnings += code.paidEarnings || 0\n totalReferrals += code.usageCount || 0\n successfulReferrals += code.successfulReferralsCount || 0\n\n return {\n id: code.id,\n code: code.code,\n usageCount: code.usageCount || 0,\n totalEarnings: code.totalEarnings || 0,\n isActive: code.isActive,\n }\n })\n\n // Calculate conversion rate\n const conversionRate = totalReferrals > 0 ? (successfulReferrals / totalReferrals) * 100 : 0\n\n // Get recent referrals (from orders with this partner's referral codes)\n const recentReferrals: PartnerStats['recentReferrals'] = []\n\n // Try to get orders with applied referral codes\n try {\n const ordersQuery = await payload.find({\n collection: 'orders',\n where: {\n appliedReferralCode: {\n in: referralCodes.map((c: any) => c.id),\n },\n },\n limit: 10,\n sort: '-createdAt',\n })\n\n for (const order of ordersQuery.docs as any[]) {\n recentReferrals.push({\n id: order.id,\n code: referralCodes.find((c: any) => c.id === order.appliedReferralCode)?.code || '',\n orderValue: order.total || 0,\n commission: order.partnerCommission || 0,\n date: order.createdAt,\n status: order.paymentStatus === 'paid' ? 'paid' : 'pending',\n })\n }\n } catch {\n // Orders collection might not exist or have different structure\n }\n\n // Calculate monthly earnings (last 6 months)\n const monthlyEarnings: PartnerStats['monthlyEarnings'] = []\n const now = new Date()\n\n for (let i = 5; i >= 0; i--) {\n const monthDate = new Date(now.getFullYear(), now.getMonth() - i, 1)\n const monthName = monthDate.toLocaleString('default', { month: 'short', year: 'numeric' })\n\n // This would need actual order data to calculate properly\n // For now, we'll provide placeholder structure\n monthlyEarnings.push({\n month: monthName,\n earnings: 0,\n referrals: 0,\n })\n }\n\n // Get the partner's active program\n let program: PartnerDashboardData['program'] = null\n\n if (referralCodes.length > 0) {\n const firstCode = referralCodes[0] as any\n if (firstCode.program) {\n try {\n const programData = await payload.findByID({\n collection: pluginConfig.collections.referralProgramsSlug,\n id: typeof firstCode.program === 'string' ? firstCode.program : firstCode.program.id,\n })\n\n if (programData) {\n const typedProgram = programData as any\n const firstRule = typedProgram.commissionRules?.[0]\n const partnerSplit =\n firstRule?.partnerSplit ??\n firstRule?.referrerSplit ??\n firstRule?.split?.partnerPercentage ??\n 0\n const customerSplit =\n firstRule?.customerSplit ??\n firstRule?.refereeSplit ??\n firstRule?.split?.customerPercentage ??\n 100 - partnerSplit\n program = {\n name: typedProgram.name,\n commissionRate: partnerSplit,\n customerDiscount: customerSplit,\n }\n }\n } catch {\n // Program might not exist\n }\n }\n }\n\n const stats: PartnerStats = {\n totalEarnings,\n pendingEarnings,\n paidEarnings,\n totalReferrals,\n successfulReferrals,\n conversionRate: Math.round(conversionRate * 100) / 100,\n recentReferrals,\n monthlyEarnings,\n }\n\n const dashboardData: PartnerDashboardData = {\n stats,\n referralCodes: referralCodeData,\n program,\n }\n\n return Response.json({\n success: true,\n data: dashboardData,\n currency: pluginConfig.defaultCurrency,\n })\n } catch (error) {\n console.error('Partner stats error:', error)\n return Response.json(\n { success: false, error: 'Failed to fetch partner stats' },\n { status: 500 },\n )\n }\n }\n\nexport const partnerStatsEndpoint = ({ pluginConfig }: Args): Endpoint => ({\n path: pluginConfig.endpoints.partnerStats,\n method: 'get',\n handler: partnerStatsHandler({ pluginConfig }),\n})\n","import type { Endpoint, PayloadHandler } from 'payload'\n\nimport type { SanitizedCouponPluginOptions } from '../types'\nimport { calculateCommissionAndDiscount } from '../utilities/calculateValues'\nimport { roundTo2 } from '../utilities/roundTo2'\n\ntype Args = {\n pluginConfig: SanitizedCouponPluginOptions\n}\n\nexport const validateCouponHandler =\n ({ pluginConfig }: Args): PayloadHandler =>\n async (req) => {\n const { payload } = req\n const { code: rawCode, cartValue, cartID, customerEmail } = req.data || {}\n const code = typeof rawCode === 'string' ? rawCode.trim() : rawCode\n\n if (!code) {\n return Response.json(\n {\n success: false,\n error: 'Code is required',\n },\n { status: 400 },\n )\n }\n\n try {\n if (pluginConfig.enableReferrals) {\n // Referral mode: validate referral codes\n return await validateReferralCode({ payload, code, cartID, pluginConfig })\n } else {\n // Coupon mode: validate coupons\n return await validateCouponCode({\n payload,\n code,\n cartValue,\n customerEmail,\n pluginConfig,\n })\n }\n } catch (error) {\n console.error('Code validation error:', error)\n return Response.json({ success: false, error: 'Internal server error' }, { status: 500 })\n }\n }\n\n// Validate coupon code (existing logic)\nasync function validateCouponCode({\n payload,\n code,\n cartValue,\n customerEmail,\n pluginConfig,\n}: {\n payload: any\n code: string\n cartValue?: number\n customerEmail?: string\n pluginConfig: SanitizedCouponPluginOptions\n}) {\n // Find the coupon\n // Find the coupon (Case insensitive check: Exact -> Lower -> Upper)\n let coupon = await payload.find({\n collection: pluginConfig.collections.couponsSlug,\n where: {\n code: { equals: code },\n },\n limit: 1,\n })\n\n if (!coupon.docs.length) {\n coupon = await payload.find({\n collection: pluginConfig.collections.couponsSlug,\n where: {\n code: { equals: code.toLowerCase() },\n },\n limit: 1,\n })\n }\n\n if (!coupon.docs.length) {\n coupon = await payload.find({\n collection: pluginConfig.collections.couponsSlug,\n where: {\n code: { equals: code.toUpperCase() },\n },\n limit: 1,\n })\n }\n\n if (!coupon.docs.length) {\n return Response.json({ success: false, error: 'Invalid coupon code' }, { status: 404 })\n }\n\n const couponData = coupon.docs[0]\n\n // Check if coupon is active\n const now = new Date()\n const activeFrom = couponData.activeFrom ? new Date(couponData.activeFrom) : null\n const activeUntil = couponData.activeUntil ? new Date(couponData.activeUntil) : null\n\n if (activeFrom && now < activeFrom) {\n return Response.json({ success: false, error: 'Coupon is not yet active' }, { status: 400 })\n }\n\n if (activeUntil && now > activeUntil) {\n return Response.json({ success: false, error: 'Coupon has expired' }, { status: 400 })\n }\n\n // Check usage limits\n if (couponData.usageLimit && couponData.usageCount >= couponData.usageLimit) {\n return Response.json({ success: false, error: 'Coupon usage limit exceeded' }, { status: 400 })\n }\n\n // Optional: per-customer limit (when customer identifier provided)\n if (\n couponData.perCustomerLimit != null &&\n couponData.perCustomerLimit > 0 &&\n typeof customerEmail === 'string' &&\n customerEmail.trim().length > 0\n ) {\n const email = customerEmail.trim()\n const { ordersSlug, orderCustomerEmailField, orderPaymentStatusField, orderPaidStatusValue } =\n pluginConfig.orderIntegration\n const ordersQuery = await payload.find({\n collection: ordersSlug,\n where: {\n and: [\n { appliedCoupon: { equals: couponData.id } },\n { [orderCustomerEmailField]: { equals: email } },\n { [orderPaymentStatusField]: { equals: orderPaidStatusValue } },\n ],\n },\n limit: 0,\n })\n if (ordersQuery.totalDocs >= couponData.perCustomerLimit) {\n return Response.json(\n { success: false, error: 'You have reached the maximum uses for this coupon.' },\n { status: 400 },\n )\n }\n }\n\n // Check minimum/maximum order value (top-level fields, same as apply endpoint)\n if (cartValue !== undefined) {\n const minOrderValue = couponData.minOrderValue\n const maxOrderValue = couponData.maxOrderValue\n\n if (minOrderValue && cartValue < minOrderValue) {\n return Response.json(\n {\n success: false,\n error: `Minimum order value of ${minOrderValue} ${pluginConfig.defaultCurrency} required`,\n },\n { status: 400 },\n )\n }\n\n if (maxOrderValue && cartValue > maxOrderValue) {\n return Response.json(\n {\n success: false,\n error: `Maximum order value of ${maxOrderValue} ${pluginConfig.defaultCurrency} exceeded`,\n },\n { status: 400 },\n )\n }\n }\n\n // Calculate discount preview (2 decimal standard)\n let discount = 0\n if (cartValue !== undefined) {\n if (couponData.type === 'percentage') {\n discount = roundTo2((cartValue * couponData.value) / 100)\n if (couponData.maxDiscountAmount != null && discount > couponData.maxDiscountAmount) {\n discount = roundTo2(couponData.maxDiscountAmount)\n }\n } else if (couponData.type === 'fixed') {\n discount = roundTo2(couponData.value)\n if (discount > cartValue) discount = roundTo2(cartValue)\n }\n }\n\n return Response.json({\n success: true,\n coupon: {\n code: couponData.code,\n type: couponData.type,\n value: couponData.value,\n description: couponData.description,\n },\n discount,\n currency: pluginConfig.defaultCurrency,\n })\n}\n\n// Validate referral code (new logic)\nasync function validateReferralCode({\n payload,\n code,\n cartID,\n pluginConfig,\n}: {\n payload: any\n code: string\n cartID?: string\n pluginConfig: SanitizedCouponPluginOptions\n}) {\n // Find the referral code\n // Find the referral code (Case insensitive check: Exact -> Lower -> Upper)\n let referral = await payload.find({\n collection: pluginConfig.collections.referralCodesSlug,\n where: {\n code: { equals: code },\n },\n limit: 1,\n })\n\n if (!referral.docs.length) {\n referral = await payload.find({\n collection: pluginConfig.collections.referralCodesSlug,\n where: {\n code: { equals: code.toLowerCase() },\n },\n limit: 1,\n })\n }\n\n if (!referral.docs.length) {\n referral = await payload.find({\n collection: pluginConfig.collections.referralCodesSlug,\n where: {\n code: { equals: code.toUpperCase() },\n },\n limit: 1,\n })\n }\n\n if (!referral.docs.length) {\n return Response.json({ success: false, error: 'Referral code not found' }, { status: 404 })\n }\n\n const referralData = referral.docs[0]\n\n // Check if referral code is active\n if (!referralData.isActive) {\n return Response.json({ success: false, error: 'Referral code is not active' }, { status: 400 })\n }\n\n // Check expiration\n if (referralData.expiresAt && new Date() > new Date(referralData.expiresAt)) {\n return Response.json({ success: false, error: 'Referral code has expired' }, { status: 400 })\n }\n\n // Check usage limit\n if (referralData.usageLimit && referralData.usageCount >= referralData.usageLimit) {\n return Response.json(\n { success: false, error: 'Referral code usage limit exceeded' },\n { status: 400 },\n )\n }\n\n // Get the referral program\n const programId =\n typeof referralData.program === 'string' ? referralData.program : referralData.program?.id\n\n const program = await payload.findByID({\n collection: pluginConfig.collections.referralProgramsSlug,\n id: programId,\n })\n\n if (!program || !program.isActive) {\n return Response.json(\n { success: false, error: 'Referral program is not active' },\n { status: 400 },\n )\n }\n\n const cart = cartID\n ? await payload.findByID({\n collection: 'carts',\n id: cartID,\n depth: 2,\n })\n : null\n\n const { partnerCommission, customerDiscount } = calculateCommissionAndDiscount({\n cartItems: cart?.items || [],\n program,\n currencyCode: pluginConfig.defaultCurrency,\n })\n\n const cartTotal = cart ? cart.subtotal || cart.total || 0 : 0\n const cappedCustomerDiscount =\n cartTotal > 0 ? Math.min(customerDiscount, cartTotal) : customerDiscount\n\n const roundedPartnerCommission = roundTo2(partnerCommission)\n const roundedCustomerDiscount = roundTo2(cappedCustomerDiscount)\n\n return Response.json({\n success: true,\n referralCode: {\n code: referralData.code,\n description: `Get ${roundedCustomerDiscount.toFixed(2)} discount with this referral code`,\n },\n partnerCommission: roundedPartnerCommission,\n customerDiscount: roundedCustomerDiscount,\n currency: pluginConfig.defaultCurrency,\n })\n}\n\nexport const validateCouponEndpoint = ({ pluginConfig }: Args): Endpoint => ({\n path: pluginConfig.endpoints.validateCoupon,\n method: 'post',\n handler: validateCouponHandler({ pluginConfig }),\n})\n","import type { CollectionBeforeChangeHook } from 'payload'\nimport type { SanitizedCouponPluginOptions } from '../types'\nimport {\n calculateCommissionAndDiscount,\n calculateCouponDiscount,\n} from '../utilities/calculateValues'\nimport { getCartItemUnitPrice } from '../utilities/pricing'\nimport { roundTo2 } from '../utilities/roundTo2'\n\nexport const recalculateCartHook =\n (pluginConfig: SanitizedCouponPluginOptions): CollectionBeforeChangeHook =>\n async ({ data, req, originalDoc }) => {\n // If no Payload, can't fetch relations\n if (!req.payload) return data\n\n // Determine effective state\n // data.items might be replacing or merging. In standard ecommerce, usually it replaces.\n // We need to calculate based on the *final* state of items.\n // If data.items is present, use it. If not, use originalDoc.items.\n const effectiveItems = data.items || originalDoc?.items || []\n\n console.log('[RecalculateCart] Hook triggered', {\n hasDataItems: !!data.items,\n dataItemsCount: data.items?.length,\n originalItemsCount: originalDoc?.items?.length,\n effectiveItemsCount: effectiveItems.length,\n })\n\n // If no items, ensure totals are 0\n if (!effectiveItems.length) {\n return {\n ...data,\n partnerCommission: 0,\n customerDiscount: 0,\n discountAmount: 0,\n total: 0,\n }\n }\n\n // Determine effective codes\n const appliedReferralCode =\n data.appliedReferralCode !== undefined\n ? data.appliedReferralCode\n : originalDoc?.appliedReferralCode\n const appliedCoupon =\n data.appliedCoupon !== undefined ? data.appliedCoupon : originalDoc?.appliedCoupon\n\n if (!appliedReferralCode && !appliedCoupon) {\n // No codes applied, just return data (cleanup done by other logic if needed, or we explicitly clear?)\n // Use case: user removed code. data.appliedCoupon would be null.\n // If we are just updating items, and code was removed, these should be 0.\n // But if code was removed, data.appliedCoupon is null.\n if (data.appliedReferralCode === null || data.appliedCoupon === null) {\n const fallbackSubtotal =\n typeof data.subtotal === 'number'\n ? data.subtotal\n : typeof originalDoc?.subtotal === 'number'\n ? originalDoc.subtotal\n : undefined\n\n return {\n ...data,\n partnerCommission: 0,\n customerDiscount: 0,\n discountAmount: 0,\n total: fallbackSubtotal,\n }\n }\n return data\n }\n\n // We need fully hydrated items to calculate prices\n // Optimized: Only fetch if we really need to recalculate.\n // Standard ecommerce recalculates 'total' and 'subtotal' in its hooks.\n // We need to know the *new* subtotal.\n // Since we don't know the order of hooks, we can't rely on data.subtotal being correct yet if we run before ecommerce.\n // SAFEST: We calculate our own subtotal based on current prices.\n\n const getRelationID = (value: unknown): number | string | undefined => {\n if (value === null || value === undefined) return undefined\n if (typeof value === 'object') return (value as { id?: number | string }).id\n if (typeof value === 'string' || typeof value === 'number') return value\n return undefined\n }\n\n const productIds = effectiveItems\n .map((item: any) => getRelationID(item.product))\n .filter((id: any): id is number | string => id !== undefined)\n\n if (!productIds.length) return data\n\n // Fetch products to get prices\n const productsQuery = await req.payload.find({\n collection: 'products', // Assumption: standard shops have products\n where: {\n id: { in: productIds },\n },\n limit: productIds.length,\n })\n\n const productsMap = new Map(productsQuery.docs.map((p) => [String(p.id), p]))\n\n let calculatedSubtotal = 0\n const enrichedItems = effectiveItems.map((item: any) => {\n const productId = getRelationID(item.product)\n const product: any = productId !== undefined ? productsMap.get(String(productId)) || {} : {}\n\n // We might need variants logic too, keeping it simple for now based on available info\n // Ideally we should replicate the price finding logic fully.\n // For now, let's map what we have.\n\n const itemPrice = getCartItemUnitPrice({\n item,\n product,\n variant: typeof item.variant === 'object' ? item.variant : undefined,\n currencyCode: pluginConfig.defaultCurrency,\n })\n\n calculatedSubtotal += itemPrice * (item.quantity ?? 1)\n\n console.log('[RecalculateCart] Item processed', {\n productId,\n quantity: item.quantity,\n priceUsed: itemPrice,\n currentSubtotal: calculatedSubtotal,\n })\n\n return {\n ...item,\n product, // Attach full product for rules\n price: itemPrice, // Normalized price\n }\n })\n\n // 1. Handle Referral\n if (appliedReferralCode && pluginConfig.enableReferrals) {\n const appliedReferralCodeID = getRelationID(appliedReferralCode)\n if (appliedReferralCodeID === undefined) {\n data.partnerCommission = 0\n data.customerDiscount = 0\n data.total = calculatedSubtotal\n return data\n }\n\n // Fetch referral code & program\n const referralQuery = await req.payload.find({\n collection: pluginConfig.collections.referralCodesSlug,\n where: {\n id: { equals: appliedReferralCodeID },\n },\n limit: 1,\n depth: 1,\n })\n\n if (referralQuery.docs.length) {\n const referralCode = referralQuery.docs[0]\n const programId =\n typeof referralCode.program === 'string' ? referralCode.program : referralCode.program?.id\n const program =\n typeof referralCode.program === 'object'\n ? referralCode.program\n : programId\n ? await req.payload.findByID({\n collection: pluginConfig.collections.referralProgramsSlug,\n id: programId,\n })\n : null\n\n if (program) {\n const { partnerCommission, customerDiscount } = calculateCommissionAndDiscount({\n cartItems: enrichedItems,\n program,\n currencyCode: pluginConfig.defaultCurrency,\n })\n\n const roundedCustomerDiscount = roundTo2(customerDiscount)\n data.partnerCommission = roundTo2(partnerCommission)\n data.customerDiscount = roundedCustomerDiscount\n\n // Update total\n // Use calculated subtotal or trust data.subtotal if present?\n // Best to use our calculated subtotal to be safely independent.\n data.total = Math.max(0, calculatedSubtotal - roundedCustomerDiscount)\n } else {\n // If referral code exists but program is unavailable, clear referral discount fields.\n data.partnerCommission = 0\n data.customerDiscount = 0\n data.total = calculatedSubtotal\n }\n }\n }\n\n // 2. Handle Coupon\n if (appliedCoupon && (!appliedReferralCode || pluginConfig.referralConfig.allowBothSystems)) {\n const appliedCouponID = getRelationID(appliedCoupon)\n if (appliedCouponID === undefined) {\n return data\n }\n\n const couponQuery = await req.payload.find({\n collection: pluginConfig.collections.couponsSlug,\n where: {\n id: { equals: appliedCouponID },\n },\n limit: 1,\n })\n\n if (couponQuery.docs.length) {\n const coupon = couponQuery.docs[0]\n const discountAmount = calculateCouponDiscount({\n coupon,\n cartTotal: calculatedSubtotal,\n })\n\n console.log('[RecalculateCart] Coupon Logic', {\n appliedCoupon,\n couponId: coupon.id,\n cartTotal: calculatedSubtotal,\n discountAmount,\n })\n\n data.discountAmount = discountAmount\n\n // If referral also applied, subtract from the already reduced total?\n // Usually discounts stack or are applied to subtotal.\n // Let's assume applied to subtotal for simplicity unless logic dictates otherwise.\n // But wait, referral discount reduces total. Coupon reduces total.\n // Standard approach: Total = Subtotal - ReferralDiscount - CouponDiscount\n\n const currentDiscount = data.customerDiscount || 0\n data.total = Math.max(0, calculatedSubtotal - currentDiscount - discountAmount)\n }\n }\n\n return data\n }\n","import type { CouponPluginOptions, SanitizedCouponPluginOptions } from '../types'\n\nexport const sanitizePluginConfig = ({\n pluginConfig,\n}: {\n pluginConfig: CouponPluginOptions\n}): SanitizedCouponPluginOptions => {\n // Apply defaults for each property when missing or invalid\n return {\n enabled: !(\n pluginConfig?.enabled === false ||\n (typeof pluginConfig?.enabled === 'string' && pluginConfig.enabled === 'false')\n ),\n enableReferrals:\n !!pluginConfig?.enableReferrals &&\n (typeof pluginConfig?.enableReferrals !== 'string' ||\n pluginConfig.enableReferrals !== 'false'),\n allowStackWithOtherCoupons:\n !!pluginConfig?.allowStackWithOtherCoupons &&\n (typeof pluginConfig?.allowStackWithOtherCoupons !== 'string' ||\n pluginConfig.allowStackWithOtherCoupons !== 'false'),\n defaultCurrency:\n typeof pluginConfig?.defaultCurrency === 'string' &&\n pluginConfig.defaultCurrency.length > 0 &&\n pluginConfig.defaultCurrency.length <= 3\n ? pluginConfig.defaultCurrency\n : 'USD',\n collections: {\n couponsSlug:\n typeof pluginConfig?.collections?.couponsSlug === 'string' &&\n pluginConfig.collections.couponsSlug.trim().length > 0 &&\n pluginConfig.collections.couponsSlug.length <= 100\n ? pluginConfig.collections.couponsSlug\n : 'coupons',\n referralProgramsSlug:\n typeof pluginConfig?.collections?.referralProgramsSlug === 'string' &&\n pluginConfig.collections.referralProgramsSlug.trim().length > 0 &&\n pluginConfig.collections.referralProgramsSlug.length <= 100\n ? pluginConfig.collections.referralProgramsSlug\n : 'referral-programs',\n referralCodesSlug:\n typeof pluginConfig?.collections?.referralCodesSlug === 'string' &&\n pluginConfig.collections.referralCodesSlug.trim().length > 0 &&\n pluginConfig.collections.referralCodesSlug.length <= 100\n ? pluginConfig.collections.referralCodesSlug\n : 'referral-codes',\n referralPartnersSlug:\n typeof pluginConfig?.collections?.referralPartnersSlug === 'string' &&\n pluginConfig.collections.referralPartnersSlug.trim().length > 0 &&\n pluginConfig.collections.referralPartnersSlug.length <= 100\n ? pluginConfig.collections.referralPartnersSlug\n : 'referral-partners',\n },\n endpoints: {\n applyCoupon:\n typeof pluginConfig?.endpoints?.applyCoupon === 'string' &&\n pluginConfig.endpoints.applyCoupon.trim().length > 0\n ? pluginConfig.endpoints.applyCoupon\n : '/coupons/apply',\n validateCoupon:\n typeof pluginConfig?.endpoints?.validateCoupon === 'string' &&\n pluginConfig.endpoints.validateCoupon.trim().length > 0\n ? pluginConfig.endpoints.validateCoupon\n : '/coupons/validate',\n partnerStats:\n typeof pluginConfig?.endpoints?.partnerStats === 'string' &&\n pluginConfig.endpoints.partnerStats.trim().length > 0\n ? pluginConfig.endpoints.partnerStats\n : '/referrals/partner-stats',\n recordOrderUsage:\n typeof pluginConfig?.endpoints?.recordOrderUsage === 'string' &&\n pluginConfig.endpoints.recordOrderUsage.trim().length > 0\n ? pluginConfig.endpoints.recordOrderUsage\n : '/coupons/record-order-usage',\n },\n autoIntegrate: pluginConfig?.autoIntegrate !== false,\n access: {\n canUseCoupons:\n typeof pluginConfig?.access?.canUseCoupons === 'function'\n ? pluginConfig.access.canUseCoupons\n : () => true,\n canUseReferrals:\n typeof pluginConfig?.access?.canUseReferrals === 'function'\n ? pluginConfig.access.canUseReferrals\n : () => false,\n isAdmin:\n typeof pluginConfig?.access?.isAdmin === 'function'\n ? pluginConfig.access.isAdmin\n : () => false,\n isPartner:\n typeof pluginConfig?.access?.isPartner === 'function'\n ? pluginConfig.access.isPartner\n : ({ req }) => {\n // Default: check if user has partner role\n const user = req?.user as { role?: string; roles?: string[] } | undefined\n if (!user) return false\n if (user.role === 'partner') return true\n if (Array.isArray(user.roles) && user.roles.includes('partner')) return true\n return false\n },\n },\n referralConfig: {\n allowBothSystems: pluginConfig?.referralConfig?.allowBothSystems ?? false,\n singleCodePerCart: pluginConfig?.referralConfig?.singleCodePerCart ?? true,\n defaultPartnerSplit: pluginConfig?.referralConfig?.defaultPartnerSplit ?? 70,\n defaultCustomerSplit: pluginConfig?.referralConfig?.defaultCustomerSplit ?? 30,\n },\n adminGroups: {\n couponsGroup: pluginConfig?.adminGroups?.couponsGroup ?? 'Coupons',\n referralsGroup: pluginConfig?.adminGroups?.referralsGroup ?? 'Referrals',\n },\n partnerDashboard: {\n enabled: pluginConfig?.partnerDashboard?.enabled ?? true,\n showEarningsSummary: pluginConfig?.partnerDashboard?.showEarningsSummary ?? true,\n showReferralPerformance: pluginConfig?.partnerDashboard?.showReferralPerformance ?? true,\n showRecentReferrals: pluginConfig?.partnerDashboard?.showRecentReferrals ?? true,\n showCommissionBreakdown: pluginConfig?.partnerDashboard?.showCommissionBreakdown ?? true,\n },\n orderIntegration: {\n ordersSlug:\n typeof pluginConfig?.orderIntegration?.ordersSlug === 'string' &&\n pluginConfig.orderIntegration.ordersSlug.trim().length > 0\n ? pluginConfig.orderIntegration.ordersSlug\n : 'orders',\n orderCustomerEmailField:\n typeof pluginConfig?.orderIntegration?.orderCustomerEmailField === 'string' &&\n pluginConfig.orderIntegration.orderCustomerEmailField.trim().length > 0\n ? pluginConfig.orderIntegration.orderCustomerEmailField\n : 'customerEmail',\n orderPaymentStatusField:\n typeof pluginConfig?.orderIntegration?.orderPaymentStatusField === 'string' &&\n pluginConfig.orderIntegration.orderPaymentStatusField.trim().length > 0\n ? pluginConfig.orderIntegration.orderPaymentStatusField\n : 'paymentStatus',\n orderPaidStatusValue:\n typeof pluginConfig?.orderIntegration?.orderPaidStatusValue === 'string'\n ? pluginConfig.orderIntegration.orderPaidStatusValue\n : 'paid',\n },\n }\n}\n","import type { Config } from 'payload'\n\nimport { createCouponsCollection } from './collections/createCouponsCollection'\nimport { createReferralCodesCollection } from './collections/createReferralCodesCollection'\nimport { createReferralProgramsCollection } from './collections/createReferralProgramsCollection'\nimport { applyCouponEndpoint } from './endpoints/applyCoupon'\nimport { partnerStatsEndpoint } from './endpoints/partnerStats'\nimport { validateCouponEndpoint } from './endpoints/validateCoupon'\nimport { recalculateCartHook } from './hooks/recalculateCart'\nimport { CouponPluginOptions } from './types'\nimport { sanitizePluginConfig } from './utilities/sanitizePluginConfig'\n\n// Fields to append to orders (referral mode)\n\nexport const payloadEcommerceCouponPlugin =\n (pluginOptions: CouponPluginOptions = {}) =>\n async (incomingConfig: Config): Promise<Config> => {\n const pluginConfig = sanitizePluginConfig({ pluginConfig: pluginOptions })\n\n if (!pluginConfig.enabled) return incomingConfig || {}\n\n // Handle null or undefined incoming config\n if (!incomingConfig) {\n incomingConfig = { collections: [], endpoints: [] } as any\n }\n if (!incomingConfig.collections) {\n incomingConfig.collections = []\n }\n\n const collectionsToAdd = []\n\n // When enableReferrals is true, both coupon and referral collections are created\n // The referralConfig.allowBothSystems determines if both can be used simultaneously\n if (pluginConfig.enableReferrals) {\n // Referral mode: create referral collections\n let referralProgramsCollection = createReferralProgramsCollection(pluginConfig)\n let referralCodesCollection = createReferralCodesCollection(pluginConfig)\n\n // Apply collection overrides if provided\n if (pluginOptions.collections?.referralProgramsCollectionOverride) {\n referralProgramsCollection =\n await pluginOptions.collections.referralProgramsCollectionOverride({\n defaultCollection: referralProgramsCollection,\n })\n }\n\n if (pluginOptions.collections?.referralCodesCollectionOverride) {\n referralCodesCollection = await pluginOptions.collections.referralCodesCollectionOverride({\n defaultCollection: referralCodesCollection,\n })\n }\n\n collectionsToAdd.push(referralProgramsCollection, referralCodesCollection)\n\n // If allowBothSystems is true, also create coupon collection\n if (pluginConfig.referralConfig.allowBothSystems) {\n let couponsCollection = createCouponsCollection(pluginConfig)\n if (pluginOptions.collections?.couponsCollectionOverride) {\n couponsCollection = await pluginOptions.collections.couponsCollectionOverride({\n defaultCollection: couponsCollection,\n })\n }\n collectionsToAdd.push(couponsCollection)\n }\n } else {\n // Coupon mode: create coupon collections only\n let couponsCollection = createCouponsCollection(pluginConfig)\n if (pluginOptions.collections?.couponsCollectionOverride) {\n couponsCollection = await pluginOptions.collections.couponsCollectionOverride({\n defaultCollection: couponsCollection,\n })\n }\n collectionsToAdd.push(couponsCollection)\n }\n\n // Add collections to config (avoid duplicates)\n const existingSlugs = new Set(incomingConfig.collections.map((c: any) => c.slug))\n const collectionsToAddFiltered = collectionsToAdd.filter((c: any) => !existingSlugs.has(c.slug))\n incomingConfig.collections = [...incomingConfig.collections, ...collectionsToAddFiltered]\n\n // Add endpoints\n if (!incomingConfig.endpoints) {\n incomingConfig.endpoints = []\n }\n\n incomingConfig.endpoints = [\n ...incomingConfig.endpoints,\n validateCouponEndpoint({ pluginConfig }),\n applyCouponEndpoint({ pluginConfig }),\n ]\n\n // Add partner stats endpoint if referrals are enabled\n if (pluginConfig.enableReferrals) {\n incomingConfig.endpoints.push(partnerStatsEndpoint({ pluginConfig }))\n }\n\n // Safe autoIntegrate implementation — ensure referral collection exists before injecting relationships\n if (pluginConfig.autoIntegrate) {\n // Ensure collections array exists\n incomingConfig.collections = incomingConfig.collections || []\n\n // After we already appended the plugin collections above, recompute slug set\n const allSlugs = new Set<string>(incomingConfig.collections.map((c: any) => c.slug))\n\n // Helper that adds a field group to an existing collection (by slug) if not already present\n const addFieldsToCollection = (targetSlug: string, newFields: any[]) => {\n const idx = incomingConfig.collections!.findIndex((c: any) => c.slug === targetSlug)\n if (idx === -1) return\n const collection = incomingConfig.collections![idx]\n collection.fields = collection.fields || []\n\n // Avoid adding duplicate fields (by name)\n const existingFieldNames = new Set(collection.fields.map((f: any) => f.name))\n for (const f of newFields) {\n if (!existingFieldNames.has(f.name)) {\n collection.fields.push(f)\n }\n }\n\n // Replace the collection entry (mutation is OK here)\n incomingConfig.collections![idx] = collection\n }\n\n // Only inject referral integration if the referral collection slug is actually present\n if (\n pluginConfig.enableReferrals &&\n allSlugs.has(pluginConfig.collections.referralCodesSlug)\n ) {\n // Fields to append to carts (referral mode)\n const cartReferralFields = [\n {\n name: 'appliedReferralCode',\n type: 'relationship',\n relationTo: pluginConfig.collections.referralCodesSlug,\n admin: { description: 'Referral code applied to this cart' },\n },\n {\n name: 'partnerCommission',\n type: 'number',\n admin: { description: 'Partner commission amount for this cart' },\n },\n {\n name: 'customerDiscount',\n type: 'number',\n admin: { description: 'Customer discount amount for this cart' },\n },\n ]\n\n // If both systems allowed, also add coupon field\n if (\n pluginConfig.referralConfig.allowBothSystems &&\n allSlugs.has(pluginConfig.collections.couponsSlug)\n ) {\n cartReferralFields.push({\n name: 'appliedCoupon',\n type: 'relationship',\n relationTo: pluginConfig.collections.couponsSlug,\n admin: { description: 'Coupon applied to this cart' },\n })\n cartReferralFields.push({\n name: 'discountAmount',\n type: 'number',\n admin: { description: 'Discount amount from coupon' },\n })\n }\n\n addFieldsToCollection('carts', cartReferralFields)\n\n // Fields to append to orders (referral mode)\n const orderReferralFields = [\n {\n name: 'appliedReferralCode',\n type: 'relationship',\n relationTo: pluginConfig.collections.referralCodesSlug,\n admin: { description: 'Referral code applied to this order', readOnly: true },\n },\n {\n name: 'partnerCommission',\n type: 'number',\n admin: { description: 'Partner commission amount for this order', readOnly: true },\n },\n {\n name: 'customerDiscount',\n type: 'number',\n admin: { description: 'Customer discount amount for this order', readOnly: true },\n },\n ]\n\n // If both systems allowed, also add coupon field to orders\n if (\n pluginConfig.referralConfig.allowBothSystems &&\n allSlugs.has(pluginConfig.collections.couponsSlug)\n ) {\n orderReferralFields.push({\n name: 'appliedCoupon',\n type: 'relationship',\n relationTo: pluginConfig.collections.couponsSlug,\n admin: { description: 'Coupon applied to this order', readOnly: true },\n })\n orderReferralFields.push({\n name: 'discountAmount',\n type: 'number',\n admin: { description: 'Discount amount from coupon', readOnly: true },\n })\n }\n\n addFieldsToCollection('orders', orderReferralFields)\n } else if (\n !pluginConfig.enableReferrals &&\n allSlugs.has(pluginConfig.collections.couponsSlug)\n ) {\n // coupon mode — similar safe injection for appliedCoupons\n const cartCouponFields = [\n {\n name: 'appliedCoupon',\n type: 'relationship',\n relationTo: pluginConfig.collections.couponsSlug,\n admin: { description: 'Coupon applied to this cart' },\n },\n {\n name: 'discountAmount',\n type: 'number',\n admin: { description: 'Discount amount from coupon' },\n },\n ]\n addFieldsToCollection('carts', cartCouponFields)\n\n const orderCouponFields = [\n {\n name: 'appliedCoupon',\n type: 'relationship',\n relationTo: pluginConfig.collections.couponsSlug,\n admin: { description: 'Coupon applied to this order', readOnly: true },\n },\n {\n name: 'discountAmount',\n type: 'number',\n admin: { description: 'Discount amount from coupon', readOnly: true },\n },\n ]\n addFieldsToCollection('orders', orderCouponFields)\n }\n }\n\n // Add Recalculate Cart Hook\n const cartIndex = incomingConfig.collections!.findIndex((c: any) => c.slug === 'carts')\n if (cartIndex > -1) {\n const collection = incomingConfig.collections![cartIndex]\n collection.hooks = {\n ...collection.hooks,\n beforeChange: [\n ...(collection.hooks?.beforeChange || []),\n recalculateCartHook(pluginConfig),\n ],\n }\n incomingConfig.collections![cartIndex] = collection\n }\n\n return incomingConfig\n }\n","import type { ApplyCouponHook, ApplyCouponResponse, PartnerDashboardData } from '../types'\n\n/**\n * Apply a coupon code to a cart\n * @param options - Coupon code, cart ID, and customer email\n * @returns Response with success status, discount amount, and coupon details\n */\nexport async function useCouponCode(options: ApplyCouponHook): Promise<ApplyCouponResponse> {\n const { code, cartID, customerEmail } = options\n\n if (!code) {\n return {\n success: false,\n message: 'Coupon code is required',\n error: 'Code is missing',\n }\n }\n\n try {\n const response = await fetch('/api/coupons/apply', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ code, cartID, customerEmail }),\n })\n\n const data = (await response.json()) as Record<string, unknown>\n\n if (!response.ok) {\n return {\n success: false,\n message: (data.error as string) || 'Failed to apply coupon',\n error: data.error as string,\n }\n }\n\n const couponData = data.coupon as Record<string, unknown> | undefined\n const referralData = data.referralCode as Record<string, unknown> | undefined\n\n return {\n success: data.success as boolean,\n message: data.message as string,\n discount: (data.discount as number) || (data.customerDiscount as number),\n partnerCommission: data.partnerCommission as number,\n customerDiscount: data.customerDiscount as number,\n coupon: couponData\n ? {\n code: (couponData.code as string) || '',\n type: (couponData.type as 'percentage' | 'fixed') || 'percentage',\n value: (couponData.value as number) || 0,\n }\n : undefined,\n referralCode: referralData\n ? {\n code: (referralData.code as string) || '',\n }\n : undefined,\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Network error'\n return { success: false, message, error: message }\n }\n}\n\n/**\n * Validate a coupon code without applying it\n * @param code - Coupon code to validate\n * @param cartValue - Optional cart value in smallest currency unit\n * @returns Response with validation result and coupon details\n */\nexport async function validateCouponCode(\n code: string,\n cartValue?: number,\n cartID?: string,\n): Promise<ApplyCouponResponse> {\n if (!code) {\n return {\n success: false,\n message: 'Code required',\n error: 'Code missing',\n }\n }\n\n try {\n const response = await fetch('/api/coupons/validate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ code, cartValue, cartID }),\n })\n\n const data = (await response.json()) as Record<string, unknown>\n\n if (!response.ok) {\n return {\n success: false,\n message: (data.error as string) || 'Invalid coupon',\n error: data.error as string,\n }\n }\n\n const couponData = data.coupon as Record<string, unknown> | undefined\n const referralData = data.referralCode as Record<string, unknown> | undefined\n\n return {\n success: data.success as boolean,\n message: data.message as string,\n coupon: couponData\n ? {\n code: (couponData.code as string) || '',\n type: (couponData.type as 'percentage' | 'fixed') || 'percentage',\n value: (couponData.value as number) || 0,\n }\n : undefined,\n referralCode: referralData\n ? {\n code: (referralData.code as string) || '',\n }\n : undefined,\n discount: data.discount as number,\n partnerCommission: data.partnerCommission as number,\n customerDiscount: data.customerDiscount as number,\n currency: data.currency as string,\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Network error'\n return { success: false, message, error: message }\n }\n}\n\nexport type PartnerStatsResponse = {\n success: boolean\n data?: PartnerDashboardData\n currency?: string\n error?: string\n}\n\n/**\n * Fetch partner dashboard statistics\n * @param apiEndpoint - Optional custom API endpoint (default: /api/referrals/partner-stats)\n * @returns Response with partner stats, referral codes, and program info\n */\nexport async function usePartnerStats(\n apiEndpoint: string = '/api/referrals/partner-stats',\n): Promise<PartnerStatsResponse> {\n try {\n const response = await fetch(apiEndpoint, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n })\n\n const data = (await response.json()) as Record<string, unknown>\n\n if (!response.ok) {\n return {\n success: false,\n error: (data.error as string) || 'Failed to fetch partner stats',\n }\n }\n\n return {\n success: data.success as boolean,\n data: data.data as PartnerDashboardData,\n currency: data.currency as string,\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Network error'\n return { success: false, error: message }\n }\n}\n","import { roundTo2 } from './roundTo2'\n\nexport type CartLike = {\n subtotal?: number\n total?: number\n discountAmount?: number\n customerDiscount?: number\n}\n\n/**\n * Computes the cart total after applying plugin discounts.\n * Use this in your host app's cart beforeChange (or wherever you compute total)\n * so the amount always reflects coupon/referral discounts and is not overwritten incorrectly.\n *\n * Formula: subtotal - discountAmount - customerDiscount (each defaulting to 0).\n */\nexport function getCartTotalWithDiscounts(cart: CartLike): number {\n const subtotal = cart.subtotal ?? cart.total ?? 0\n const discountAmount = cart.discountAmount ?? 0\n const customerDiscount = cart.customerDiscount ?? 0\n return roundTo2(Math.max(0, subtotal - discountAmount - customerDiscount))\n}\n","import type { Payload } from 'payload'\n\nimport type { SanitizedCouponPluginOptions } from '../types'\n\nexport type OrderWithCouponFields = {\n id?: string\n appliedCoupon?: string | { id: string }\n appliedReferralCode?: string | { id: string }\n partnerCommission?: number\n customerDiscount?: number\n discountAmount?: number\n}\n\nexport type RecordCouponUsageResult = {\n recordedCoupon: boolean\n recordedReferral: boolean\n}\n\n/**\n * Record coupon and referral usage when an order is placed successfully.\n * Call this once when the order is created/paid (e.g. from Orders collection afterChange hook).\n *\n * - Coupon: increments the coupon's usageCount.\n * - Referral: increments the referral code's usageCount and successfulReferralsCount,\n * and adds order.partnerCommission to totalEarnings and pendingEarnings (partner gets commission;\n * referee discount is already on the order).\n */\nexport async function recordCouponUsageForOrder(\n payload: Payload,\n order: OrderWithCouponFields,\n pluginConfig: SanitizedCouponPluginOptions,\n): Promise<RecordCouponUsageResult> {\n const result: RecordCouponUsageResult = { recordedCoupon: false, recordedReferral: false }\n\n const couponId =\n order.appliedCoupon == null\n ? null\n : typeof order.appliedCoupon === 'string'\n ? order.appliedCoupon\n : order.appliedCoupon?.id\n\n const referralCodeId =\n order.appliedReferralCode == null\n ? null\n : typeof order.appliedReferralCode === 'string'\n ? order.appliedReferralCode\n : order.appliedReferralCode?.id\n\n if (couponId) {\n const coupon = await payload.findByID({\n collection: pluginConfig.collections.couponsSlug,\n id: couponId,\n })\n if (coupon) {\n await payload.update({\n collection: pluginConfig.collections.couponsSlug,\n id: couponId,\n data: {\n usageCount: (coupon.usageCount ?? 0) + 1,\n },\n })\n result.recordedCoupon = true\n }\n }\n\n if (referralCodeId) {\n const referralCode = await payload.findByID({\n collection: pluginConfig.collections.referralCodesSlug,\n id: referralCodeId,\n })\n if (referralCode) {\n const commission = Number(order.partnerCommission) || 0\n const currentTotal = Number((referralCode as any).totalEarnings) || 0\n const currentPending = Number((referralCode as any).pendingEarnings) || 0\n const currentUsageCount = Number((referralCode as any).usageCount) || 0\n const currentSuccessful = Number((referralCode as any).successfulReferralsCount) || 0\n\n await payload.update({\n collection: pluginConfig.collections.referralCodesSlug,\n id: referralCodeId,\n data: {\n usageCount: currentUsageCount + 1,\n successfulReferralsCount: currentSuccessful + 1,\n totalEarnings: currentTotal + commission,\n pendingEarnings: currentPending + commission,\n },\n })\n result.recordedReferral = true\n }\n }\n\n return result\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["validateCouponCode"],"sources":["../src/collections/createCouponsCollection.ts","../src/utilities/userRoles.ts","../src/collections/createReferralCodesCollection.ts","../src/collections/createReferralProgramsCollection.ts","../src/utilities/roundTo2.ts","../src/utilities/pricing.ts","../src/utilities/calculateValues.ts","../src/endpoints/applyCoupon.ts","../src/endpoints/partnerStats.ts","../src/endpoints/validateCoupon.ts","../src/hooks/recalculateCart.ts","../src/utilities/sanitizePluginConfig.ts","../src/plugin.ts","../src/client/hooks.ts","../src/utilities/getCartTotalWithDiscounts.ts","../src/utilities/recordCouponUsageForOrder.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nimport type { SanitizedCouponPluginOptions } from '../types'\n\nexport const createCouponsCollection = (\n pluginConfig: SanitizedCouponPluginOptions,\n): CollectionConfig => {\n const { collections, access, defaultCurrency, adminGroups } = pluginConfig\n\n return {\n slug: collections.couponsSlug,\n admin: {\n useAsTitle: 'code',\n defaultColumns: ['code', 'type', 'value', 'activeFrom', 'activeUntil'],\n group: adminGroups.couponsGroup,\n },\n access: {\n read: access.canUseCoupons || (() => true),\n create: access.isAdmin || (() => false),\n update: access.isAdmin || (() => false),\n delete: access.isAdmin || (() => false),\n },\n fields: [\n {\n name: 'code',\n type: 'text',\n required: true,\n unique: true,\n admin: {\n description: 'The coupon code that customers will enter',\n },\n },\n {\n name: 'description',\n type: 'text',\n admin: {\n description: 'Optional description for admin reference',\n },\n },\n {\n name: 'type',\n type: 'select',\n required: true,\n options: [\n { label: 'Percentage', value: 'percentage' },\n { label: 'Fixed Amount', value: 'fixed' },\n ],\n defaultValue: 'percentage',\n admin: {\n description: 'Whether this is a percentage or fixed amount discount',\n },\n },\n {\n name: 'value',\n type: 'number',\n required: true,\n admin: {\n description: `If percentage, 10 = 10%. If fixed, interpreted in ${defaultCurrency} (smallest currency units)`,\n step: 0.01,\n },\n },\n {\n name: 'maxDiscountAmount',\n type: 'number',\n admin: {\n description: `Maximum discount amount in ${defaultCurrency} (smallest currency unit). Leave empty for no cap.`,\n },\n },\n {\n name: 'usageLimit',\n type: 'number',\n admin: {\n description:\n 'Total times this coupon can be used across all customers. Empty = unlimited.',\n },\n },\n {\n name: 'perCustomerLimit',\n type: 'number',\n admin: {\n description: 'Times a single customer can use this coupon. Empty = unlimited.',\n },\n },\n {\n name: 'activeFrom',\n type: 'date',\n admin: {\n description:\n 'Coupon becomes active from this date. Leave empty for immediate activation.',\n },\n },\n {\n name: 'activeUntil',\n type: 'date',\n admin: {\n description: 'Coupon expires after this date. Leave empty for no expiration.',\n },\n },\n {\n name: 'minOrderValue',\n type: 'number',\n admin: {\n description: `Minimum order value required in ${defaultCurrency} (smallest currency units)`,\n },\n },\n {\n name: 'maxOrderValue',\n type: 'number',\n admin: {\n description: `Maximum order value allowed in ${defaultCurrency} (smallest currency units)`,\n },\n },\n {\n name: 'usageCount',\n type: 'number',\n defaultValue: 0,\n admin: {\n description: 'How many times this coupon has been used',\n readOnly: true,\n },\n },\n {\n name: 'createdBy',\n type: 'relationship',\n relationTo: 'users',\n admin: {\n readOnly: true,\n position: 'sidebar',\n },\n },\n ],\n hooks: {\n beforeChange: [\n ({ operation, req, data }) => {\n if (operation === 'create' && req.user) {\n data.createdBy = req.user.id\n }\n return data\n },\n ],\n },\n timestamps: true,\n }\n}\n","import type { SanitizedCouponPluginOptions } from '../types'\nimport type { Where } from 'payload'\n\ntype RoleConfig = SanitizedCouponPluginOptions['roleConfig']\n\nfunction readByPath(input: unknown, path: string): unknown {\n if (!input || typeof input !== 'object' || !path) return undefined\n return path.split('.').reduce<unknown>((acc, key) => {\n if (!acc || typeof acc !== 'object') return undefined\n return (acc as Record<string, unknown>)[key]\n }, input)\n}\n\nfunction toRoleArray(value: unknown): string[] {\n if (typeof value === 'string' && value.trim().length > 0) return [value]\n if (Array.isArray(value)) {\n return value\n .filter((item): item is string => typeof item === 'string')\n .map((item) => item.trim())\n .filter(Boolean)\n }\n return []\n}\n\nconst normalizeRoleValue = (value: string): string => value.trim().toLowerCase()\n\nexport const resolveUserRoles = ({\n user,\n roleConfig,\n}: {\n user: unknown\n roleConfig: RoleConfig\n}): string[] => {\n if (!user) return []\n\n if (typeof roleConfig.customRoleResolver === 'function') {\n const custom = roleConfig.customRoleResolver(user)\n return Array.isArray(custom) ? custom.map(normalizeRoleValue).filter(Boolean) : []\n }\n\n const roles = roleConfig.roleFieldPaths.flatMap((path) => toRoleArray(readByPath(user, path)))\n return [...new Set(roles.map(normalizeRoleValue).filter(Boolean))]\n}\n\nexport const userHasAnyRole = ({\n user,\n roleConfig,\n targetRoles,\n}: {\n user: unknown\n roleConfig: RoleConfig\n targetRoles: string[]\n}): boolean => {\n const userRoles = new Set(resolveUserRoles({ user, roleConfig }))\n for (const target of targetRoles) {\n if (userRoles.has(normalizeRoleValue(target))) return true\n }\n return false\n}\n\nexport const isPartnerUser = ({\n user,\n roleConfig,\n}: {\n user: unknown\n roleConfig: RoleConfig\n}): boolean =>\n userHasAnyRole({\n user,\n roleConfig,\n targetRoles: roleConfig.partnerRoleValues,\n })\n\nexport const isAdminUser = ({\n user,\n roleConfig,\n}: {\n user: unknown\n roleConfig: RoleConfig\n}): boolean =>\n userHasAnyRole({\n user,\n roleConfig,\n targetRoles: roleConfig.adminRoleValues,\n })\n\nexport const buildPartnerUserFilterWhere = ({\n roleConfig,\n}: {\n roleConfig: RoleConfig\n}): Where | true => {\n if (!roleConfig.roleFieldPaths.length || !roleConfig.partnerRoleValues.length) return true\n\n const conditions = roleConfig.roleFieldPaths.map((fieldPath) => ({\n [fieldPath]: { in: roleConfig.partnerRoleValues },\n }))\n\n if (conditions.length === 1) return conditions[0] as Where\n return { or: conditions } as Where\n}\n","import type { CollectionConfig } from 'payload'\n\nimport type { SanitizedCouponPluginOptions } from '../types'\nimport { buildPartnerUserFilterWhere, isAdminUser, isPartnerUser } from '../utilities/userRoles'\n\nexport const createReferralCodesCollection = (\n pluginConfig: SanitizedCouponPluginOptions,\n): CollectionConfig => {\n const { collections, access, adminGroups, defaultCurrency, roleConfig } = pluginConfig\n\n return {\n slug: collections.referralCodesSlug,\n admin: {\n useAsTitle: 'code',\n defaultColumns: ['code', 'partner', 'program', 'usageCount', 'isActive'],\n group: adminGroups.referralsGroup,\n },\n access: {\n read: ({ req }) => {\n // Partners can read their own codes, admins can read all\n const user = req?.user as { id?: string } | undefined\n if (!user) return false\n\n // Admin access\n if (isAdminUser({ user, roleConfig }) || access.isAdmin?.({ req } as any)) {\n return true\n }\n\n // Partner access - only their own codes\n if (isPartnerUser({ user, roleConfig }) || access.isPartner?.({ req } as any)) {\n return {\n partner: {\n equals: user.id,\n },\n }\n }\n\n // Default access from config\n return access.canUseReferrals ? access.canUseReferrals({ req } as any) : false\n },\n create: ({ req }) => {\n // Partners can create their own codes, admins can create any\n const user = req?.user\n if (!user) return false\n\n if (isAdminUser({ user, roleConfig }) || access.isAdmin?.({ req } as any)) {\n return true\n }\n\n if (isPartnerUser({ user, roleConfig }) || access.isPartner?.({ req } as any)) {\n return true\n }\n\n return access.isAdmin ? access.isAdmin({ req } as any) : false\n },\n update: access.isAdmin || (() => false),\n delete: access.isAdmin || (() => false),\n },\n fields: [\n {\n name: 'code',\n type: 'text',\n required: true,\n unique: true,\n admin: {\n description: 'The referral code that customers will enter',\n },\n },\n {\n name: 'program',\n type: 'relationship',\n relationTo: collections.referralProgramsSlug,\n required: true,\n admin: {\n description: 'The referral program this code belongs to',\n },\n },\n {\n name: 'partner',\n type: 'relationship',\n relationTo: 'users',\n required: true,\n filterOptions: ({ req, user }) => {\n const currentUser = user || req?.user\n if (isAdminUser({ user: currentUser, roleConfig }) || access.isAdmin?.({ req } as any)) {\n return true\n }\n return buildPartnerUserFilterWhere({ roleConfig })\n },\n admin: {\n description: 'The partner who owns this referral code',\n },\n },\n {\n name: 'isActive',\n type: 'checkbox',\n defaultValue: true,\n admin: {\n description: 'Whether this referral code is currently active',\n },\n },\n {\n name: 'usageCount',\n type: 'number',\n defaultValue: 0,\n admin: {\n description: 'How many times this referral code has been used',\n readOnly: true,\n },\n },\n {\n name: 'usageLimit',\n type: 'number',\n admin: {\n description: 'Maximum times this code can be used. Empty = unlimited.',\n },\n },\n {\n name: 'expiresAt',\n type: 'date',\n admin: {\n description: 'When this referral code expires',\n },\n },\n {\n name: 'successfulReferralsCount',\n type: 'number',\n defaultValue: 0,\n admin: {\n description: 'Total count of successful referrals using this code',\n readOnly: true,\n },\n },\n {\n name: 'totalEarnings',\n type: 'number',\n defaultValue: 0,\n admin: {\n description: `Total earnings generated by this code in ${defaultCurrency}`,\n readOnly: true,\n },\n },\n {\n name: 'pendingEarnings',\n type: 'number',\n defaultValue: 0,\n admin: {\n description: `Pending earnings awaiting payout in ${defaultCurrency}`,\n readOnly: true,\n },\n },\n {\n name: 'paidEarnings',\n type: 'number',\n defaultValue: 0,\n admin: {\n description: `Total earnings paid out in ${defaultCurrency}`,\n readOnly: true,\n },\n },\n {\n name: 'metadata',\n type: 'json',\n admin: {\n description: 'Additional metadata for the referral code',\n position: 'sidebar',\n },\n },\n ],\n hooks: {\n beforeChange: [\n ({ operation, req, data }) => {\n // Auto-generate code if not provided\n if (operation === 'create' && !data.code && data.partner) {\n const timestamp = Date.now().toString(36)\n const random = Math.random().toString(36).substring(2, 8)\n data.code = `REF-${timestamp}-${random}`.toUpperCase()\n }\n\n // Auto-assign partner to current user if partner\n if (operation === 'create' && req.user) {\n const user = req.user as { id?: string }\n if (isPartnerUser({ user, roleConfig })) {\n data.partner = user.id\n }\n }\n\n return data\n },\n ],\n },\n timestamps: true,\n }\n}\n","import type { CollectionConfig } from 'payload'\n\nimport type { SanitizedCouponPluginOptions } from '../types'\n\ntype RuleData = {\n appliesTo?: 'all' | 'products' | 'segments' | 'categories'\n products?: unknown[]\n categories?: unknown[]\n tags?: unknown[]\n totalCommission?: { type?: 'fixed' | 'percentage'; value?: number; maxAmount?: number }\n partnerSplit?: number\n customerSplit?: number\n minOrderAmount?: number\n}\n\nfunction toNumber(value: unknown): number | null {\n return typeof value === 'number' && Number.isFinite(value) ? value : null\n}\n\nconst deriveCustomerSplit = (partnerSplit: unknown): number => {\n const partner = toNumber(partnerSplit)\n if (partner == null) return 0\n if (partner < 0) return 100\n if (partner > 100) return 0\n return 100 - partner\n}\n\nexport const createReferralProgramsCollection = (\n pluginConfig: SanitizedCouponPluginOptions,\n): CollectionConfig => {\n const { collections, access, defaultCurrency, adminGroups, referralConfig } = pluginConfig\n const allowedTotalCommissionTypes = referralConfig.allowedTotalCommissionTypes\n\n const beforeChange: NonNullable<CollectionConfig['hooks']>['beforeChange'] = [\n ({ data }: { data: Record<string, unknown> }) => {\n if (\n !data.commissionRules ||\n !Array.isArray(data.commissionRules) ||\n data.commissionRules.length === 0\n ) {\n throw new Error('At least one commission rule is required')\n }\n\n data.commissionRules = data.commissionRules.map(\n (rule: Record<string, unknown>, index: number) => {\n const r = rule as RuleData\n\n if (!r.totalCommission) {\n throw new Error(`Commission rule ${index + 1}: Total Commission is required`)\n }\n\n if (\n !r.totalCommission.type ||\n !allowedTotalCommissionTypes.includes(r.totalCommission.type)\n ) {\n throw new Error(\n `Commission rule ${index + 1}: Total Commission type must be one of ${allowedTotalCommissionTypes.join(', ')}`,\n )\n }\n\n const totalValue = toNumber(r.totalCommission.value)\n if (totalValue == null || totalValue < 0) {\n throw new Error(\n `Commission rule ${index + 1}: Total Commission value must be a non-negative number`,\n )\n }\n if (r.totalCommission.type === 'percentage' && totalValue > 100) {\n throw new Error(\n `Commission rule ${index + 1}: Percentage Total Commission cannot exceed 100`,\n )\n }\n\n const maxAmount = toNumber(r.totalCommission.maxAmount)\n if (maxAmount != null && maxAmount < 0) {\n throw new Error(\n `Commission rule ${index + 1}: Max Amount must be a non-negative number`,\n )\n }\n\n const appliesTo = r.appliesTo ?? 'all'\n if (appliesTo === 'products' && (!r.products || r.products.length === 0)) {\n throw new Error(`Commission rule ${index + 1}: At least one product is required`)\n }\n\n if (\n (appliesTo === 'segments' || appliesTo === 'categories') &&\n (!r.categories || r.categories.length === 0) &&\n (!r.tags || r.tags.length === 0)\n ) {\n throw new Error(\n `Commission rule ${index + 1}: At least one category or tag is required`,\n )\n }\n\n const partnerSplit = toNumber(r.partnerSplit)\n if (partnerSplit == null || partnerSplit < 0 || partnerSplit > 100) {\n throw new Error(`Commission rule ${index + 1}: Partner Split must be between 0 and 100`)\n }\n\n const customerSplit = 100 - partnerSplit\n const minOrderAmount = toNumber(\n (r as RuleData & { minOrderAmount?: number }).minOrderAmount,\n )\n if (minOrderAmount != null && minOrderAmount < 0) {\n throw new Error(\n `Commission rule ${index + 1}: Minimum Order Amount must be a non-negative number`,\n )\n }\n\n return {\n ...rule,\n appliesTo: appliesTo === 'categories' ? 'segments' : appliesTo,\n totalCommission: {\n type: r.totalCommission.type,\n value: totalValue,\n maxAmount: maxAmount ?? null,\n },\n partnerSplit,\n customerSplit,\n minOrderAmount: minOrderAmount ?? null,\n }\n },\n )\n\n return data\n },\n ]\n\n return {\n slug: collections.referralProgramsSlug,\n admin: {\n useAsTitle: 'name',\n defaultColumns: ['name', 'commissionRules', 'isActive'],\n group: adminGroups.referralsGroup,\n },\n access: {\n read: access.canUseReferrals || (() => true),\n create: access.isAdmin || (() => false),\n update: access.isAdmin || (() => false),\n delete: access.isAdmin || (() => false),\n },\n hooks: {\n beforeChange,\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n required: true,\n admin: {\n description: 'Name of the referral program for admin reference',\n },\n },\n {\n name: 'isActive',\n type: 'checkbox',\n defaultValue: true,\n admin: {\n description: 'Whether this referral program is currently active',\n },\n },\n {\n name: 'commissionRules',\n type: 'array',\n required: true,\n minRows: 1,\n admin: {\n description: 'Rules for referral commission and customer discount distribution.',\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n required: false,\n admin: { description: 'Optional rule label for admin clarity' },\n },\n {\n name: 'appliesTo',\n type: 'select',\n required: true,\n options: [\n { label: 'All Products', value: 'all' },\n { label: 'Specific Products', value: 'products' },\n { label: 'Categories and Tags', value: 'segments' },\n ],\n defaultValue: 'all',\n },\n {\n name: 'products',\n type: 'relationship',\n relationTo: 'products',\n hasMany: true,\n admin: {\n condition: (_: unknown, siblingData: { appliesTo?: string }) =>\n siblingData?.appliesTo === 'products',\n description: 'Products this rule applies to',\n },\n },\n {\n name: 'categories',\n type: 'relationship',\n relationTo: 'categories',\n hasMany: true,\n admin: {\n condition: (_: unknown, siblingData: { appliesTo?: string }) =>\n siblingData?.appliesTo === 'segments',\n description: 'Any matching category can activate this rule',\n },\n },\n {\n name: 'tags',\n type: 'relationship',\n relationTo: 'tags',\n hasMany: true,\n admin: {\n condition: (_: unknown, siblingData: { appliesTo?: string }) =>\n siblingData?.appliesTo === 'segments',\n description: 'Any matching tag can activate this rule',\n },\n },\n {\n name: 'totalCommission',\n type: 'group',\n admin: {\n description: 'Total commission pool to split between partner and customer',\n },\n fields: [\n {\n name: 'type',\n type: 'select',\n required: true,\n options: allowedTotalCommissionTypes.map((value) => ({\n label: value === 'fixed' ? 'Fixed Amount' : 'Percentage of Order',\n value,\n })),\n defaultValue: allowedTotalCommissionTypes.includes('fixed')\n ? 'fixed'\n : 'percentage',\n },\n {\n name: 'value',\n type: 'number',\n required: true,\n min: 0,\n admin: {\n description: `Total commission value`,\n },\n },\n {\n name: 'maxAmount',\n type: 'number',\n min: 0,\n admin: {\n description: `Max commission cap per item in ${defaultCurrency}`,\n },\n },\n ],\n },\n {\n name: 'partnerSplit',\n type: 'number',\n required: true,\n min: 0,\n max: 100,\n defaultValue: referralConfig.defaultPartnerSplit,\n admin: {\n description: 'Percentage of total commission given to Partner (0-100)',\n },\n },\n {\n name: 'minOrderAmount',\n type: 'number',\n min: 0,\n admin: {\n description: `Minimum cart subtotal required for this rule in ${defaultCurrency}. Leave empty for no minimum.`,\n },\n },\n {\n name: 'customerSplit',\n type: 'number',\n min: 0,\n max: 100,\n hooks: {\n beforeValidate: [\n ({ siblingData }: { siblingData?: { partnerSplit?: number } }) =>\n deriveCustomerSplit(siblingData?.partnerSplit),\n ],\n beforeChange: [\n ({ siblingData }: { siblingData?: { partnerSplit?: number } }) =>\n deriveCustomerSplit(siblingData?.partnerSplit),\n ],\n },\n admin: {\n readOnly: true,\n description: 'Auto-calculated from Partner Split (saved automatically)',\n },\n },\n ],\n },\n ],\n timestamps: true,\n }\n}\n","/**\n * Rounds a number to 2 decimal places (standard for monetary values).\n */\nexport function roundTo2(value: number): number {\n return Math.round(value * 100) / 100\n}\n","export const DEFAULT_PRICE_CURRENCY = 'AED'\n\ntype PriceEntity = {\n price?: number | null\n} & Record<string, unknown>\n\nfunction normalizeCurrencyCode(currencyCode?: string): string {\n if (!currencyCode) return DEFAULT_PRICE_CURRENCY\n return currencyCode.toUpperCase()\n}\n\nfunction readNumberField(entity: unknown, key: string): number | undefined {\n if (!entity || typeof entity !== 'object') return undefined\n const value = (entity as Record<string, unknown>)[key]\n return typeof value === 'number' ? value : undefined\n}\n\nexport function getPriceFieldKey(currencyCode: string): string {\n return `priceIn${normalizeCurrencyCode(currencyCode)}`\n}\n\nexport function readMoneyField(\n entity: PriceEntity | null | undefined,\n currencyCode: string,\n defaultCurrencyCode = DEFAULT_PRICE_CURRENCY,\n): number | undefined {\n if (!entity) return undefined\n\n const primaryField = getPriceFieldKey(currencyCode)\n const primary = readNumberField(entity, primaryField)\n if (typeof primary === 'number') return primary\n\n const fallbackField = getPriceFieldKey(defaultCurrencyCode)\n if (fallbackField !== primaryField) {\n const fallback = readNumberField(entity, fallbackField)\n if (typeof fallback === 'number') return fallback\n }\n\n return typeof entity.price === 'number' ? entity.price : undefined\n}\n\nexport function resolveMoneyField(\n entity: PriceEntity | null | undefined,\n currencyCode: string,\n defaultCurrencyCode = DEFAULT_PRICE_CURRENCY,\n): number {\n return readMoneyField(entity, currencyCode, defaultCurrencyCode) ?? 0\n}\n\nexport function getCartItemUnitPrice({\n item,\n product,\n variant,\n currencyCode,\n defaultCurrencyCode = DEFAULT_PRICE_CURRENCY,\n}: {\n item?: {\n price?: number | null\n unitPrice?: number | null\n } | null\n product?: PriceEntity | null\n variant?: PriceEntity | null\n currencyCode: string\n defaultCurrencyCode?: string\n}): number {\n if (typeof item?.price === 'number') return item.price\n if (typeof item?.unitPrice === 'number') return item.unitPrice\n if (variant) return resolveMoneyField(variant, currencyCode, defaultCurrencyCode)\n return resolveMoneyField(product, currencyCode, defaultCurrencyCode)\n}\n","import { roundTo2 } from './roundTo2'\nimport { getCartItemUnitPrice } from './pricing'\n\nexport function calculateCouponDiscount({ coupon, cartTotal }: { coupon: any; cartTotal: number }) {\n let discount = 0\n\n if (coupon.type === 'percentage') {\n discount = roundTo2((cartTotal * coupon.value) / 100)\n if (coupon.maxDiscountAmount != null && discount > coupon.maxDiscountAmount) {\n discount = roundTo2(coupon.maxDiscountAmount)\n }\n } else if (coupon.type === 'fixed') {\n discount = roundTo2(coupon.value)\n if (discount > cartTotal) {\n discount = roundTo2(cartTotal)\n }\n }\n\n return roundTo2(discount)\n}\n\nfunction relationId(value: any): string | number | null {\n if (value == null) return null\n if (typeof value === 'string' || typeof value === 'number') return value\n if (typeof value === 'object' && (typeof value.id === 'string' || typeof value.id === 'number')) {\n return value.id\n }\n return null\n}\n\nconst allowedCommissionTypesSet = (\n allowed: Array<'fixed' | 'percentage'> | undefined,\n): Set<string> =>\n new Set((allowed && allowed.length ? allowed : ['fixed', 'percentage']).map((v) => v))\n\nfunction normalizeIds(values: any[] | null | undefined): Array<string | number> {\n if (!Array.isArray(values)) return []\n return values.map(relationId).filter((v): v is string | number => v != null)\n}\n\nfunction getRuleSplits(rule: any): { partnerSplit: number; customerSplit: number } | null {\n const partnerRaw =\n typeof rule.partnerSplit === 'number'\n ? rule.partnerSplit\n : typeof rule.referrerSplit === 'number'\n ? rule.referrerSplit\n : null\n if (partnerRaw == null) return null\n\n const customerRaw =\n typeof rule.customerSplit === 'number'\n ? rule.customerSplit\n : typeof rule.refereeSplit === 'number'\n ? rule.refereeSplit\n : 100 - partnerRaw\n\n return {\n partnerSplit: partnerRaw,\n customerSplit: customerRaw,\n }\n}\n\nfunction calculateItemRewardByRule({\n rule,\n itemTotal,\n quantity,\n allowedTotalCommissionTypes,\n}: {\n rule: any\n itemTotal: number\n quantity: number\n allowedTotalCommissionTypes?: Array<'fixed' | 'percentage'>\n}): { partner: number; customer: number } | null {\n const allowedTypes = allowedCommissionTypesSet(allowedTotalCommissionTypes)\n\n // Shared model (v2)\n if (rule.totalCommission) {\n if (!allowedTypes.has(rule.totalCommission.type)) return null\n\n const splits = getRuleSplits(rule)\n if (!splits) return null\n\n let totalPot = 0\n if (rule.totalCommission.type === 'percentage') {\n totalPot = (itemTotal * rule.totalCommission.value) / 100\n } else {\n totalPot = rule.totalCommission.value * quantity\n }\n\n if (rule.totalCommission.maxAmount != null) {\n const maxPotForLine = rule.totalCommission.maxAmount * quantity\n if (totalPot > maxPotForLine) {\n totalPot = maxPotForLine\n }\n }\n\n return {\n partner: Math.floor((totalPot * splits.partnerSplit) / 100),\n customer: Math.floor((totalPot * splits.customerSplit) / 100),\n }\n }\n\n // Compatibility fallback for legacy direct rules during migration window.\n if (rule.referrerReward && rule.refereeReward) {\n let partner = 0\n if (rule.referrerReward.type === 'percentage') {\n partner = (itemTotal * rule.referrerReward.value) / 100\n } else {\n partner = rule.referrerReward.value * quantity\n }\n if (rule.referrerReward.maxReward != null && partner > rule.referrerReward.maxReward) {\n partner = rule.referrerReward.maxReward\n }\n\n let customer = 0\n if (rule.refereeReward.type === 'percentage') {\n customer = (itemTotal * rule.refereeReward.value) / 100\n } else {\n customer = rule.refereeReward.value * quantity\n }\n if (rule.refereeReward.maxReward != null && customer > rule.refereeReward.maxReward) {\n customer = rule.refereeReward.maxReward\n }\n\n return { partner, customer }\n }\n\n return null\n}\n\nfunction getItemCategoryIds(item: any): Array<string | number> {\n const productCategories = Array.isArray(item?.product?.categories)\n ? normalizeIds(item.product.categories)\n : []\n const singleCategory = relationId(item?.category ?? item?.product?.category)\n return [...productCategories, ...(singleCategory != null ? [singleCategory] : [])]\n}\n\nfunction getItemTagIds(item: any): Array<string | number> {\n return Array.isArray(item?.product?.tags) ? normalizeIds(item.product.tags) : []\n}\n\nfunction selectBestRuleForItem({\n rules,\n item,\n itemTotal,\n quantity,\n cartTotal,\n allowedTotalCommissionTypes,\n}: {\n rules: any[]\n item: any\n itemTotal: number\n quantity: number\n cartTotal: number\n allowedTotalCommissionTypes?: Array<'fixed' | 'percentage'>\n}): { rule: any; reward: { partner: number; customer: number } } | null {\n const allowedTypes = allowedCommissionTypesSet(allowedTotalCommissionTypes)\n const eligibleRules = rules.filter((rule: any) => {\n const hasSharedType = rule?.totalCommission?.type\n ? allowedTypes.has(rule.totalCommission.type)\n : true\n if (!hasSharedType) return false\n if (typeof rule?.minOrderAmount === 'number' && Number.isFinite(rule.minOrderAmount)) {\n return cartTotal >= rule.minOrderAmount\n }\n return true\n })\n\n const productId = relationId(item.product)\n const itemCategoryIds = new Set(getItemCategoryIds(item))\n const itemTagIds = new Set(getItemTagIds(item))\n\n const productCandidates = eligibleRules.filter(\n (r: any) =>\n r.appliesTo === 'products' &&\n normalizeIds(r.products).some((id) => productId != null && id === productId),\n )\n\n const segmentCategoryCandidates = eligibleRules.filter((r: any) => {\n const isSegment = r.appliesTo === 'segments' || r.appliesTo === 'categories'\n if (!isSegment) return false\n return normalizeIds(r.categories).some((id) => itemCategoryIds.has(id))\n })\n\n const segmentTagCandidates = eligibleRules.filter((r: any) => {\n if (r.appliesTo !== 'segments') return false\n return normalizeIds(r.tags).some((id) => itemTagIds.has(id))\n })\n\n const allCandidates = eligibleRules.filter((r: any) => r.appliesTo === 'all')\n\n const levels = [productCandidates, segmentCategoryCandidates, segmentTagCandidates, allCandidates]\n const candidates = levels.find((level) => level.length > 0) ?? []\n if (!candidates.length) return null\n\n let best: { rule: any; reward: { partner: number; customer: number } } | null = null\n\n for (const rule of candidates) {\n const reward = calculateItemRewardByRule({\n rule,\n itemTotal,\n quantity,\n allowedTotalCommissionTypes,\n })\n if (!reward) continue\n\n if (!best) {\n best = { rule, reward }\n continue\n }\n\n if (reward.customer > best.reward.customer) {\n best = { rule, reward }\n continue\n }\n\n if (reward.customer === best.reward.customer && reward.partner > best.reward.partner) {\n best = { rule, reward }\n }\n }\n\n return best\n}\n\nexport function getProgramMinimumOrderAmount({\n program,\n allowedTotalCommissionTypes,\n}: {\n program: any\n allowedTotalCommissionTypes?: Array<'fixed' | 'percentage'>\n}): number | null {\n const rules = Array.isArray(program?.commissionRules) ? program.commissionRules : []\n if (!rules.length) return null\n\n const allowedTypes = allowedCommissionTypesSet(allowedTotalCommissionTypes)\n const minValues = rules\n .filter((rule: any) => {\n if (rule?.totalCommission?.type) return allowedTypes.has(rule.totalCommission.type)\n return true\n })\n .map((rule: any) => rule?.minOrderAmount)\n .filter(\n (value: unknown): value is number => typeof value === 'number' && Number.isFinite(value),\n )\n\n if (!minValues.length) return null\n return Math.min(...minValues)\n}\n\nexport function calculateCommissionAndDiscount({\n cartItems,\n program,\n currencyCode = 'AED',\n cartTotal = 0,\n allowedTotalCommissionTypes,\n}: {\n cartItems: any[]\n program: any\n currencyCode?: string\n cartTotal?: number\n allowedTotalCommissionTypes?: Array<'fixed' | 'percentage'>\n}): { partnerCommission: number; customerDiscount: number } {\n const rules = Array.isArray(program?.commissionRules) ? program.commissionRules : []\n\n if (!rules.length) {\n return { partnerCommission: 0, customerDiscount: 0 }\n }\n\n let totalPartnerCommission = 0\n let totalCustomerDiscount = 0\n\n for (const item of cartItems) {\n const product = typeof item.product === 'object' ? item.product : {}\n const variant = typeof item.variant === 'object' ? item.variant : {}\n\n const itemPrice = getCartItemUnitPrice({\n item,\n product,\n variant,\n currencyCode,\n })\n\n const quantity = item.quantity ?? 1\n const itemTotal = itemPrice * quantity\n\n const bestMatch = selectBestRuleForItem({\n rules,\n item: { ...item, product },\n itemTotal,\n quantity,\n cartTotal,\n allowedTotalCommissionTypes,\n })\n\n if (!bestMatch) continue\n\n totalPartnerCommission += bestMatch.reward.partner\n totalCustomerDiscount += bestMatch.reward.customer\n }\n\n return { partnerCommission: totalPartnerCommission, customerDiscount: totalCustomerDiscount }\n}\n","import type { Endpoint, PayloadHandler } from 'payload'\nimport type { SanitizedCouponPluginOptions } from '../types'\nimport {\n calculateCommissionAndDiscount,\n calculateCouponDiscount,\n getProgramMinimumOrderAmount,\n} from '../utilities/calculateValues'\nimport { roundTo2 } from '../utilities/roundTo2'\n\ntype Args = {\n pluginConfig: SanitizedCouponPluginOptions\n}\n\n// Debug Capture\nconst globalDebugLogs: string[] = []\n\nconst getRelationId = (value: any): string | number | null => {\n if (value == null) return null\n if (typeof value === 'string' || typeof value === 'number') return value\n if (typeof value === 'object' && (typeof value.id === 'string' || typeof value.id === 'number')) {\n return value.id\n }\n return null\n}\n\nexport const applyCouponHandler =\n ({ pluginConfig }: Args): PayloadHandler =>\n async (req) => {\n globalDebugLogs.length = 0 // Reset logs\n const { payload } = req\n const { code: rawCode, cartID, customerEmail } = req.data || {}\n const code = typeof rawCode === 'string' ? rawCode.trim() : rawCode\n\n if (!code || !cartID) {\n return Response.json(\n {\n success: false,\n error: `${pluginConfig.enableReferrals ? 'Referral code' : 'Coupon code'} and cart ID are required`,\n },\n { status: 400 },\n )\n }\n\n try {\n // Find the cart first to check for existing codes\n const cartQuery = await payload.findByID({\n collection: 'carts',\n id: cartID,\n depth: 2,\n })\n\n if (!cartQuery) {\n return Response.json({ success: false, error: 'Cart not found' }, { status: 404 })\n }\n\n // Check if single code per cart is enforced\n if (pluginConfig.referralConfig.singleCodePerCart) {\n const hasExistingCoupon = cartQuery.appliedCoupon\n const hasExistingReferral = cartQuery.appliedReferralCode\n\n if (hasExistingCoupon || hasExistingReferral) {\n return Response.json(\n {\n success: false,\n error:\n 'A code has already been applied to this cart. Only one code can be used per order.',\n },\n { status: 400 },\n )\n }\n }\n\n if (pluginConfig.enableReferrals) {\n // Try referral code first\n const referralResult = await handleReferralCode({\n payload,\n code,\n cartID,\n cart: cartQuery,\n customerEmail,\n pluginConfig,\n })\n\n // If referral code not found and both systems allowed, try coupon\n if (\n !referralResult.ok &&\n referralResult.status === 404 &&\n pluginConfig.referralConfig.allowBothSystems\n ) {\n return await handleCouponCode({\n payload,\n code,\n cartID,\n cart: cartQuery,\n customerEmail,\n pluginConfig,\n })\n }\n\n return referralResult\n } else {\n // Coupon mode: handle coupons\n return await handleCouponCode({\n payload,\n code,\n cartID,\n cart: cartQuery,\n customerEmail,\n pluginConfig,\n })\n }\n } catch (error) {\n console.error('Code application error:', error)\n return Response.json({ success: false, error: 'Internal server error' }, { status: 500 })\n }\n }\n\n// Handle coupon application\nasync function handleCouponCode({\n payload,\n code,\n cartID,\n cart,\n customerEmail,\n pluginConfig,\n}: {\n payload: any\n code: string\n cartID: string\n cart: any\n customerEmail?: string\n pluginConfig: SanitizedCouponPluginOptions\n}) {\n // Find the coupon\n // Find the coupon (Case insensitive check: Exact -> Lower -> Upper)\n let couponQuery = await payload.find({\n collection: pluginConfig.collections.couponsSlug,\n where: {\n code: { equals: code },\n },\n limit: 1,\n })\n\n if (!couponQuery.docs.length) {\n couponQuery = await payload.find({\n collection: pluginConfig.collections.couponsSlug,\n where: {\n code: { equals: code.toLowerCase() },\n },\n limit: 1,\n })\n }\n\n if (!couponQuery.docs.length) {\n couponQuery = await payload.find({\n collection: pluginConfig.collections.couponsSlug,\n where: {\n code: { equals: code.toUpperCase() },\n },\n limit: 1,\n })\n }\n\n if (!couponQuery.docs.length) {\n return Response.json({ success: false, error: 'Invalid coupon code' }, { status: 404 })\n }\n\n const coupon = couponQuery.docs[0]\n\n // Check if coupon is active\n const now = new Date()\n const activeFrom = coupon.activeFrom ? new Date(coupon.activeFrom) : null\n const activeUntil = coupon.activeUntil ? new Date(coupon.activeUntil) : null\n\n if (activeFrom && now < activeFrom) {\n return Response.json({ success: false, error: 'Coupon is not yet active' }, { status: 400 })\n }\n\n if (activeUntil && now > activeUntil) {\n return Response.json({ success: false, error: 'Coupon has expired' }, { status: 400 })\n }\n\n // Check usage limits\n if (coupon.usageLimit && coupon.usageCount >= coupon.usageLimit) {\n return Response.json({ success: false, error: 'Coupon usage limit exceeded' }, { status: 400 })\n }\n\n // Per-customer limit: require customer email and count paid orders with this coupon for this customer\n if (coupon.perCustomerLimit != null && coupon.perCustomerLimit > 0) {\n const email = typeof customerEmail === 'string' ? customerEmail.trim() : ''\n if (!email) {\n return Response.json(\n { success: false, error: 'Customer email is required for this coupon.' },\n { status: 400 },\n )\n }\n const { ordersSlug, orderCustomerEmailField, orderPaymentStatusField, orderPaidStatusValue } =\n pluginConfig.orderIntegration\n const ordersQuery = await payload.find({\n collection: ordersSlug,\n where: {\n and: [\n { appliedCoupon: { equals: coupon.id } },\n { [orderCustomerEmailField]: { equals: email } },\n { [orderPaymentStatusField]: { equals: orderPaidStatusValue } },\n ],\n },\n limit: 0,\n })\n if (ordersQuery.totalDocs >= coupon.perCustomerLimit) {\n return Response.json(\n { success: false, error: 'You have reached the maximum uses for this coupon.' },\n { status: 400 },\n )\n }\n }\n\n // Check if coupon already applied to this cart\n if (getRelationId(cart.appliedCoupon) === coupon.id) {\n return Response.json(\n { success: false, error: 'Coupon already applied to this cart' },\n { status: 400 },\n )\n }\n\n // Calculate discount based on cart total\n const cartTotal = cart.subtotal || cart.total || 0\n\n // Check minimum order value\n if (coupon.minOrderValue && cartTotal < coupon.minOrderValue) {\n return Response.json(\n {\n success: false,\n error: `Minimum order value of ${coupon.minOrderValue} ${pluginConfig.defaultCurrency} required`,\n },\n { status: 400 },\n )\n }\n\n // Check maximum order value\n if (coupon.maxOrderValue && cartTotal > coupon.maxOrderValue) {\n return Response.json(\n {\n success: false,\n error: `Maximum order value of ${coupon.maxOrderValue} ${pluginConfig.defaultCurrency} exceeded`,\n },\n { status: 400 },\n )\n }\n\n const discountAmount = calculateCouponDiscount({ coupon, cartTotal })\n const total = roundTo2(Math.max(0, cartTotal - discountAmount))\n\n // Apply coupon to cart (usage is counted when order is placed via recordCouponUsageForOrder)\n await payload.update({\n collection: 'carts',\n id: cartID,\n data: {\n appliedCoupon: coupon.id,\n discountAmount,\n total,\n },\n })\n\n return Response.json({\n success: true,\n message: 'Coupon applied successfully',\n coupon: {\n code: coupon.code,\n type: coupon.type,\n value: coupon.value,\n },\n discount: discountAmount,\n currency: pluginConfig.defaultCurrency,\n debug: globalDebugLogs,\n })\n}\n\n// Handle referral code application\nasync function handleReferralCode({\n payload,\n code,\n cartID,\n cart,\n customerEmail: _customerEmail,\n pluginConfig,\n}: {\n payload: any\n code: string\n cartID: string\n cart: any\n customerEmail?: string\n pluginConfig: SanitizedCouponPluginOptions\n}) {\n // Find the referral code\n // Find the referral code (Case insensitive check: Exact -> Lower -> Upper)\n let referralQuery = await payload.find({\n collection: pluginConfig.collections.referralCodesSlug,\n where: {\n code: { equals: code },\n },\n limit: 1,\n depth: 1,\n })\n\n if (!referralQuery.docs.length) {\n referralQuery = await payload.find({\n collection: pluginConfig.collections.referralCodesSlug,\n where: {\n code: { equals: code.toLowerCase() },\n },\n limit: 1,\n depth: 1,\n })\n }\n\n if (!referralQuery.docs.length) {\n referralQuery = await payload.find({\n collection: pluginConfig.collections.referralCodesSlug,\n where: {\n code: { equals: code.toUpperCase() },\n },\n limit: 1,\n depth: 1,\n })\n }\n\n if (!referralQuery.docs.length) {\n return Response.json({ success: false, error: 'Invalid referral code' }, { status: 404 })\n }\n\n const referralCode = referralQuery.docs[0]\n\n // Check if referral code is active\n if (!referralCode.isActive) {\n return Response.json({ success: false, error: 'Referral code is not active' }, { status: 400 })\n }\n\n // Check expiration\n if (referralCode.expiresAt && new Date() > new Date(referralCode.expiresAt)) {\n return Response.json({ success: false, error: 'Referral code has expired' }, { status: 400 })\n }\n\n // Check usage limit\n if (referralCode.usageLimit && referralCode.usageCount >= referralCode.usageLimit) {\n return Response.json(\n { success: false, error: 'Referral code usage limit exceeded' },\n { status: 400 },\n )\n }\n\n // Get the referral program\n const programId =\n typeof referralCode.program === 'string' ? referralCode.program : referralCode.program?.id\n\n const program = await payload.findByID({\n collection: pluginConfig.collections.referralProgramsSlug,\n id: programId,\n })\n\n if (!program || !program.isActive) {\n return Response.json(\n { success: false, error: 'Referral program is not active' },\n { status: 400 },\n )\n }\n\n // Check if referral code already applied to this cart\n if (getRelationId(cart.appliedReferralCode) === referralCode.id) {\n return Response.json(\n { success: false, error: 'Referral code already applied to this cart' },\n { status: 400 },\n )\n }\n\n // Calculate commission and discount\n const cartTotal = cart.subtotal || cart.total || 0\n const minOrderAmount = getProgramMinimumOrderAmount({\n program,\n allowedTotalCommissionTypes: pluginConfig.referralConfig.allowedTotalCommissionTypes,\n })\n\n if (typeof minOrderAmount === 'number' && cartTotal < minOrderAmount) {\n return Response.json(\n {\n success: false,\n error: `Minimum order value of ${minOrderAmount} ${pluginConfig.defaultCurrency} required for this referral program`,\n },\n { status: 400 },\n )\n }\n\n // Calculate based on commission rules\n const { partnerCommission, customerDiscount } = calculateCommissionAndDiscount({\n cartItems: cart.items || [],\n program,\n currencyCode: pluginConfig.defaultCurrency,\n cartTotal,\n allowedTotalCommissionTypes: pluginConfig.referralConfig.allowedTotalCommissionTypes,\n })\n\n // Round commission and discount\n const roundedPartnerCommission = roundTo2(partnerCommission)\n const roundedCustomerDiscount = roundTo2(customerDiscount)\n const total = roundTo2(Math.max(0, cartTotal - roundedCustomerDiscount))\n\n // Apply referral to cart\n await payload.update({\n collection: 'carts',\n id: cartID,\n data: {\n appliedReferralCode: referralCode.id,\n partnerCommission: roundedPartnerCommission,\n customerDiscount: roundedCustomerDiscount,\n total,\n },\n })\n\n return Response.json({\n success: true,\n message: 'Referral code applied successfully',\n referralCode: {\n code: referralCode.code,\n },\n partnerCommission: roundedPartnerCommission,\n customerDiscount: roundedCustomerDiscount,\n currency: pluginConfig.defaultCurrency,\n debug: globalDebugLogs,\n })\n}\n\nexport const applyCouponEndpoint = ({ pluginConfig }: Args): Endpoint => ({\n path: pluginConfig.endpoints.applyCoupon,\n method: 'post',\n handler: applyCouponHandler({ pluginConfig }),\n})\n","import type { Endpoint, PayloadHandler } from 'payload'\n\nimport type { PartnerDashboardData, PartnerStats, SanitizedCouponPluginOptions } from '../types'\nimport { isAdminUser, isPartnerUser } from '../utilities/userRoles'\n\ntype Args = {\n pluginConfig: SanitizedCouponPluginOptions\n}\n\nexport const partnerStatsHandler =\n ({ pluginConfig }: Args): PayloadHandler =>\n async (req) => {\n const { payload, user } = req\n\n if (!user) {\n return Response.json({ success: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const typedUser = user as { id: string }\n\n // Check if user is a partner\n const isPartner =\n isPartnerUser({ user: typedUser, roleConfig: pluginConfig.roleConfig }) ||\n pluginConfig.access.isPartner?.({ req } as any)\n const isAdmin =\n isAdminUser({ user: typedUser, roleConfig: pluginConfig.roleConfig }) ||\n pluginConfig.access.isAdmin?.({ req } as any)\n\n if (!isPartner && !isAdmin) {\n return Response.json({ success: false, error: 'Partner access required' }, { status: 403 })\n }\n\n try {\n // Get partner's referral codes\n const referralCodesQuery = await payload.find({\n collection: pluginConfig.collections.referralCodesSlug,\n where: {\n partner: { equals: typedUser.id },\n },\n limit: 100,\n })\n\n const referralCodes = referralCodesQuery.docs\n\n // Calculate stats\n let totalEarnings = 0\n let pendingEarnings = 0\n let paidEarnings = 0\n let totalReferrals = 0\n let successfulReferrals = 0\n\n const referralCodeData = referralCodes.map((code: any) => {\n totalEarnings += code.totalEarnings || 0\n pendingEarnings += code.pendingEarnings || 0\n paidEarnings += code.paidEarnings || 0\n totalReferrals += code.usageCount || 0\n successfulReferrals += code.successfulReferralsCount || 0\n\n return {\n id: code.id,\n code: code.code,\n usageCount: code.usageCount || 0,\n totalEarnings: code.totalEarnings || 0,\n isActive: code.isActive,\n }\n })\n\n // Calculate conversion rate\n const conversionRate = totalReferrals > 0 ? (successfulReferrals / totalReferrals) * 100 : 0\n\n // Get recent referrals (from orders with this partner's referral codes)\n const recentReferrals: PartnerStats['recentReferrals'] = []\n\n // Try to get orders with applied referral codes\n try {\n const ordersQuery = await payload.find({\n collection: 'orders',\n where: {\n appliedReferralCode: {\n in: referralCodes.map((c: any) => c.id),\n },\n },\n limit: 10,\n sort: '-createdAt',\n })\n\n for (const order of ordersQuery.docs as any[]) {\n recentReferrals.push({\n id: order.id,\n code: referralCodes.find((c: any) => c.id === order.appliedReferralCode)?.code || '',\n orderValue: order.total || 0,\n commission: order.partnerCommission || 0,\n date: order.createdAt,\n status: order.paymentStatus === 'paid' ? 'paid' : 'pending',\n })\n }\n } catch {\n // Orders collection might not exist or have different structure\n }\n\n // Calculate monthly earnings (last 6 months)\n const monthlyEarnings: PartnerStats['monthlyEarnings'] = []\n const now = new Date()\n\n for (let i = 5; i >= 0; i--) {\n const monthDate = new Date(now.getFullYear(), now.getMonth() - i, 1)\n const monthName = monthDate.toLocaleString('default', { month: 'short', year: 'numeric' })\n\n // This would need actual order data to calculate properly\n // For now, we'll provide placeholder structure\n monthlyEarnings.push({\n month: monthName,\n earnings: 0,\n referrals: 0,\n })\n }\n\n // Get the partner's active program\n let program: PartnerDashboardData['program'] = null\n\n if (referralCodes.length > 0) {\n const firstCode = referralCodes[0] as any\n if (firstCode.program) {\n try {\n const programData = await payload.findByID({\n collection: pluginConfig.collections.referralProgramsSlug,\n id: typeof firstCode.program === 'string' ? firstCode.program : firstCode.program.id,\n })\n\n if (programData) {\n const typedProgram = programData as any\n const firstRule = typedProgram.commissionRules?.[0]\n const partnerSplit =\n firstRule?.partnerSplit ??\n firstRule?.referrerSplit ??\n firstRule?.split?.partnerPercentage ??\n 0\n const customerSplit =\n firstRule?.customerSplit ??\n firstRule?.refereeSplit ??\n firstRule?.split?.customerPercentage ??\n 100 - partnerSplit\n program = {\n name: typedProgram.name,\n commissionRate: partnerSplit,\n customerDiscount: customerSplit,\n }\n }\n } catch {\n // Program might not exist\n }\n }\n }\n\n const stats: PartnerStats = {\n totalEarnings,\n pendingEarnings,\n paidEarnings,\n totalReferrals,\n successfulReferrals,\n conversionRate: Math.round(conversionRate * 100) / 100,\n recentReferrals,\n monthlyEarnings,\n }\n\n const dashboardData: PartnerDashboardData = {\n stats,\n referralCodes: referralCodeData,\n program,\n }\n\n return Response.json({\n success: true,\n data: dashboardData,\n currency: pluginConfig.defaultCurrency,\n })\n } catch (error) {\n console.error('Partner stats error:', error)\n return Response.json(\n { success: false, error: 'Failed to fetch partner stats' },\n { status: 500 },\n )\n }\n }\n\nexport const partnerStatsEndpoint = ({ pluginConfig }: Args): Endpoint => ({\n path: pluginConfig.endpoints.partnerStats,\n method: 'get',\n handler: partnerStatsHandler({ pluginConfig }),\n})\n","import type { Endpoint, PayloadHandler } from 'payload'\n\nimport type { SanitizedCouponPluginOptions } from '../types'\nimport {\n calculateCommissionAndDiscount,\n getProgramMinimumOrderAmount,\n} from '../utilities/calculateValues'\nimport { roundTo2 } from '../utilities/roundTo2'\n\ntype Args = {\n pluginConfig: SanitizedCouponPluginOptions\n}\n\nexport const validateCouponHandler =\n ({ pluginConfig }: Args): PayloadHandler =>\n async (req) => {\n const { payload } = req\n const { code: rawCode, cartValue, cartID, customerEmail } = req.data || {}\n const code = typeof rawCode === 'string' ? rawCode.trim() : rawCode\n\n if (!code) {\n return Response.json(\n {\n success: false,\n error: 'Code is required',\n },\n { status: 400 },\n )\n }\n\n try {\n if (pluginConfig.enableReferrals) {\n // Referral mode: validate referral codes\n return await validateReferralCode({ payload, code, cartID, pluginConfig })\n } else {\n // Coupon mode: validate coupons\n return await validateCouponCode({\n payload,\n code,\n cartValue,\n customerEmail,\n pluginConfig,\n })\n }\n } catch (error) {\n console.error('Code validation error:', error)\n return Response.json({ success: false, error: 'Internal server error' }, { status: 500 })\n }\n }\n\n// Validate coupon code (existing logic)\nasync function validateCouponCode({\n payload,\n code,\n cartValue,\n customerEmail,\n pluginConfig,\n}: {\n payload: any\n code: string\n cartValue?: number\n customerEmail?: string\n pluginConfig: SanitizedCouponPluginOptions\n}) {\n // Find the coupon\n // Find the coupon (Case insensitive check: Exact -> Lower -> Upper)\n let coupon = await payload.find({\n collection: pluginConfig.collections.couponsSlug,\n where: {\n code: { equals: code },\n },\n limit: 1,\n })\n\n if (!coupon.docs.length) {\n coupon = await payload.find({\n collection: pluginConfig.collections.couponsSlug,\n where: {\n code: { equals: code.toLowerCase() },\n },\n limit: 1,\n })\n }\n\n if (!coupon.docs.length) {\n coupon = await payload.find({\n collection: pluginConfig.collections.couponsSlug,\n where: {\n code: { equals: code.toUpperCase() },\n },\n limit: 1,\n })\n }\n\n if (!coupon.docs.length) {\n return Response.json({ success: false, error: 'Invalid coupon code' }, { status: 404 })\n }\n\n const couponData = coupon.docs[0]\n\n // Check if coupon is active\n const now = new Date()\n const activeFrom = couponData.activeFrom ? new Date(couponData.activeFrom) : null\n const activeUntil = couponData.activeUntil ? new Date(couponData.activeUntil) : null\n\n if (activeFrom && now < activeFrom) {\n return Response.json({ success: false, error: 'Coupon is not yet active' }, { status: 400 })\n }\n\n if (activeUntil && now > activeUntil) {\n return Response.json({ success: false, error: 'Coupon has expired' }, { status: 400 })\n }\n\n // Check usage limits\n if (couponData.usageLimit && couponData.usageCount >= couponData.usageLimit) {\n return Response.json({ success: false, error: 'Coupon usage limit exceeded' }, { status: 400 })\n }\n\n // Optional: per-customer limit (when customer identifier provided)\n if (\n couponData.perCustomerLimit != null &&\n couponData.perCustomerLimit > 0 &&\n typeof customerEmail === 'string' &&\n customerEmail.trim().length > 0\n ) {\n const email = customerEmail.trim()\n const { ordersSlug, orderCustomerEmailField, orderPaymentStatusField, orderPaidStatusValue } =\n pluginConfig.orderIntegration\n const ordersQuery = await payload.find({\n collection: ordersSlug,\n where: {\n and: [\n { appliedCoupon: { equals: couponData.id } },\n { [orderCustomerEmailField]: { equals: email } },\n { [orderPaymentStatusField]: { equals: orderPaidStatusValue } },\n ],\n },\n limit: 0,\n })\n if (ordersQuery.totalDocs >= couponData.perCustomerLimit) {\n return Response.json(\n { success: false, error: 'You have reached the maximum uses for this coupon.' },\n { status: 400 },\n )\n }\n }\n\n // Check minimum/maximum order value (top-level fields, same as apply endpoint)\n if (cartValue !== undefined) {\n const minOrderValue = couponData.minOrderValue\n const maxOrderValue = couponData.maxOrderValue\n\n if (minOrderValue && cartValue < minOrderValue) {\n return Response.json(\n {\n success: false,\n error: `Minimum order value of ${minOrderValue} ${pluginConfig.defaultCurrency} required`,\n },\n { status: 400 },\n )\n }\n\n if (maxOrderValue && cartValue > maxOrderValue) {\n return Response.json(\n {\n success: false,\n error: `Maximum order value of ${maxOrderValue} ${pluginConfig.defaultCurrency} exceeded`,\n },\n { status: 400 },\n )\n }\n }\n\n // Calculate discount preview (2 decimal standard)\n let discount = 0\n if (cartValue !== undefined) {\n if (couponData.type === 'percentage') {\n discount = roundTo2((cartValue * couponData.value) / 100)\n if (couponData.maxDiscountAmount != null && discount > couponData.maxDiscountAmount) {\n discount = roundTo2(couponData.maxDiscountAmount)\n }\n } else if (couponData.type === 'fixed') {\n discount = roundTo2(couponData.value)\n if (discount > cartValue) discount = roundTo2(cartValue)\n }\n }\n\n return Response.json({\n success: true,\n coupon: {\n code: couponData.code,\n type: couponData.type,\n value: couponData.value,\n description: couponData.description,\n },\n discount,\n currency: pluginConfig.defaultCurrency,\n })\n}\n\n// Validate referral code (new logic)\nasync function validateReferralCode({\n payload,\n code,\n cartID,\n pluginConfig,\n}: {\n payload: any\n code: string\n cartID?: string\n pluginConfig: SanitizedCouponPluginOptions\n}) {\n // Find the referral code\n // Find the referral code (Case insensitive check: Exact -> Lower -> Upper)\n let referral = await payload.find({\n collection: pluginConfig.collections.referralCodesSlug,\n where: {\n code: { equals: code },\n },\n limit: 1,\n })\n\n if (!referral.docs.length) {\n referral = await payload.find({\n collection: pluginConfig.collections.referralCodesSlug,\n where: {\n code: { equals: code.toLowerCase() },\n },\n limit: 1,\n })\n }\n\n if (!referral.docs.length) {\n referral = await payload.find({\n collection: pluginConfig.collections.referralCodesSlug,\n where: {\n code: { equals: code.toUpperCase() },\n },\n limit: 1,\n })\n }\n\n if (!referral.docs.length) {\n return Response.json({ success: false, error: 'Referral code not found' }, { status: 404 })\n }\n\n const referralData = referral.docs[0]\n\n // Check if referral code is active\n if (!referralData.isActive) {\n return Response.json({ success: false, error: 'Referral code is not active' }, { status: 400 })\n }\n\n // Check expiration\n if (referralData.expiresAt && new Date() > new Date(referralData.expiresAt)) {\n return Response.json({ success: false, error: 'Referral code has expired' }, { status: 400 })\n }\n\n // Check usage limit\n if (referralData.usageLimit && referralData.usageCount >= referralData.usageLimit) {\n return Response.json(\n { success: false, error: 'Referral code usage limit exceeded' },\n { status: 400 },\n )\n }\n\n // Get the referral program\n const programId =\n typeof referralData.program === 'string' ? referralData.program : referralData.program?.id\n\n const program = await payload.findByID({\n collection: pluginConfig.collections.referralProgramsSlug,\n id: programId,\n })\n\n if (!program || !program.isActive) {\n return Response.json(\n { success: false, error: 'Referral program is not active' },\n { status: 400 },\n )\n }\n\n const cart = cartID\n ? await payload.findByID({\n collection: 'carts',\n id: cartID,\n depth: 2,\n })\n : null\n\n const cartTotal = cart ? cart.subtotal || cart.total || 0 : 0\n const minOrderAmount = getProgramMinimumOrderAmount({\n program,\n allowedTotalCommissionTypes: pluginConfig.referralConfig.allowedTotalCommissionTypes,\n })\n\n if (typeof minOrderAmount === 'number' && cartTotal < minOrderAmount) {\n return Response.json(\n {\n success: false,\n error: `Minimum order value of ${minOrderAmount} ${pluginConfig.defaultCurrency} required for this referral program`,\n },\n { status: 400 },\n )\n }\n\n const { partnerCommission, customerDiscount } = calculateCommissionAndDiscount({\n cartItems: cart?.items || [],\n program,\n currencyCode: pluginConfig.defaultCurrency,\n cartTotal,\n allowedTotalCommissionTypes: pluginConfig.referralConfig.allowedTotalCommissionTypes,\n })\n\n const cappedCustomerDiscount =\n cartTotal > 0 ? Math.min(customerDiscount, cartTotal) : customerDiscount\n\n const roundedPartnerCommission = roundTo2(partnerCommission)\n const roundedCustomerDiscount = roundTo2(cappedCustomerDiscount)\n\n return Response.json({\n success: true,\n referralCode: {\n code: referralData.code,\n description: `Get ${roundedCustomerDiscount.toFixed(2)} discount with this referral code`,\n },\n partnerCommission: roundedPartnerCommission,\n customerDiscount: roundedCustomerDiscount,\n currency: pluginConfig.defaultCurrency,\n })\n}\n\nexport const validateCouponEndpoint = ({ pluginConfig }: Args): Endpoint => ({\n path: pluginConfig.endpoints.validateCoupon,\n method: 'post',\n handler: validateCouponHandler({ pluginConfig }),\n})\n","import type { CollectionBeforeChangeHook } from 'payload'\nimport type { SanitizedCouponPluginOptions } from '../types'\nimport {\n calculateCommissionAndDiscount,\n calculateCouponDiscount,\n getProgramMinimumOrderAmount,\n} from '../utilities/calculateValues'\nimport { getCartItemUnitPrice } from '../utilities/pricing'\nimport { roundTo2 } from '../utilities/roundTo2'\n\nexport const recalculateCartHook =\n (pluginConfig: SanitizedCouponPluginOptions): CollectionBeforeChangeHook =>\n async ({ data, req, originalDoc }) => {\n // If no Payload, can't fetch relations\n if (!req.payload) return data\n\n // Determine effective state\n // data.items might be replacing or merging. In standard ecommerce, usually it replaces.\n // We need to calculate based on the *final* state of items.\n // If data.items is present, use it. If not, use originalDoc.items.\n const effectiveItems = data.items || originalDoc?.items || []\n\n // If no items, ensure totals are 0\n if (!effectiveItems.length) {\n return {\n ...data,\n partnerCommission: 0,\n customerDiscount: 0,\n discountAmount: 0,\n total: 0,\n }\n }\n\n // Determine effective codes\n const appliedReferralCode =\n data.appliedReferralCode !== undefined\n ? data.appliedReferralCode\n : originalDoc?.appliedReferralCode\n const appliedCoupon =\n data.appliedCoupon !== undefined ? data.appliedCoupon : originalDoc?.appliedCoupon\n\n if (!appliedReferralCode && !appliedCoupon) {\n // No codes applied, just return data (cleanup done by other logic if needed, or we explicitly clear?)\n // Use case: user removed code. data.appliedCoupon would be null.\n // If we are just updating items, and code was removed, these should be 0.\n // But if code was removed, data.appliedCoupon is null.\n if (data.appliedReferralCode === null || data.appliedCoupon === null) {\n const fallbackSubtotal =\n typeof data.subtotal === 'number'\n ? data.subtotal\n : typeof originalDoc?.subtotal === 'number'\n ? originalDoc.subtotal\n : undefined\n\n return {\n ...data,\n partnerCommission: 0,\n customerDiscount: 0,\n discountAmount: 0,\n total: fallbackSubtotal,\n }\n }\n return data\n }\n\n // We need fully hydrated items to calculate prices\n // Optimized: Only fetch if we really need to recalculate.\n // Standard ecommerce recalculates 'total' and 'subtotal' in its hooks.\n // We need to know the *new* subtotal.\n // Since we don't know the order of hooks, we can't rely on data.subtotal being correct yet if we run before ecommerce.\n // SAFEST: We calculate our own subtotal based on current prices.\n\n const getRelationID = (value: unknown): number | string | undefined => {\n if (value === null || value === undefined) return undefined\n if (typeof value === 'object') return (value as { id?: number | string }).id\n if (typeof value === 'string' || typeof value === 'number') return value\n return undefined\n }\n\n const productIds = effectiveItems\n .map((item: any) => getRelationID(item.product))\n .filter((id: any): id is number | string => id !== undefined)\n\n if (!productIds.length) return data\n\n // Fetch products to get prices\n const productsQuery = await req.payload.find({\n collection: 'products', // Assumption: standard shops have products\n where: {\n id: { in: productIds },\n },\n limit: productIds.length,\n })\n\n const productsMap = new Map(productsQuery.docs.map((p) => [String(p.id), p]))\n\n let calculatedSubtotal = 0\n const enrichedItems = effectiveItems.map((item: any) => {\n const productId = getRelationID(item.product)\n const product: any = productId !== undefined ? productsMap.get(String(productId)) || {} : {}\n\n // We might need variants logic too, keeping it simple for now based on available info\n // Ideally we should replicate the price finding logic fully.\n // For now, let's map what we have.\n\n const itemPrice = getCartItemUnitPrice({\n item,\n product,\n variant: typeof item.variant === 'object' ? item.variant : undefined,\n currencyCode: pluginConfig.defaultCurrency,\n })\n\n calculatedSubtotal += itemPrice * (item.quantity ?? 1)\n\n return {\n ...item,\n product, // Attach full product for rules\n price: itemPrice, // Normalized price\n }\n })\n\n // 1. Handle Referral\n if (appliedReferralCode && pluginConfig.enableReferrals) {\n const appliedReferralCodeID = getRelationID(appliedReferralCode)\n if (appliedReferralCodeID === undefined) {\n data.partnerCommission = 0\n data.customerDiscount = 0\n data.total = calculatedSubtotal\n return data\n }\n\n // Fetch referral code & program\n const referralQuery = await req.payload.find({\n collection: pluginConfig.collections.referralCodesSlug,\n where: {\n id: { equals: appliedReferralCodeID },\n },\n limit: 1,\n depth: 1,\n })\n\n if (referralQuery.docs.length) {\n const referralCode = referralQuery.docs[0]\n const programId =\n typeof referralCode.program === 'string' ? referralCode.program : referralCode.program?.id\n const program =\n typeof referralCode.program === 'object'\n ? referralCode.program\n : programId\n ? await req.payload.findByID({\n collection: pluginConfig.collections.referralProgramsSlug,\n id: programId,\n })\n : null\n\n if (program) {\n const minOrderAmount = getProgramMinimumOrderAmount({\n program,\n allowedTotalCommissionTypes: pluginConfig.referralConfig.allowedTotalCommissionTypes,\n })\n if (typeof minOrderAmount === 'number' && calculatedSubtotal < minOrderAmount) {\n data.appliedReferralCode = null\n data.partnerCommission = 0\n data.customerDiscount = 0\n data.total = calculatedSubtotal\n return data\n }\n\n const { partnerCommission, customerDiscount } = calculateCommissionAndDiscount({\n cartItems: enrichedItems,\n program,\n currencyCode: pluginConfig.defaultCurrency,\n cartTotal: calculatedSubtotal,\n allowedTotalCommissionTypes: pluginConfig.referralConfig.allowedTotalCommissionTypes,\n })\n\n const roundedCustomerDiscount = roundTo2(customerDiscount)\n data.partnerCommission = roundTo2(partnerCommission)\n data.customerDiscount = roundedCustomerDiscount\n\n // Update total\n // Use calculated subtotal or trust data.subtotal if present?\n // Best to use our calculated subtotal to be safely independent.\n data.total = Math.max(0, calculatedSubtotal - roundedCustomerDiscount)\n } else {\n // If referral code exists but program is unavailable, clear referral discount fields.\n data.appliedReferralCode = null\n data.partnerCommission = 0\n data.customerDiscount = 0\n data.total = calculatedSubtotal\n }\n }\n }\n\n // 2. Handle Coupon\n if (appliedCoupon && (!appliedReferralCode || pluginConfig.referralConfig.allowBothSystems)) {\n const appliedCouponID = getRelationID(appliedCoupon)\n if (appliedCouponID === undefined) {\n return data\n }\n\n const couponQuery = await req.payload.find({\n collection: pluginConfig.collections.couponsSlug,\n where: {\n id: { equals: appliedCouponID },\n },\n limit: 1,\n })\n\n if (couponQuery.docs.length) {\n const coupon = couponQuery.docs[0]\n const discountAmount = calculateCouponDiscount({\n coupon,\n cartTotal: calculatedSubtotal,\n })\n\n data.discountAmount = discountAmount\n\n // If referral also applied, subtract from the already reduced total?\n // Usually discounts stack or are applied to subtotal.\n // Let's assume applied to subtotal for simplicity unless logic dictates otherwise.\n // But wait, referral discount reduces total. Coupon reduces total.\n // Standard approach: Total = Subtotal - ReferralDiscount - CouponDiscount\n\n const currentDiscount = data.customerDiscount || 0\n data.total = Math.max(0, calculatedSubtotal - currentDiscount - discountAmount)\n }\n }\n\n return data\n }\n","import type { CouponPluginOptions, SanitizedCouponPluginOptions } from '../types'\nimport { isAdminUser, isPartnerUser } from './userRoles'\n\nexport const sanitizePluginConfig = ({\n pluginConfig,\n}: {\n pluginConfig: CouponPluginOptions\n}): SanitizedCouponPluginOptions => {\n const roleConfig = {\n roleFieldPaths:\n Array.isArray(pluginConfig?.roleConfig?.roleFieldPaths) &&\n pluginConfig.roleConfig.roleFieldPaths.length > 0\n ? pluginConfig.roleConfig.roleFieldPaths\n .filter((value): value is string => typeof value === 'string')\n .map((value) => value.trim())\n .filter(Boolean)\n : ['role', 'roles'],\n adminRoleValues:\n Array.isArray(pluginConfig?.roleConfig?.adminRoleValues) &&\n pluginConfig.roleConfig.adminRoleValues.length > 0\n ? pluginConfig.roleConfig.adminRoleValues\n .filter((value): value is string => typeof value === 'string')\n .map((value) => value.trim())\n .filter(Boolean)\n : ['admin'],\n partnerRoleValues:\n Array.isArray(pluginConfig?.roleConfig?.partnerRoleValues) &&\n pluginConfig.roleConfig.partnerRoleValues.length > 0\n ? pluginConfig.roleConfig.partnerRoleValues\n .filter((value): value is string => typeof value === 'string')\n .map((value) => value.trim())\n .filter(Boolean)\n : ['partner'],\n customRoleResolver:\n typeof pluginConfig?.roleConfig?.customRoleResolver === 'function'\n ? pluginConfig.roleConfig.customRoleResolver\n : undefined,\n }\n\n const normalizedAllowedTotalCommissionTypes = Array.isArray(\n pluginConfig?.referralConfig?.allowedTotalCommissionTypes,\n )\n ? [\n ...new Set(\n pluginConfig.referralConfig.allowedTotalCommissionTypes.filter(\n (value): value is 'fixed' | 'percentage' => value === 'fixed' || value === 'percentage',\n ),\n ),\n ]\n : []\n\n // Apply defaults for each property when missing or invalid\n return {\n enabled: !(\n pluginConfig?.enabled === false ||\n (typeof pluginConfig?.enabled === 'string' && pluginConfig.enabled === 'false')\n ),\n enableReferrals:\n !!pluginConfig?.enableReferrals &&\n (typeof pluginConfig?.enableReferrals !== 'string' ||\n pluginConfig.enableReferrals !== 'false'),\n allowStackWithOtherCoupons:\n !!pluginConfig?.allowStackWithOtherCoupons &&\n (typeof pluginConfig?.allowStackWithOtherCoupons !== 'string' ||\n pluginConfig.allowStackWithOtherCoupons !== 'false'),\n defaultCurrency:\n typeof pluginConfig?.defaultCurrency === 'string' &&\n pluginConfig.defaultCurrency.length > 0 &&\n pluginConfig.defaultCurrency.length <= 3\n ? pluginConfig.defaultCurrency\n : 'USD',\n collections: {\n couponsSlug:\n typeof pluginConfig?.collections?.couponsSlug === 'string' &&\n pluginConfig.collections.couponsSlug.trim().length > 0 &&\n pluginConfig.collections.couponsSlug.length <= 100\n ? pluginConfig.collections.couponsSlug\n : 'coupons',\n referralProgramsSlug:\n typeof pluginConfig?.collections?.referralProgramsSlug === 'string' &&\n pluginConfig.collections.referralProgramsSlug.trim().length > 0 &&\n pluginConfig.collections.referralProgramsSlug.length <= 100\n ? pluginConfig.collections.referralProgramsSlug\n : 'referral-programs',\n referralCodesSlug:\n typeof pluginConfig?.collections?.referralCodesSlug === 'string' &&\n pluginConfig.collections.referralCodesSlug.trim().length > 0 &&\n pluginConfig.collections.referralCodesSlug.length <= 100\n ? pluginConfig.collections.referralCodesSlug\n : 'referral-codes',\n referralPartnersSlug:\n typeof pluginConfig?.collections?.referralPartnersSlug === 'string' &&\n pluginConfig.collections.referralPartnersSlug.trim().length > 0 &&\n pluginConfig.collections.referralPartnersSlug.length <= 100\n ? pluginConfig.collections.referralPartnersSlug\n : 'referral-partners',\n },\n endpoints: {\n applyCoupon:\n typeof pluginConfig?.endpoints?.applyCoupon === 'string' &&\n pluginConfig.endpoints.applyCoupon.trim().length > 0\n ? pluginConfig.endpoints.applyCoupon\n : '/coupons/apply',\n validateCoupon:\n typeof pluginConfig?.endpoints?.validateCoupon === 'string' &&\n pluginConfig.endpoints.validateCoupon.trim().length > 0\n ? pluginConfig.endpoints.validateCoupon\n : '/coupons/validate',\n partnerStats:\n typeof pluginConfig?.endpoints?.partnerStats === 'string' &&\n pluginConfig.endpoints.partnerStats.trim().length > 0\n ? pluginConfig.endpoints.partnerStats\n : '/referrals/partner-stats',\n recordOrderUsage:\n typeof pluginConfig?.endpoints?.recordOrderUsage === 'string' &&\n pluginConfig.endpoints.recordOrderUsage.trim().length > 0\n ? pluginConfig.endpoints.recordOrderUsage\n : '/coupons/record-order-usage',\n },\n autoIntegrate: pluginConfig?.autoIntegrate !== false,\n access: {\n canUseCoupons:\n typeof pluginConfig?.access?.canUseCoupons === 'function'\n ? pluginConfig.access.canUseCoupons\n : () => true,\n canUseReferrals:\n typeof pluginConfig?.access?.canUseReferrals === 'function'\n ? pluginConfig.access.canUseReferrals\n : () => false,\n isAdmin:\n typeof pluginConfig?.access?.isAdmin === 'function'\n ? pluginConfig.access.isAdmin\n : ({ req }) => isAdminUser({ user: req?.user, roleConfig }),\n isPartner:\n typeof pluginConfig?.access?.isPartner === 'function'\n ? pluginConfig.access.isPartner\n : ({ req }) => isPartnerUser({ user: req?.user, roleConfig }),\n },\n referralConfig: {\n allowBothSystems: pluginConfig?.referralConfig?.allowBothSystems ?? false,\n singleCodePerCart: pluginConfig?.referralConfig?.singleCodePerCart ?? true,\n defaultPartnerSplit: pluginConfig?.referralConfig?.defaultPartnerSplit ?? 70,\n defaultCustomerSplit: pluginConfig?.referralConfig?.defaultCustomerSplit ?? 30,\n allowedTotalCommissionTypes:\n normalizedAllowedTotalCommissionTypes.length > 0\n ? normalizedAllowedTotalCommissionTypes\n : ['fixed', 'percentage'],\n },\n adminGroups: {\n couponsGroup: pluginConfig?.adminGroups?.couponsGroup ?? 'Coupons',\n referralsGroup: pluginConfig?.adminGroups?.referralsGroup ?? 'Referrals',\n },\n partnerDashboard: {\n enabled: pluginConfig?.partnerDashboard?.enabled ?? true,\n showEarningsSummary: pluginConfig?.partnerDashboard?.showEarningsSummary ?? true,\n showReferralPerformance: pluginConfig?.partnerDashboard?.showReferralPerformance ?? true,\n showRecentReferrals: pluginConfig?.partnerDashboard?.showRecentReferrals ?? true,\n showCommissionBreakdown: pluginConfig?.partnerDashboard?.showCommissionBreakdown ?? true,\n },\n orderIntegration: {\n ordersSlug:\n typeof pluginConfig?.orderIntegration?.ordersSlug === 'string' &&\n pluginConfig.orderIntegration.ordersSlug.trim().length > 0\n ? pluginConfig.orderIntegration.ordersSlug\n : 'orders',\n orderCustomerEmailField:\n typeof pluginConfig?.orderIntegration?.orderCustomerEmailField === 'string' &&\n pluginConfig.orderIntegration.orderCustomerEmailField.trim().length > 0\n ? pluginConfig.orderIntegration.orderCustomerEmailField\n : 'customerEmail',\n orderPaymentStatusField:\n typeof pluginConfig?.orderIntegration?.orderPaymentStatusField === 'string' &&\n pluginConfig.orderIntegration.orderPaymentStatusField.trim().length > 0\n ? pluginConfig.orderIntegration.orderPaymentStatusField\n : 'paymentStatus',\n orderPaidStatusValue:\n typeof pluginConfig?.orderIntegration?.orderPaidStatusValue === 'string'\n ? pluginConfig.orderIntegration.orderPaidStatusValue\n : 'paid',\n },\n roleConfig,\n }\n}\n","import type { Config } from 'payload'\n\nimport { createCouponsCollection } from './collections/createCouponsCollection'\nimport { createReferralCodesCollection } from './collections/createReferralCodesCollection'\nimport { createReferralProgramsCollection } from './collections/createReferralProgramsCollection'\nimport { applyCouponEndpoint } from './endpoints/applyCoupon'\nimport { partnerStatsEndpoint } from './endpoints/partnerStats'\nimport { validateCouponEndpoint } from './endpoints/validateCoupon'\nimport { recalculateCartHook } from './hooks/recalculateCart'\nimport { CouponPluginOptions } from './types'\nimport { sanitizePluginConfig } from './utilities/sanitizePluginConfig'\n\n// Fields to append to orders (referral mode)\n\nexport const payloadEcommerceCouponPlugin =\n (pluginOptions: CouponPluginOptions = {}) =>\n async (incomingConfig: Config): Promise<Config> => {\n const pluginConfig = sanitizePluginConfig({ pluginConfig: pluginOptions })\n\n if (!pluginConfig.enabled) return incomingConfig || {}\n\n // Handle null or undefined incoming config\n if (!incomingConfig) {\n incomingConfig = { collections: [], endpoints: [] } as any\n }\n if (!incomingConfig.collections) {\n incomingConfig.collections = []\n }\n\n const collectionsToAdd = []\n\n // When enableReferrals is true, both coupon and referral collections are created\n // The referralConfig.allowBothSystems determines if both can be used simultaneously\n if (pluginConfig.enableReferrals) {\n // Referral mode: create referral collections\n let referralProgramsCollection = createReferralProgramsCollection(pluginConfig)\n let referralCodesCollection = createReferralCodesCollection(pluginConfig)\n\n // Apply collection overrides if provided\n if (pluginOptions.collections?.referralProgramsCollectionOverride) {\n referralProgramsCollection =\n await pluginOptions.collections.referralProgramsCollectionOverride({\n defaultCollection: referralProgramsCollection,\n })\n }\n\n if (pluginOptions.collections?.referralCodesCollectionOverride) {\n referralCodesCollection = await pluginOptions.collections.referralCodesCollectionOverride({\n defaultCollection: referralCodesCollection,\n })\n }\n\n collectionsToAdd.push(referralProgramsCollection, referralCodesCollection)\n\n // If allowBothSystems is true, also create coupon collection\n if (pluginConfig.referralConfig.allowBothSystems) {\n let couponsCollection = createCouponsCollection(pluginConfig)\n if (pluginOptions.collections?.couponsCollectionOverride) {\n couponsCollection = await pluginOptions.collections.couponsCollectionOverride({\n defaultCollection: couponsCollection,\n })\n }\n collectionsToAdd.push(couponsCollection)\n }\n } else {\n // Coupon mode: create coupon collections only\n let couponsCollection = createCouponsCollection(pluginConfig)\n if (pluginOptions.collections?.couponsCollectionOverride) {\n couponsCollection = await pluginOptions.collections.couponsCollectionOverride({\n defaultCollection: couponsCollection,\n })\n }\n collectionsToAdd.push(couponsCollection)\n }\n\n // Add collections to config (avoid duplicates)\n const existingSlugs = new Set(incomingConfig.collections.map((c: any) => c.slug))\n const collectionsToAddFiltered = collectionsToAdd.filter((c: any) => !existingSlugs.has(c.slug))\n incomingConfig.collections = [...incomingConfig.collections, ...collectionsToAddFiltered]\n\n // Add endpoints\n if (!incomingConfig.endpoints) {\n incomingConfig.endpoints = []\n }\n\n incomingConfig.endpoints = [\n ...incomingConfig.endpoints,\n validateCouponEndpoint({ pluginConfig }),\n applyCouponEndpoint({ pluginConfig }),\n ]\n\n // Add partner stats endpoint if referrals are enabled\n if (pluginConfig.enableReferrals) {\n incomingConfig.endpoints.push(partnerStatsEndpoint({ pluginConfig }))\n }\n\n // Safe autoIntegrate implementation — ensure referral collection exists before injecting relationships\n if (pluginConfig.autoIntegrate) {\n // Ensure collections array exists\n incomingConfig.collections = incomingConfig.collections || []\n\n // After we already appended the plugin collections above, recompute slug set\n const allSlugs = new Set<string>(incomingConfig.collections.map((c: any) => c.slug))\n\n // Helper that adds a field group to an existing collection (by slug) if not already present\n const addFieldsToCollection = (targetSlug: string, newFields: any[]) => {\n const idx = incomingConfig.collections!.findIndex((c: any) => c.slug === targetSlug)\n if (idx === -1) return\n const collection = incomingConfig.collections![idx]\n collection.fields = collection.fields || []\n\n // Avoid adding duplicate fields (by name)\n const existingFieldNames = new Set(collection.fields.map((f: any) => f.name))\n for (const f of newFields) {\n if (!existingFieldNames.has(f.name)) {\n collection.fields.push(f)\n }\n }\n\n // Replace the collection entry (mutation is OK here)\n incomingConfig.collections![idx] = collection\n }\n\n // Only inject referral integration if the referral collection slug is actually present\n if (\n pluginConfig.enableReferrals &&\n allSlugs.has(pluginConfig.collections.referralCodesSlug)\n ) {\n // Fields to append to carts (referral mode)\n const cartReferralFields = [\n {\n name: 'appliedReferralCode',\n type: 'relationship',\n relationTo: pluginConfig.collections.referralCodesSlug,\n admin: { description: 'Referral code applied to this cart' },\n },\n {\n name: 'partnerCommission',\n type: 'number',\n admin: { description: 'Partner commission amount for this cart' },\n },\n {\n name: 'customerDiscount',\n type: 'number',\n admin: { description: 'Customer discount amount for this cart' },\n },\n ]\n\n // If both systems allowed, also add coupon field\n if (\n pluginConfig.referralConfig.allowBothSystems &&\n allSlugs.has(pluginConfig.collections.couponsSlug)\n ) {\n cartReferralFields.push({\n name: 'appliedCoupon',\n type: 'relationship',\n relationTo: pluginConfig.collections.couponsSlug,\n admin: { description: 'Coupon applied to this cart' },\n })\n cartReferralFields.push({\n name: 'discountAmount',\n type: 'number',\n admin: { description: 'Discount amount from coupon' },\n })\n }\n\n addFieldsToCollection('carts', cartReferralFields)\n\n // Fields to append to orders (referral mode)\n const orderReferralFields = [\n {\n name: 'appliedReferralCode',\n type: 'relationship',\n relationTo: pluginConfig.collections.referralCodesSlug,\n admin: { description: 'Referral code applied to this order', readOnly: true },\n },\n {\n name: 'partnerCommission',\n type: 'number',\n admin: { description: 'Partner commission amount for this order', readOnly: true },\n },\n {\n name: 'customerDiscount',\n type: 'number',\n admin: { description: 'Customer discount amount for this order', readOnly: true },\n },\n ]\n\n // If both systems allowed, also add coupon field to orders\n if (\n pluginConfig.referralConfig.allowBothSystems &&\n allSlugs.has(pluginConfig.collections.couponsSlug)\n ) {\n orderReferralFields.push({\n name: 'appliedCoupon',\n type: 'relationship',\n relationTo: pluginConfig.collections.couponsSlug,\n admin: { description: 'Coupon applied to this order', readOnly: true },\n })\n orderReferralFields.push({\n name: 'discountAmount',\n type: 'number',\n admin: { description: 'Discount amount from coupon', readOnly: true },\n })\n }\n\n addFieldsToCollection('orders', orderReferralFields)\n } else if (\n !pluginConfig.enableReferrals &&\n allSlugs.has(pluginConfig.collections.couponsSlug)\n ) {\n // coupon mode — similar safe injection for appliedCoupons\n const cartCouponFields = [\n {\n name: 'appliedCoupon',\n type: 'relationship',\n relationTo: pluginConfig.collections.couponsSlug,\n admin: { description: 'Coupon applied to this cart' },\n },\n {\n name: 'discountAmount',\n type: 'number',\n admin: { description: 'Discount amount from coupon' },\n },\n ]\n addFieldsToCollection('carts', cartCouponFields)\n\n const orderCouponFields = [\n {\n name: 'appliedCoupon',\n type: 'relationship',\n relationTo: pluginConfig.collections.couponsSlug,\n admin: { description: 'Coupon applied to this order', readOnly: true },\n },\n {\n name: 'discountAmount',\n type: 'number',\n admin: { description: 'Discount amount from coupon', readOnly: true },\n },\n ]\n addFieldsToCollection('orders', orderCouponFields)\n }\n }\n\n // Add Recalculate Cart Hook\n const cartIndex = incomingConfig.collections!.findIndex((c: any) => c.slug === 'carts')\n if (cartIndex > -1) {\n const collection = incomingConfig.collections![cartIndex]\n collection.hooks = {\n ...collection.hooks,\n beforeChange: [\n ...(collection.hooks?.beforeChange || []),\n recalculateCartHook(pluginConfig),\n ],\n }\n incomingConfig.collections![cartIndex] = collection\n }\n\n return incomingConfig\n }\n","import type { ApplyCouponHook, ApplyCouponResponse, PartnerDashboardData } from '../types'\n\n/**\n * Apply a coupon code to a cart\n * @param options - Coupon code, cart ID, and customer email\n * @returns Response with success status, discount amount, and coupon details\n */\nexport async function useCouponCode(options: ApplyCouponHook): Promise<ApplyCouponResponse> {\n const { code, cartID, customerEmail } = options\n\n if (!code) {\n return {\n success: false,\n message: 'Coupon code is required',\n error: 'Code is missing',\n }\n }\n\n try {\n const response = await fetch('/api/coupons/apply', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ code, cartID, customerEmail }),\n })\n\n const data = (await response.json()) as Record<string, unknown>\n\n if (!response.ok) {\n return {\n success: false,\n message: (data.error as string) || 'Failed to apply coupon',\n error: data.error as string,\n }\n }\n\n const couponData = data.coupon as Record<string, unknown> | undefined\n const referralData = data.referralCode as Record<string, unknown> | undefined\n\n return {\n success: data.success as boolean,\n message: data.message as string,\n discount: (data.discount as number) || (data.customerDiscount as number),\n partnerCommission: data.partnerCommission as number,\n customerDiscount: data.customerDiscount as number,\n coupon: couponData\n ? {\n code: (couponData.code as string) || '',\n type: (couponData.type as 'percentage' | 'fixed') || 'percentage',\n value: (couponData.value as number) || 0,\n }\n : undefined,\n referralCode: referralData\n ? {\n code: (referralData.code as string) || '',\n }\n : undefined,\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Network error'\n return { success: false, message, error: message }\n }\n}\n\n/**\n * Validate a coupon code without applying it\n * @param code - Coupon code to validate\n * @param cartValue - Optional cart value in smallest currency unit\n * @returns Response with validation result and coupon details\n */\nexport async function validateCouponCode(\n code: string,\n cartValue?: number,\n cartID?: string,\n): Promise<ApplyCouponResponse> {\n if (!code) {\n return {\n success: false,\n message: 'Code required',\n error: 'Code missing',\n }\n }\n\n try {\n const response = await fetch('/api/coupons/validate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ code, cartValue, cartID }),\n })\n\n const data = (await response.json()) as Record<string, unknown>\n\n if (!response.ok) {\n return {\n success: false,\n message: (data.error as string) || 'Invalid coupon',\n error: data.error as string,\n }\n }\n\n const couponData = data.coupon as Record<string, unknown> | undefined\n const referralData = data.referralCode as Record<string, unknown> | undefined\n\n return {\n success: data.success as boolean,\n message: data.message as string,\n coupon: couponData\n ? {\n code: (couponData.code as string) || '',\n type: (couponData.type as 'percentage' | 'fixed') || 'percentage',\n value: (couponData.value as number) || 0,\n }\n : undefined,\n referralCode: referralData\n ? {\n code: (referralData.code as string) || '',\n }\n : undefined,\n discount: data.discount as number,\n partnerCommission: data.partnerCommission as number,\n customerDiscount: data.customerDiscount as number,\n currency: data.currency as string,\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Network error'\n return { success: false, message, error: message }\n }\n}\n\nexport type PartnerStatsResponse = {\n success: boolean\n data?: PartnerDashboardData\n currency?: string\n error?: string\n}\n\n/**\n * Fetch partner dashboard statistics\n * @param apiEndpoint - Optional custom API endpoint (default: /api/referrals/partner-stats)\n * @returns Response with partner stats, referral codes, and program info\n */\nexport async function usePartnerStats(\n apiEndpoint: string = '/api/referrals/partner-stats',\n): Promise<PartnerStatsResponse> {\n try {\n const response = await fetch(apiEndpoint, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n })\n\n const data = (await response.json()) as Record<string, unknown>\n\n if (!response.ok) {\n return {\n success: false,\n error: (data.error as string) || 'Failed to fetch partner stats',\n }\n }\n\n return {\n success: data.success as boolean,\n data: data.data as PartnerDashboardData,\n currency: data.currency as string,\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Network error'\n return { success: false, error: message }\n }\n}\n","import { roundTo2 } from './roundTo2'\n\nexport type CartLike = {\n subtotal?: number\n total?: number\n discountAmount?: number\n customerDiscount?: number\n}\n\n/**\n * Computes the cart total after applying plugin discounts.\n * Use this in your host app's cart beforeChange (or wherever you compute total)\n * so the amount always reflects coupon/referral discounts and is not overwritten incorrectly.\n *\n * Formula: subtotal - discountAmount - customerDiscount (each defaulting to 0).\n */\nexport function getCartTotalWithDiscounts(cart: CartLike): number {\n const subtotal = cart.subtotal ?? cart.total ?? 0\n const discountAmount = cart.discountAmount ?? 0\n const customerDiscount = cart.customerDiscount ?? 0\n return roundTo2(Math.max(0, subtotal - discountAmount - customerDiscount))\n}\n","import type { Payload } from 'payload'\n\nimport type { SanitizedCouponPluginOptions } from '../types'\n\nexport type OrderWithCouponFields = {\n id?: string\n appliedCoupon?: string | { id: string }\n appliedReferralCode?: string | { id: string }\n partnerCommission?: number\n customerDiscount?: number\n discountAmount?: number\n}\n\nexport type RecordCouponUsageResult = {\n recordedCoupon: boolean\n recordedReferral: boolean\n}\n\n/**\n * Record coupon and referral usage when an order is placed successfully.\n * Call this once when the order is created/paid (e.g. from Orders collection afterChange hook).\n *\n * - Coupon: increments the coupon's usageCount.\n * - Referral: increments the referral code's usageCount and successfulReferralsCount,\n * and adds order.partnerCommission to totalEarnings and pendingEarnings (partner gets commission;\n * referee discount is already on the order).\n */\nexport async function recordCouponUsageForOrder(\n payload: Payload,\n order: OrderWithCouponFields,\n pluginConfig: SanitizedCouponPluginOptions,\n): Promise<RecordCouponUsageResult> {\n const result: RecordCouponUsageResult = { recordedCoupon: false, recordedReferral: false }\n\n const couponId =\n order.appliedCoupon == null\n ? null\n : typeof order.appliedCoupon === 'string'\n ? order.appliedCoupon\n : order.appliedCoupon?.id\n\n const referralCodeId =\n order.appliedReferralCode == null\n ? null\n : typeof order.appliedReferralCode === 'string'\n ? order.appliedReferralCode\n : order.appliedReferralCode?.id\n\n if (couponId) {\n const coupon = await payload.findByID({\n collection: pluginConfig.collections.couponsSlug,\n id: couponId,\n })\n if (coupon) {\n await payload.update({\n collection: pluginConfig.collections.couponsSlug,\n id: couponId,\n data: {\n usageCount: (coupon.usageCount ?? 0) + 1,\n },\n })\n result.recordedCoupon = true\n }\n }\n\n if (referralCodeId) {\n const referralCode = await payload.findByID({\n collection: pluginConfig.collections.referralCodesSlug,\n id: referralCodeId,\n })\n if (referralCode) {\n const commission = Number(order.partnerCommission) || 0\n const currentTotal = Number((referralCode as any).totalEarnings) || 0\n const currentPending = Number((referralCode as any).pendingEarnings) || 0\n const currentUsageCount = Number((referralCode as any).usageCount) || 0\n const currentSuccessful = Number((referralCode as any).successfulReferralsCount) || 0\n\n await payload.update({\n collection: pluginConfig.collections.referralCodesSlug,\n id: referralCodeId,\n data: {\n usageCount: currentUsageCount + 1,\n successfulReferralsCount: currentSuccessful + 1,\n totalEarnings: currentTotal + commission,\n pendingEarnings: currentPending + commission,\n },\n })\n result.recordedReferral = true\n }\n }\n\n return result\n}\n"],"mappings":"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"}
|