refactor(members): use helcim helper + fix wrong card-lookup URL

This commit is contained in:
Jennie Robinson Faber 2026-04-08 22:03:42 +01:00
parent 03d6a66b84
commit 0d792c7c70
2 changed files with 33 additions and 155 deletions

View file

@ -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",