refactor(config): cadence-keyed plan id, add getTierAmount, drop per-tier helcimPlanId

This commit is contained in:
Jennie Robinson Faber 2026-04-18 17:19:05 +01:00
parent 35197c465b
commit be0e6e7699
3 changed files with 64 additions and 159 deletions

View file

@ -1,82 +1,22 @@
// Central configuration for Ghost Guild Contribution Levels and Helcim Plans
// Central configuration for Ghost Guild Contribution Levels
export const CONTRIBUTION_TIERS = {
FREE: {
value: "0",
amount: 0,
label: "$0 - I need support right now",
tier: "free",
helcimPlanId: null, // No Helcim plan needed for free tier
},
SUPPORTER: {
value: "5",
amount: 5,
label: "$5 - I can contribute",
tier: "supporter",
helcimPlanId: "supporter-monthly-5",
},
MEMBER: {
value: "15",
amount: 15,
label: "$15 - I can sustain the community",
tier: "member",
helcimPlanId: "member-monthly-15",
},
ADVOCATE: {
value: "30",
amount: 30,
label: "$30 - I can support others too",
tier: "advocate",
helcimPlanId: "advocate-monthly-30",
},
CHAMPION: {
value: "50",
amount: 50,
label: "$50 - I want to sponsor multiple members",
tier: "champion",
helcimPlanId: "champion-monthly-50",
},
};
FREE: { value: "0", amount: 0, label: "$0 - I need support right now", tier: "free" },
SUPPORTER: { value: "5", amount: 5, label: "$5 - I can contribute", tier: "supporter" },
MEMBER: { value: "15", amount: 15, label: "$15 - I can sustain the community", tier: "member" },
ADVOCATE: { value: "30", amount: 30, label: "$30 - I can support others too", tier: "advocate" },
CHAMPION: { value: "50", amount: 50, label: "$50 - I want to sponsor multiple members", tier: "champion" },
}
// Get all contribution options as an array (useful for forms)
export const getContributionOptions = () => {
return Object.values(CONTRIBUTION_TIERS);
};
export const getContributionOptions = () => Object.values(CONTRIBUTION_TIERS)
export const getValidContributionValues = () => Object.values(CONTRIBUTION_TIERS).map(t => t.value)
export const getContributionTierByValue = (value) =>
Object.values(CONTRIBUTION_TIERS).find(t => t.value === value)
export const requiresPayment = (value) => (getContributionTierByValue(value)?.amount ?? 0) > 0
export const isValidContributionValue = (value) => getValidContributionValues().includes(value)
export const getPaidContributionTiers = () =>
Object.values(CONTRIBUTION_TIERS).filter(t => t.amount > 0)
// Get valid contribution values for validation
export const getValidContributionValues = () => {
return Object.values(CONTRIBUTION_TIERS).map((tier) => tier.value);
};
// Get contribution tier by value
export const getContributionTierByValue = (value) => {
return Object.values(CONTRIBUTION_TIERS).find((tier) => tier.value === value);
};
// Get Helcim plan ID for a contribution tier
export const getHelcimPlanId = (contributionValue) => {
const tier = getContributionTierByValue(contributionValue);
return tier?.helcimPlanId || null;
};
// Check if a contribution tier requires payment
export const requiresPayment = (contributionValue) => {
const tier = getContributionTierByValue(contributionValue);
return tier?.amount > 0;
};
// Check if a contribution value is valid
export const isValidContributionValue = (value) => {
return getValidContributionValues().includes(value);
};
// Get contribution tier by Helcim plan ID
export const getContributionTierByHelcimPlan = (helcimPlanId) => {
return Object.values(CONTRIBUTION_TIERS).find(
(tier) => tier.helcimPlanId === helcimPlanId,
);
};
// Get paid tiers only (excluding free tier)
export const getPaidContributionTiers = () => {
return Object.values(CONTRIBUTION_TIERS).filter((tier) => tier.amount > 0);
};
export function getTierAmount(tier, cadence = 'monthly') {
const base = parseFloat(tier.amount)
return cadence === 'annual' ? base * 10 : base
}

View file

