refactor(members): use helcim helper + fix wrong card-lookup URL
This commit is contained in:
parent
03d6a66b84
commit
0d792c7c70
2 changed files with 33 additions and 155 deletions
|
|
@ -1,12 +1,10 @@
|
||||||
// Cancel member subscription
|
// Cancel member subscription
|
||||||
import Member from "../../models/member.js";
|
import Member from "../../models/member.js";
|
||||||
|
import { cancelHelcimSubscription } from "../../utils/helcim.js";
|
||||||
const HELCIM_API_BASE = "https://api.helcim.com/v2";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
try {
|
try {
|
||||||
const member = await requireAuth(event);
|
const member = await requireAuth(event);
|
||||||
const config = useRuntimeConfig(event);
|
|
||||||
|
|
||||||
// If already on free tier, nothing to cancel
|
// If already on free tier, nothing to cancel
|
||||||
if (member.contributionTier === "0" || !member.helcimSubscriptionId) {
|
if (member.contributionTier === "0" || !member.helcimSubscriptionId) {
|
||||||
|
|
@ -18,32 +16,10 @@ export default defineEventHandler(async (event) => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const helcimToken = config.helcimApiToken;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Cancel Helcim subscription
|
await cancelHelcimSubscription(member.helcimSubscriptionId);
|
||||||
const response = await fetch(
|
} catch (cancelError) {
|
||||||
`${HELCIM_API_BASE}/subscriptions/${member.helcimSubscriptionId}`,
|
console.error("Error canceling Helcim subscription:", cancelError);
|
||||||
{
|
|
||||||
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);
|
|
||||||
// Continue anyway - we'll update the member record
|
// Continue anyway - we'll update the member record
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,23 @@
|
||||||
import {
|
import {
|
||||||
getHelcimPlanId,
|
getHelcimPlanId,
|
||||||
requiresPayment,
|
requiresPayment,
|
||||||
|
getContributionTierByValue,
|
||||||
} from "../../config/contributions.js";
|
} from "../../config/contributions.js";
|
||||||
import { connectDB } from "../../utils/mongoose.js";
|
import { connectDB } from "../../utils/mongoose.js";
|
||||||
import Member from "../../models/member.js";
|
import Member from "../../models/member.js";
|
||||||
|
import {
|
||||||
const HELCIM_API_BASE = "https://api.helcim.com/v2";
|
getHelcimCustomer,
|
||||||
|
listHelcimCustomerCards,
|
||||||
|
createHelcimSubscription,
|
||||||
|
updateHelcimSubscription,
|
||||||
|
cancelHelcimSubscription,
|
||||||
|
generateIdempotencyKey,
|
||||||
|
} from "../../utils/helcim.js";
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
try {
|
try {
|
||||||
const member = await requireAuth(event);
|
const member = await requireAuth(event);
|
||||||
await connectDB();
|
await connectDB();
|
||||||
const config = useRuntimeConfig(event);
|
|
||||||
const body = await validateBody(event, updateContributionSchema);
|
const body = await validateBody(event, updateContributionSchema);
|
||||||
|
|
||||||
const oldTier = member.contributionTier;
|
const oldTier = member.contributionTier;
|
||||||
|
|
@ -31,7 +37,6 @@ export default defineEventHandler(async (event) => {
|
||||||
logActivity(member._id, 'contribution_changed', { from: oldTier, to: newTier })
|
logActivity(member._id, 'contribution_changed', { from: oldTier, to: newTier })
|
||||||
}
|
}
|
||||||
|
|
||||||
const helcimToken = config.helcimApiToken;
|
|
||||||
const oldRequiresPayment = requiresPayment(oldTier);
|
const oldRequiresPayment = requiresPayment(oldTier);
|
||||||
const newRequiresPayment = requiresPayment(newTier);
|
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 {
|
try {
|
||||||
const customerResponse = await fetch(
|
const customerData = await getHelcimCustomer(member.helcimCustomerId);
|
||||||
`${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 customerCode = customerData.customerCode;
|
const customerCode = customerData.customerCode;
|
||||||
|
|
||||||
if (!customerCode) {
|
if (!customerCode) {
|
||||||
throw new Error("No customer code found");
|
throw new Error("No customer code found");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if customer has saved cards
|
// Check for saved cards (FIX: use the correct endpoint)
|
||||||
const cardsResponse = await fetch(
|
const cards = await listHelcimCustomerCards(member.helcimCustomerId);
|
||||||
`${HELCIM_API_BASE}/card-terminals?customerId=${member.helcimCustomerId}`,
|
const hasCards = Array.isArray(cards) && cards.length > 0;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasCards) {
|
if (!hasCards) {
|
||||||
throw new Error("No saved payment methods");
|
throw new Error("No saved payment methods");
|
||||||
|
|
@ -105,34 +78,12 @@ export default defineEventHandler(async (event) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate idempotency key
|
const idempotencyKey = generateIdempotencyKey();
|
||||||
const chars =
|
|
||||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
||||||
let idempotencyKey = "";
|
|
||||||
for (let i = 0; i < 25; i++) {
|
|
||||||
idempotencyKey += chars.charAt(
|
|
||||||
Math.floor(Math.random() * chars.length),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get tier amount
|
// Get tier amount
|
||||||
const { getContributionTierByValue } = await import(
|
|
||||||
"../../config/contributions.js"
|
|
||||||
);
|
|
||||||
const tierInfo = getContributionTierByValue(newTier);
|
const tierInfo = getContributionTierByValue(newTier);
|
||||||
|
|
||||||
const subscriptionResponse = await fetch(
|
const subscriptionData = await createHelcimSubscription(
|
||||||
`${HELCIM_API_BASE}/subscriptions`,
|
|
||||||
{
|
|
||||||
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],
|
dateActivated: new Date().toISOString().split("T")[0],
|
||||||
paymentPlanId: parseInt(newPlanId),
|
paymentPlanId: parseInt(newPlanId),
|
||||||
|
|
@ -140,18 +91,9 @@ export default defineEventHandler(async (event) => {
|
||||||
recurringAmount: parseFloat(tierInfo.amount),
|
recurringAmount: parseFloat(tierInfo.amount),
|
||||||
paymentMethod: "card",
|
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];
|
const subscription = subscriptionData.data?.[0];
|
||||||
|
|
||||||
if (!subscription) {
|
if (!subscription) {
|
||||||
|
|
@ -192,26 +134,9 @@ export default defineEventHandler(async (event) => {
|
||||||
if (oldRequiresPayment && !newRequiresPayment) {
|
if (oldRequiresPayment && !newRequiresPayment) {
|
||||||
if (member.helcimSubscriptionId) {
|
if (member.helcimSubscriptionId) {
|
||||||
try {
|
try {
|
||||||
// Cancel Helcim subscription
|
await cancelHelcimSubscription(member.helcimSubscriptionId);
|
||||||
const response = await fetch(
|
} catch (cancelError) {
|
||||||
`${HELCIM_API_BASE}/subscriptions/${member.helcimSubscriptionId}`,
|
console.error("Error canceling Helcim subscription:", cancelError);
|
||||||
{
|
|
||||||
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);
|
|
||||||
// Continue anyway - we'll update the member record
|
// Continue anyway - we'll update the member record
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -253,34 +178,11 @@ export default defineEventHandler(async (event) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Update subscription plan in Helcim
|
const subscriptionData = await updateHelcimSubscription(
|
||||||
const response = await fetch(
|
member.helcimSubscriptionId,
|
||||||
`${HELCIM_API_BASE}/subscriptions/${member.helcimSubscriptionId}`,
|
{ paymentPlanId: parseInt(newPlanId) },
|
||||||
{
|
|
||||||
method: "PATCH",
|
|
||||||
headers: {
|
|
||||||
accept: "application/json",
|
|
||||||
"content-type": "application/json",
|
|
||||||
"api-token": helcimToken,
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
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
|
// Update member record
|
||||||
await Member.findByIdAndUpdate(
|
await Member.findByIdAndUpdate(
|
||||||
member._id,
|
member._id,
|
||||||
|
|
@ -295,8 +197,8 @@ export default defineEventHandler(async (event) => {
|
||||||
message: "Successfully updated contribution level",
|
message: "Successfully updated contribution level",
|
||||||
subscription: subscriptionData,
|
subscription: subscriptionData,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (updateError) {
|
||||||
console.error("Error updating Helcim subscription:", error);
|
console.error("Error updating Helcim subscription:", updateError);
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 500,
|
statusCode: 500,
|
||||||
statusMessage: "Subscription update failed",
|
statusMessage: "Subscription update failed",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue