diff --git a/server/api/members/update-contribution.post.js b/server/api/members/update-contribution.post.js index e99c217..695e7c9 100644 --- a/server/api/members/update-contribution.post.js +++ b/server/api/members/update-contribution.post.js @@ -12,7 +12,9 @@ import { updateHelcimSubscription, cancelHelcimSubscription, generateIdempotencyKey, + listHelcimCustomerTransactions, } from "../../utils/helcim.js"; +import { upsertPaymentFromHelcim } from "../../utils/payments.js"; export default defineEventHandler(async (event) => { try { @@ -118,6 +120,27 @@ export default defineEventHandler(async (event) => { { runValidators: false } ); + try { + const txs = await listHelcimCustomerTransactions(customerCode); + const latestPaid = txs.find((t) => t.status === 'paid'); + if (latestPaid) { + await upsertPaymentFromHelcim( + { + _id: member._id, + email: member.email, + name: member.name, + helcimCustomerId: member.helcimCustomerId, + helcimSubscriptionId: subscription.id, + billingCadence: cadence, + }, + latestPaid, + { paymentType: cadence, sendConfirmation: true } + ); + } + } catch (err) { + console.error('[payments] free→paid charge log failed, will be picked up by reconciliation:', err?.message || err); + } + logContributionChange(); return { diff --git a/tests/server/api/update-contribution.test.js b/tests/server/api/update-contribution.test.js index 3b60bed..a62e243 100644 --- a/tests/server/api/update-contribution.test.js +++ b/tests/server/api/update-contribution.test.js @@ -11,7 +11,9 @@ import { listHelcimCustomerCards, createHelcimSubscription, cancelHelcimSubscription, + listHelcimCustomerTransactions, } from '../../../server/utils/helcim.js' +import { upsertPaymentFromHelcim } from '../../../server/utils/payments.js' import handler from '../../../server/api/members/update-contribution.post.js' import { createMockEvent } from '../helpers/createMockEvent.js' @@ -31,6 +33,10 @@ vi.mock('../../../server/utils/helcim.js', () => ({ updateHelcimSubscription: vi.fn(), cancelHelcimSubscription: vi.fn(), generateIdempotencyKey: vi.fn().mockReturnValue('idem-key-123'), + listHelcimCustomerTransactions: vi.fn().mockResolvedValue([]), +})) +vi.mock('../../../server/utils/payments.js', () => ({ + upsertPaymentFromHelcim: vi.fn().mockResolvedValue({ created: true, payment: { _id: 'p1' } }) })) // Nitro auto-imports @@ -224,6 +230,9 @@ describe('update-contribution endpoint — Case 1 (free→paid)', () => { listHelcimCustomerCards.mockResolvedValue([{ id: 'card-1' }]) createHelcimSubscription.mockResolvedValue({ data: [{ id: 'sub-new', status: 'active', nextBillingDate: '2026-05-18' }] }) Member.findByIdAndUpdate.mockResolvedValue({}) + listHelcimCustomerTransactions.mockResolvedValueOnce([ + { id: 'tx-upgrade', date: '2026-04-20', amount: 15, status: 'paid', currency: 'CAD' } + ]) const event = createMockEvent({ method: 'POST', @@ -242,6 +251,12 @@ describe('update-contribution endpoint — Case 1 (free→paid)', () => { { $set: expect.objectContaining({ billingCadence: 'monthly', contributionAmount: 15, helcimSubscriptionId: 'sub-new' }) }, { runValidators: false } ) + expect(listHelcimCustomerTransactions).toHaveBeenCalledWith('code-1') + expect(upsertPaymentFromHelcim).toHaveBeenCalledWith( + expect.objectContaining({ _id: 'member-c1', helcimSubscriptionId: 'sub-new', billingCadence: 'monthly' }), + expect.objectContaining({ id: 'tx-upgrade' }), + { paymentType: 'monthly', sendConfirmation: true } + ) expect(result.success).toBe(true) expect(result.message).toBe('Successfully upgraded to paid tier') })