@ -1,85 +1,30 @@
// Server-side contribution config
// Copy of the client-side config for server use
// Central configuration for Ghost Guild Contribution Levels and Helcim Plans
export const CONTRIBUTION_TIERS = {
FREE: {
value: "0",
amount: 0,
label: "$0 - I need support right now",
tier: "free",
helcimPlanId: null, // No Helcim plan needed for free tier
},
SUPPORTER: {
value: "5",
amount: 5,
label: "$5 - I can contribute",
tier: "supporter",
helcimPlanId: 20162,
},
MEMBER: {
value: "15",
amount: 15,
label: "$15 - I can sustain the community",
tier: "member",
helcimPlanId: 21596,
},
ADVOCATE: {
value: "30",
amount: 30,
label: "$30 - I can support others too",
tier: "advocate",
helcimPlanId: 21597,
},
CHAMPION: {
value: "50",
amount: 50,
label: "$50 - I want to sponsor multiple members",
tier: "champion",
helcimPlanId: 21598,
},
};
FREE: { value: "0", amount: 0, label: "$0 - I need support right now", tier: "free" },
SUPPORTER: { value: "5", amount: 5, label: "$5 - I can contribute", tier: "supporter" },
MEMBER: { value: "15", amount: 15, label: "$15 - I can sustain the community", tier: "member" },
ADVOCATE: { value: "30", amount: 30, label: "$30 - I can support others too", tier: "advocate" },
CHAMPION: { value: "50", amount: 50, label: "$50 - I want to sponsor multiple members", tier: "champion" },
}
// Get all contribution options as an array (useful for forms)
export const getContributionOptions = () => {
return Object.values(CONTRIBUTION_TIERS);
};
export const getContributionOptions = () => Object.values(CONTRIBUTION_TIERS)
export const getValidContributionValues = () => Object.values(CONTRIBUTION_TIERS).map(t => t.value)
export const getContributionTierByValue = (value) =>
Object.values(CONTRIBUTION_TIERS).find(t => t.value === value)
export const requiresPayment = (value) => (getContributionTierByValue(value)?.amount ?? 0) > 0
export const isValidContributionValue = (value) => getValidContributionValues().includes(value)
export const getPaidContributionTiers = () =>
Object.values(CONTRIBUTION_TIERS).filter(t => t.amount > 0)
// Get valid contribution values for validation
export const getValidContributionValues = () => {
return Object.values(CONTRIBUTION_TIERS).map((tier) => tier.value);
};
export function getTierAmount(tier, cadence = 'monthly') {
const base = parseFloat(tier.amount)
return cadence === 'annual' ? base * 10 : base
}
// Get contribution tier by value
export const getContributionTierByValue = (value) => {
return Object.values(CONTRIBUTION_TIERS).find((tier) => tier.value === value);
};
// Get Helcim plan ID for a contribution tier
export const getHelcimPlanId = (contributionValue) => {
const tier = getContributionTierByValue(contributionValue);
return tier?.helcimPlanId || null;
};
// Check if a contribution tier requires payment
export const requiresPayment = (contributionValue) => {
const tier = getContributionTierByValue(contributionValue);
return tier?.amount > 0;
};
// Check if a contribution value is valid
export const isValidContributionValue = (value) => {
return getValidContributionValues().includes(value);
};
// Get contribution tier by Helcim plan ID
export const getContributionTierByHelcimPlan = (helcimPlanId) => {
return Object.values(CONTRIBUTION_TIERS).find(
(tier) => tier.helcimPlanId === helcimPlanId,
);
};
// Get paid tiers only (excluding free tier)
export const getPaidContributionTiers = () => {
return Object.values(CONTRIBUTION_TIERS).filter((tier) => tier.amount > 0);
};
// Cadence-keyed plan-id lookup. Throws via createError at call site if env missing;
// this helper just returns the raw runtime-config value.
export function getHelcimPlanId(cadence) {
const config = useRuntimeConfig()
if (cadence === 'annual') return config.helcimAnnualPlanId || null
return config.helcimMonthlyPlanId || null
}

View file

@ -0,0 +1,20 @@
import { describe, it, expect } from 'vitest'
import { getTierAmount } from '../../../server/config/contributions.js'
describe('getTierAmount', () => {
it('returns monthly amount as-is', () => {
expect(getTierAmount({ amount: 5 }, 'monthly')).toBe(5)
})
it('returns annual amount as base * 10', () => {
expect(getTierAmount({ amount: 5 }, 'annual')).toBe(50)
})
it('returns annual amount for $50 tier', () => {
expect(getTierAmount({ amount: 50 }, 'annual')).toBe(500)
})
it('defaults cadence to monthly', () => {
expect(getTierAmount({ amount: 15 })).toBe(15)
})
})