fix(server): treat contributionAmount as cadence-unit (drop ×12)

ContributionAmountField now emits cadence-unit values (180 for $180/yr,
15 for $15/mo). Server endpoints were still multiplying annual by 12,
which would have charged $2160/yr instead of $180/yr after the form
ports in Tasks 2–3.

- helcim/subscription.post.js: recurringAmount = contributionAmount
  (no more × 12 for annual)
- members/update-contribution.post.js: same drop in both Case 1
  (free→paid) and Case 3 (paid→paid)
- slack.ts notifyNewMember: new positional `cadence` param so the
  Slack notification suffix renders /yr or /mo instead of hardcoded
  /month; all three call sites updated to pass member.billingCadence
- tests updated to match the new contract:
  - helcim-subscription.test.js: annual tests now send the cadence-
    unit amount (180, 600) and expect the same recurringAmount
  - update-contribution.test.js: annual Case 1 and Case 3 tests
    updated likewise
This commit is contained in:
Jennie Robinson Faber 2026-05-23 15:37:52 +01:00
parent e0e7da5cca
commit 5023fb14ad
6 changed files with 24 additions and 22 deletions

View file

@ -83,10 +83,10 @@ describe('update-contribution endpoint — Case 3 (paid→paid)', () => {
expect(result.message).toBe('Successfully updated contribution level')
})
it('annual $5 → $15: calls updateHelcimSubscription with recurringAmount 180', async () => {
it('annual $60 → $180: calls updateHelcimSubscription with recurringAmount 180', async () => {
const mockMember = {
_id: 'member-2',
contributionAmount: 5,
contributionAmount: 60,
helcimSubscriptionId: 'sub-2',
billingCadence: 'annual',
}
@ -97,7 +97,7 @@ describe('update-contribution endpoint — Case 3 (paid→paid)', () => {
const event = createMockEvent({
method: 'POST',
path: '/api/members/update-contribution',
body: { contributionAmount: 15, cadence: 'annual' },
body: { contributionAmount: 180, cadence: 'annual' },
})
const result = await handler(event)
@ -105,16 +105,16 @@ describe('update-contribution endpoint — Case 3 (paid→paid)', () => {
expect(updateHelcimSubscription).toHaveBeenCalledWith('sub-2', { recurringAmount: 180 })
expect(Member.findByIdAndUpdate).toHaveBeenCalledWith(
'member-2',
{ $set: { contributionAmount: 15 } },
{ $set: { contributionAmount: 180 } },
{ runValidators: false }
)
expect(result.success).toBe(true)
})
it('annual $15 → $50: calls updateHelcimSubscription with recurringAmount 600', async () => {
it('annual $180 → $600: calls updateHelcimSubscription with recurringAmount 600', async () => {
const mockMember = {
_id: 'member-3',
contributionAmount: 15,
contributionAmount: 180,
helcimSubscriptionId: 'sub-3',
billingCadence: 'annual',
}
@ -125,7 +125,7 @@ describe('update-contribution endpoint — Case 3 (paid→paid)', () => {
const event = createMockEvent({
method: 'POST',
path: '/api/members/update-contribution',
body: { contributionAmount: 50, cadence: 'annual' },
body: { contributionAmount: 600, cadence: 'annual' },
})
await handler(event)
@ -296,7 +296,7 @@ describe('update-contribution endpoint — Case 1 (free→paid)', () => {
const event = createMockEvent({
method: 'POST',
path: '/api/members/update-contribution',
body: { contributionAmount: 15, cadence: 'annual' },
body: { contributionAmount: 180, cadence: 'annual' },
})
const result = await handler(event)
@ -307,7 +307,7 @@ describe('update-contribution endpoint — Case 1 (free→paid)', () => {
)
expect(Member.findByIdAndUpdate).toHaveBeenCalledWith(
'member-c1',
{ $set: expect.objectContaining({ billingCadence: 'annual', contributionAmount: 15, helcimSubscriptionId: 'sub-annual' }) },
{ $set: expect.objectContaining({ billingCadence: 'annual', contributionAmount: 180, helcimSubscriptionId: 'sub-annual' }) },
{ runValidators: false }
)
expect(result.success).toBe(true)