diff --git a/app/components/ImageUpload.vue b/app/components/ImageUpload.vue
index bd8a87e..7db4306 100644
--- a/app/components/ImageUpload.vue
+++ b/app/components/ImageUpload.vue
@@ -77,12 +77,7 @@
@@ -225,3 +220,16 @@ const updateAltText = (altText) => {
});
};
+
+
diff --git a/app/pages/admin/members/index.vue b/app/pages/admin/members/index.vue
index bce22e1..88f28b5 100644
--- a/app/pages/admin/members/index.vue
+++ b/app/pages/admin/members/index.vue
@@ -41,10 +41,11 @@
@@ -371,10 +372,11 @@
diff --git a/server/api/helcim/customer.post.js b/server/api/helcim/customer.post.js
index 2d09ff3..28ceda3 100644
--- a/server/api/helcim/customer.post.js
+++ b/server/api/helcim/customer.post.js
@@ -4,7 +4,7 @@ import { connectDB } from '../../utils/mongoose.js'
import { createHelcimCustomer } from '../../utils/helcim.js'
import PreRegistration from '../../models/preRegistration.js'
import { sendMagicLink } from '../../utils/magicLink.js'
-import { setPaymentBridgeCookie } from '../../utils/auth.js'
+import { setSignupBridgeCookie } from '../../utils/auth.js'
import { rateLimit } from '../../utils/rateLimit.js'
export default defineEventHandler(async (event) => {
@@ -116,10 +116,10 @@ export default defineEventHandler(async (event) => {
})
// Signup completes (paid checkout or free activation) before the magic
- // link is clicked, so issue a short-lived, payment-only bridge cookie
- // that lets /api/helcim/initialize-payment and /api/helcim/subscription
- // identify the member without a verified auth session.
- setPaymentBridgeCookie(event, member)
+ // link is clicked, so issue a short-lived signup-bridge cookie that lets
+ // /api/helcim/initialize-payment and /api/helcim/subscription identify
+ // the member without a verified auth session.
+ setSignupBridgeCookie(event, member)
return {
success: true,
diff --git a/server/api/helcim/initialize-payment.post.js b/server/api/helcim/initialize-payment.post.js
index a01b8d0..3826b08 100644
--- a/server/api/helcim/initialize-payment.post.js
+++ b/server/api/helcim/initialize-payment.post.js
@@ -2,7 +2,7 @@ import Member from '../../models/member.js'
import { loadPublicEvent } from '../../utils/loadEvent.js'
import { loadPublicSeries } from '../../utils/loadSeries.js'
import { calculateTicketPrice, calculateSeriesTicketPrice, hasMemberAccess } from '../../utils/tickets.js'
-import { requireAuth, getOptionalMember, getPaymentBridgeMember } from '../../utils/auth.js'
+import { requireAuth, getOptionalMember, getSignupBridgeMember } from '../../utils/auth.js'
import { initializeHelcimPaySession } from '../../utils/helcim.js'
export default defineEventHandler(async (event) => {
@@ -17,7 +17,7 @@ export default defineEventHandler(async (event) => {
if (!isTicket) {
if (isMembershipSignup) {
- const bridgeMember = await getPaymentBridgeMember(event)
+ const bridgeMember = await getSignupBridgeMember(event)
if (!bridgeMember) {
await requireAuth(event)
}
diff --git a/server/api/helcim/subscription.post.js b/server/api/helcim/subscription.post.js
index 577e264..d02846f 100644
--- a/server/api/helcim/subscription.post.js
+++ b/server/api/helcim/subscription.post.js
@@ -3,7 +3,7 @@ import { getHelcimPlanId, requiresPayment } from '../../config/contributions.js'
import Member from '../../models/member.js'
import { connectDB } from '../../utils/mongoose.js'
import { getSlackService } from '../../utils/slack.ts'
-import { requireAuth, getPaymentBridgeMember } from '../../utils/auth.js'
+import { requireAuth, getSignupBridgeMember } from '../../utils/auth.js'
import { createHelcimSubscription, generateIdempotencyKey, listHelcimCustomerTransactions } from '../../utils/helcim.js'
import { sendWelcomeEmail } from '../../utils/resend.js'
import { upsertPaymentFromHelcim } from '../../utils/payments.js'
@@ -11,8 +11,8 @@ import { upsertPaymentFromHelcim } from '../../utils/payments.js'
export default defineEventHandler(async (event) => {
try {
// Membership signup completes subscription before email verify; allow the
- // payment-bridge cookie set by /api/helcim/customer to satisfy auth here.
- const bridgeMember = await getPaymentBridgeMember(event)
+ // signup-bridge cookie set by /api/helcim/customer to satisfy auth here.
+ const bridgeMember = await getSignupBridgeMember(event)
if (!bridgeMember) {
await requireAuth(event)
}
diff --git a/server/utils/auth.js b/server/utils/auth.js
index 1876636..6603d6d 100644
--- a/server/utils/auth.js
+++ b/server/utils/auth.js
@@ -23,26 +23,27 @@ export function setAuthCookie(event, member) {
}
/**
- * Issue a 30-minute payment-bridge cookie scoped to membership-signup checkout.
+ * Issue a 30-minute signup-bridge cookie scoped to membership-signup flow.
*
* The signup flow (POST /api/helcim/customer) defers the full session cookie
- * to email-verify (magic link). For paid tiers the user still needs to complete
- * Helcim checkout in the same browser tab — this short-lived, payment-only
- * token lets `/api/helcim/initialize-payment` accept the call without a full
- * session. The cookie is NOT honored by requireAuth and grants nothing else.
+ * to email-verify (magic link). The bridge cookie lets the in-progress signup
+ * complete its activation step (free or paid) before that magic link is
+ * clicked: /api/helcim/subscription accepts it for $0 activation, and
+ * /api/helcim/initialize-payment accepts it for paid Helcim checkout.
+ * The cookie is NOT honored by requireAuth and grants nothing else.
*/
-export function setPaymentBridgeCookie(event, member) {
+export function setSignupBridgeCookie(event, member) {
const token = jwt.sign(
{
memberId: member._id.toString(),
email: member.email,
- scope: 'payment_bridge'
+ scope: 'signup_bridge'
},
useRuntimeConfig(event).jwtSecret,
{ expiresIn: '30m' }
)
- setCookie(event, 'payment-bridge', token, {
+ setCookie(event, 'signup-bridge', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
@@ -52,12 +53,12 @@ export function setPaymentBridgeCookie(event, member) {
}
/**
- * Verify a payment-bridge cookie and return the associated Member, or null.
- * Used by /api/helcim/initialize-payment to allow the membership-signup
- * checkout to proceed before email verification.
+ * Verify a signup-bridge cookie and return the associated Member, or null.
+ * Used by /api/helcim/subscription and /api/helcim/initialize-payment to
+ * let the in-progress signup complete activation before email verification.
*/
-export async function getPaymentBridgeMember(event) {
- const token = getCookie(event, 'payment-bridge')
+export async function getSignupBridgeMember(event) {
+ const token = getCookie(event, 'signup-bridge')
if (!token) return null
let decoded
@@ -67,7 +68,7 @@ export async function getPaymentBridgeMember(event) {
return null
}
- if (decoded.scope !== 'payment_bridge') return null
+ if (decoded.scope !== 'signup_bridge') return null
await connectDB()
const member = await Member.findById(decoded.memberId)
diff --git a/tests/server/api/activation-auto-flag.test.js b/tests/server/api/activation-auto-flag.test.js
index bcf7493..2895506 100644
--- a/tests/server/api/activation-auto-flag.test.js
+++ b/tests/server/api/activation-auto-flag.test.js
@@ -45,7 +45,7 @@ vi.mock('../../../server/models/preRegistration.js', () => ({
vi.mock('../../../server/utils/mongoose.js', () => ({ connectDB: vi.fn() }))
vi.mock('../../../server/utils/auth.js', () => ({
requireAuth: vi.fn(),
- getPaymentBridgeMember: vi.fn().mockResolvedValue(null),
+ getSignupBridgeMember: vi.fn().mockResolvedValue(null),
setAuthCookie: vi.fn()
}))
vi.mock('../../../server/utils/slack.ts', () => ({
diff --git a/tests/server/api/free-signup-flow.test.js b/tests/server/api/free-signup-flow.test.js
index bee73b3..b95b752 100644
--- a/tests/server/api/free-signup-flow.test.js
+++ b/tests/server/api/free-signup-flow.test.js
@@ -60,9 +60,9 @@ const SUBSCRIPTION_BODY = {
function extractBridgeCookie(event) {
const setCookie = event.node.res.getHeader('set-cookie')
const cookies = Array.isArray(setCookie) ? setCookie : [setCookie].filter(Boolean)
- const match = cookies.find(c => typeof c === 'string' && c.startsWith('payment-bridge='))
+ const match = cookies.find(c => typeof c === 'string' && c.startsWith('signup-bridge='))
if (!match) return null
- return match.match(/payment-bridge=([^;]+)/)[1]
+ return match.match(/signup-bridge=([^;]+)/)[1]
}
describe('signup → subscription bridge-cookie hand-off', () => {
@@ -104,7 +104,7 @@ describe('signup → subscription bridge-cookie hand-off', () => {
expect(result1.member.status).toBe('pending_payment')
const bridgeToken = extractBridgeCookie(customerEvent)
- expect(bridgeToken, 'payment-bridge cookie missing on $0 signup').toBeTruthy()
+ expect(bridgeToken, 'signup-bridge cookie missing on $0 signup').toBeTruthy()
Member.findOneAndUpdate.mockResolvedValue({ _id: MEMBER_ID, status: 'pending_payment' })
Member.findById.mockResolvedValue({
@@ -120,7 +120,7 @@ describe('signup → subscription bridge-cookie hand-off', () => {
method: 'POST',
path: '/api/helcim/subscription',
headers: { origin: ALLOWED_ORIGIN },
- cookies: { 'payment-bridge': bridgeToken },
+ cookies: { 'signup-bridge': bridgeToken },
body: SUBSCRIPTION_BODY
})
diff --git a/tests/server/api/helcim-customer.test.js b/tests/server/api/helcim-customer.test.js
index a023c27..2aa6ae3 100644
--- a/tests/server/api/helcim-customer.test.js
+++ b/tests/server/api/helcim-customer.test.js
@@ -3,7 +3,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'
import Member from '../../../server/models/member.js'
import { createHelcimCustomer } from '../../../server/utils/helcim.js'
import { sendMagicLink } from '../../../server/utils/magicLink.js'
-import { setAuthCookie, setPaymentBridgeCookie } from '../../../server/utils/auth.js'
+import { setAuthCookie, setSignupBridgeCookie } from '../../../server/utils/auth.js'
import customerHandler from '../../../server/api/helcim/customer.post.js'
import { resetRateLimit } from '../../../server/utils/rateLimit.js'
import { createMockEvent } from '../helpers/createMockEvent.js'
@@ -24,7 +24,7 @@ vi.mock('../../../server/utils/magicLink.js', () => ({
}))
vi.mock('../../../server/utils/auth.js', () => ({
setAuthCookie: vi.fn(),
- setPaymentBridgeCookie: vi.fn()
+ setSignupBridgeCookie: vi.fn()
}))
// helcimCustomerSchema is auto-imported in the handler — stub it to a passthrough
@@ -303,7 +303,7 @@ describe('POST /api/helcim/customer', () => {
'guest@example.com',
expect.objectContaining({ subject: 'Verify your Ghost Guild signup' })
)
- expect(setPaymentBridgeCookie).toHaveBeenCalled()
+ expect(setSignupBridgeCookie).toHaveBeenCalled()
expect(setAuthCookie).not.toHaveBeenCalled()
// Response shape mirrors new-signup case AND surfaces the preserved _id.
@@ -365,7 +365,7 @@ describe('POST /api/helcim/customer', () => {
)
})
- it('sets a payment-bridge cookie on paid-tier signup so checkout can proceed', async () => {
+ it('sets a signup-bridge cookie on paid-tier signup so checkout can proceed', async () => {
const event = build({
body: {
name: 'Paid User',
@@ -376,7 +376,7 @@ describe('POST /api/helcim/customer', () => {
}
})
await customerHandler(event)
- expect(setPaymentBridgeCookie).toHaveBeenCalled()
+ expect(setSignupBridgeCookie).toHaveBeenCalled()
expect(sendMagicLink).toHaveBeenCalledWith(
'paid@example.com',
expect.objectContaining({ subject: 'Verify your Ghost Guild signup' })
diff --git a/tests/server/api/helcim-subscription.test.js b/tests/server/api/helcim-subscription.test.js
index 8e6bc77..6845cd4 100644
--- a/tests/server/api/helcim-subscription.test.js
+++ b/tests/server/api/helcim-subscription.test.js
@@ -15,7 +15,7 @@ vi.mock('../../../server/models/member.js', () => ({
vi.mock('../../../server/utils/mongoose.js', () => ({ connectDB: vi.fn() }))
vi.mock('../../../server/utils/auth.js', () => ({
requireAuth: vi.fn(),
- getPaymentBridgeMember: vi.fn().mockResolvedValue(null)
+ getSignupBridgeMember: vi.fn().mockResolvedValue(null)
}))
vi.mock('../../../server/utils/slack.ts', () => ({
getSlackService: vi.fn().mockReturnValue(null)