From 0d792c7c70df90cf66242c625ae1a4bdf5a26ca3 Mon Sep 17 00:00:00 2001 From: Jennie Robinson Faber Date: Wed, 8 Apr 2026 22:03:42 +0100 Subject: [PATCH] refactor(members): use helcim helper + fix wrong card-lookup URL --- .../api/members/cancel-subscription.post.js | 32 +--- .../api/members/update-contribution.post.js | 156 ++++-------------- 2 files changed, 33 insertions(+), 155 deletions(-) diff --git a/server/api/members/cancel-subscription.post.js b/server/api/members/cancel-subscription.post.js index 6f6b405..b16d54a 100644 --- a/server/api/members/cancel-subscription.post.js +++ b/server/api/members/cancel-subscription.post.js @@ -1,12 +1,10 @@ // Cancel member subscription import Member from "../../models/member.js"; - -const HELCIM_API_BASE = "https://api.helcim.com/v2"; +import { cancelHelcimSubscription } from "../../utils/helcim.js"; export default defineEventHandler(async (event) => { try { const member = await requireAuth(event); - const config = useRuntimeConfig(event); // If already on free tier, nothing to cancel if (member.contributionTier === "0" || !member.helcimSubscriptionId) { @@ -18,32 +16,10 @@ export default defineEventHandler(async (event) => { }; } - const helcimToken = config.helcimApiToken; - try { - // Cancel Helcim subscription - const response = await fetch( - `${HELCIM_API_BASE}/subscriptions/${member.helcimSubscriptionId}`, - { - method: "DELETE", - headers: { - accept: "application/json", - "api-token": helcimToken, - }, - }, - ); - - if (!response.ok) { - const errorText = await response.text(); - console.error( - "Failed to cancel Helcim subscription:", - response.status, - errorText, - ); - // Continue anyway - we'll update the member record - } - } catch (error) { - console.error("Error canceling Helcim subscription:", error); + await cancelHelcimSubscription(member.helcimSubscriptionId); + } catch (cancelError) { + console.error("Error canceling Helcim subscription:", cancelError); // Continue anyway - we'll update the member record } diff --git a/server/api/members/update-contribution.post.js b/server/api/members/update-contribution.post.js index 5627340..fdd0bec 100644 --- a/server/api/members/update-contribution.post.js +++ b/server/api/members/update-contribution.post.js @@ -2,17 +2,23 @@ import { getHelcimPlanId, requiresPayment, + getContributionTierByValue, } from "../../config/contributions.js"; import { connectDB } from "../../utils/mongoose.js"; import Member from "../../models/member.js"; - -const HELCIM_API_BASE = "https://api.helcim.com/v2"; +import { + getHelcimCustomer, + listHelcimCustomerCards, + createHelcimSubscription, + updateHelcimSubscription, + cancelHelcimSubscription, + generateIdempotencyKey, +} from "../../utils/helcim.js"; export default defineEventHandler(async (event) => { try { const member = await requireAuth(event); await connectDB(); - const config = useRuntimeConfig(event); const body = await validateBody(event, updateContributionSchema); const oldTier = member.contributionTier; @@ -31,7 +37,6 @@ export default defineEventHandler(async (event) => { logActivity(member._id, 'contribution_changed', { from: oldTier, to: newTier }) } - const helcimToken = config.helcimApiToken; const oldRequiresPayment = requiresPayment(oldTier); const newRequiresPayment = requiresPayment(newTier); @@ -47,49 +52,17 @@ export default defineEventHandler(async (event) => { }); } - // Try to fetch customer info from Helcim to check for saved cards - const helcimToken = config.helcimApiToken; - try { - const customerResponse = await fetch( - `${HELCIM_API_BASE}/customers/${member.helcimCustomerId}`, - { - method: "GET", - headers: { - accept: "application/json", - "api-token": helcimToken, - }, - }, - ); - - if (!customerResponse.ok) { - throw new Error("Failed to fetch customer info"); - } - - const customerData = await customerResponse.json(); + const customerData = await getHelcimCustomer(member.helcimCustomerId); const customerCode = customerData.customerCode; if (!customerCode) { throw new Error("No customer code found"); } - // Check if customer has saved cards - const cardsResponse = await fetch( - `${HELCIM_API_BASE}/card-terminals?customerId=${member.helcimCustomerId}`, - { - method: "GET", - headers: { - accept: "application/json", - "api-token": helcimToken, - }, - }, - ); - - let hasCards = false; - if (cardsResponse.ok) { - const cardsData = await cardsResponse.json(); - hasCards = cardsData.cards && cardsData.cards.length > 0; - } + // Check for saved cards (FIX: use the correct endpoint) + const cards = await listHelcimCustomerCards(member.helcimCustomerId); + const hasCards = Array.isArray(cards) && cards.length > 0; if (!hasCards) { throw new Error("No saved payment methods"); @@ -105,53 +78,22 @@ export default defineEventHandler(async (event) => { }); } - // Generate idempotency key - const chars = - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - let idempotencyKey = ""; - for (let i = 0; i < 25; i++) { - idempotencyKey += chars.charAt( - Math.floor(Math.random() * chars.length), - ); - } + const idempotencyKey = generateIdempotencyKey(); // Get tier amount - const { getContributionTierByValue } = await import( - "../../config/contributions.js" - ); const tierInfo = getContributionTierByValue(newTier); - const subscriptionResponse = await fetch( - `${HELCIM_API_BASE}/subscriptions`, + const subscriptionData = await createHelcimSubscription( { - method: "POST", - headers: { - accept: "application/json", - "content-type": "application/json", - "api-token": helcimToken, - "idempotency-key": idempotencyKey, - }, - body: JSON.stringify({ - subscriptions: [ - { - dateActivated: new Date().toISOString().split("T")[0], - paymentPlanId: parseInt(newPlanId), - customerCode: customerCode, - recurringAmount: parseFloat(tierInfo.amount), - paymentMethod: "card", - }, - ], - }), + dateActivated: new Date().toISOString().split("T")[0], + paymentPlanId: parseInt(newPlanId), + customerCode: customerCode, + recurringAmount: parseFloat(tierInfo.amount), + paymentMethod: "card", }, + idempotencyKey, ); - if (!subscriptionResponse.ok) { - const errorText = await subscriptionResponse.text(); - console.error("Failed to create subscription:", errorText); - throw new Error('Subscription creation failed'); - } - - const subscriptionData = await subscriptionResponse.json(); const subscription = subscriptionData.data?.[0]; if (!subscription) { @@ -192,26 +134,9 @@ export default defineEventHandler(async (event) => { if (oldRequiresPayment && !newRequiresPayment) { if (member.helcimSubscriptionId) { try { - // Cancel Helcim subscription - const response = await fetch( - `${HELCIM_API_BASE}/subscriptions/${member.helcimSubscriptionId}`, - { - method: "DELETE", - headers: { - accept: "application/json", - "api-token": helcimToken, - }, - }, - ); - - if (!response.ok) { - console.error( - "Failed to cancel Helcim subscription:", - response.status, - ); - } - } catch (error) { - console.error("Error canceling Helcim subscription:", error); + await cancelHelcimSubscription(member.helcimSubscriptionId); + } catch (cancelError) { + console.error("Error canceling Helcim subscription:", cancelError); // Continue anyway - we'll update the member record } } @@ -253,34 +178,11 @@ export default defineEventHandler(async (event) => { } try { - // Update subscription plan in Helcim - const response = await fetch( - `${HELCIM_API_BASE}/subscriptions/${member.helcimSubscriptionId}`, - { - method: "PATCH", - headers: { - accept: "application/json", - "content-type": "application/json", - "api-token": helcimToken, - }, - body: JSON.stringify({ - paymentPlanId: parseInt(newPlanId), - }), - }, + const subscriptionData = await updateHelcimSubscription( + member.helcimSubscriptionId, + { paymentPlanId: parseInt(newPlanId) }, ); - if (!response.ok) { - const errorText = await response.text(); - console.error( - "Failed to update Helcim subscription:", - response.status, - errorText, - ); - throw new Error('Subscription update failed'); - } - - const subscriptionData = await response.json(); - // Update member record await Member.findByIdAndUpdate( member._id, @@ -295,8 +197,8 @@ export default defineEventHandler(async (event) => { message: "Successfully updated contribution level", subscription: subscriptionData, }; - } catch (error) { - console.error("Error updating Helcim subscription:", error); + } catch (updateError) { + console.error("Error updating Helcim subscription:", updateError); throw createError({ statusCode: 500, statusMessage: "Subscription update failed",