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
Helcim returns next-charge as `dateBilling` on POST /subscriptions, but the
two CREATE sites were reading `subscription.nextBillingDate`, leaving
`member.nextBillingDate` empty after every signup and free→paid upgrade.
The lazy refresh in subscription.get.js (which already accepts both shapes)
masked it on next account-page load, so renders eventually populated — but
the success response we returned to the client also had `nextBillingDate:
undefined`. Mirror the GET-side resolution at both CREATE sites: prefer
`dateBilling`, fall back to `nextBillingDate`. Existing Number.isNaN guard
unchanged; defensively rejects malformed strings from either field.
In the Case 1 (free→paid) branch of update-contribution, after the
subscription is created and the member row is updated, fetch the
newest paid Helcim transaction and upsert a Payment with
paymentType=cadence and sendConfirmation=true.
Paid→paid (Case 3) is intentionally NOT wired — no new transaction
occurs at amount change; the next recurring charge is captured by the
reconciliation script.
Persist nextBillingDate on subscription create/update; unset on
cancel or downgrade to free. Account page displays the cached
date and lazily refreshes from Helcim when the cached value is
within 24h of now (or missing).