Service Library - subscriptionManagement
This document provides a complete reference of the custom code library for the subscriptionManagement service. It includes all library functions, edge functions with their REST endpoints, templates, and assets.
Library Functions
Library functions are reusable modules available to all business APIs and other custom code within the service via require("lib/<moduleName>").
subscriptionCheckNoActive.js
const db = require("dbLayer");
module.exports = async function subscriptionCheckNoActive(companyId) {
if (!companyId) throw new Error("companyId is required");
// Check if company already has an active or pending (awaiting payment) subscription
const existing = await db.getCompanySubscriptionListByMQuery({
companyId,
status: { $in: ['active', 'pending'] },
isActive: true
});
if (existing && existing.length > 0) {
throw new Error('An active or pending subscription already exists for this company. Cancel the existing one first.');
}
return true;
};
subscriptionValidateUpdate.js
// DEPRECATED: No longer used. Subscription updates are now restricted to superAdmin/saasAdmin only.
module.exports = async function subscriptionValidateUpdate() {
return true;
};
Edge Functions
Edge functions are custom HTTP endpoint handlers that run outside the standard Business API pipeline. Each edge function is paired with an Edge Controller that defines its REST endpoint.
onSubscriptionPaymentDone.js
Edge Controller:
- Path: ``
- Method:
GET - Login Required: No
const db = require("dbLayer");
module.exports = async (request) => {
// This edge function is triggered by Stripe after successful payment.
// The request contains the companySubscription order data.
const subscription = request.companySubscription;
console.log('onSubscriptionPaymentDone: Called with subscription:', JSON.stringify(subscription, null, 2));
if (!subscription || !subscription.id) {
console.error('onSubscriptionPaymentDone: No subscription data in request');
return;
}
// Check payment status - check both paymentStatus and paymentConfirmation fields
const paymentStatus = subscription.paymentStatus || subscription.paymentConfirmation;
console.log('onSubscriptionPaymentDone: Payment status found:', paymentStatus);
if (paymentStatus !== 'paid') {
console.log('onSubscriptionPaymentDone: Payment status is not paid, skipping activation. Status:', paymentStatus);
return;
}
const now = new Date();
const expiryDate = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000); // 30 days from now
try {
console.log('onSubscriptionPaymentDone: Activating subscription', subscription.id);
const updateData = {
status: 'active',
activationDate: now,
expiryDate: expiryDate,
subscribedFeatures: ['aiInsights', 'aiWorkforceAnalytics']
};
console.log('onSubscriptionPaymentDone: Update data:', JSON.stringify(updateData, null, 2));
const result = await db.updateCompanySubscriptionById(subscription.id, updateData);
console.log('onSubscriptionPaymentDone: Subscription activated successfully. Result:', JSON.stringify(result, null, 2));
console.log('onSubscriptionPaymentDone: Subscription activated for company', subscription.companyId, 'until', expiryDate);
} catch (err) {
console.error('onSubscriptionPaymentDone: Failed to activate subscription', err);
console.error('onSubscriptionPaymentDone: Error stack:', err.stack);
}
};
handlePaymentSuccess.js
Edge Controller:
- Path: ``
- Method:
GET - Login Required: No
module.exports = async (eventPayload) => {
const { paymentData, companySubscription } = eventPayload;
console.log('[handlePaymentSuccess] Received payment success event:', JSON.stringify({
subscriptionId: companySubscription?.id,
paymentStatus: paymentData?.status,
paymentId: paymentData?.paymentId
}));
if (!companySubscription || !companySubscription.id) {
console.error('[handlePaymentSuccess] No subscription ID in event payload');
return { success: false, error: 'No subscription ID' };
}
try {
// Update subscription status to active using M2M API
const db = require('dbLayer');
const updateResult = await db.updateCompanySubscriptionById(
companySubscription.id,
{
status: 'active',
paymentStatus: 'paid',
paymentConfirmation: 'paid',
paymentStatusUpdatedAt: new Date()
}
);
console.log('[handlePaymentSuccess] Subscription updated successfully:', {
subscriptionId: companySubscription.id,
updateResult
});
return {
success: true,
message: 'Subscription activated',
subscriptionId: companySubscription.id
};
} catch (error) {
console.error('[handlePaymentSuccess] Error updating subscription:', error.message);
return {
success: false,
error: error.message
};
}
};
deletePaymentMethod.js
Edge Controller:
- Path:
/payment-methods/delete/:paymentMethodId - Method:
GET - Login Required: Yes
const db = require("dbLayer");
const { getIntegrationClient } = require("integrations");
module.exports = async (context) => {
const { paymentMethodId } = context.params;
const userId = context.session.userId;
const companyId = context.session.companyId;
try {
// Find the payment method in local DB by LOCAL ID (UUID)
const paymentMethod = await db.getSys_paymentMethodById(paymentMethodId);
if (!paymentMethod) {
const error = new Error("Payment method not found");
error.status = 404;
throw error;
}
// Verify ownership
if (
paymentMethod.userId !== userId ||
paymentMethod.companyId !== companyId
) {
const error = new Error("Payment method not owned by user");
error.status = 403;
throw error;
}
// Get Stripe client from integrations
const stripe = await getIntegrationClient("stripe");
// Detach from Stripe using the Stripe paymentMethodId
await stripe.paymentMethods.detach(paymentMethod.paymentMethodId);
// Delete from local DB
await db.deleteSys_paymentMethodById(paymentMethodId);
return {
result: "OK",
message: "Payment method deleted successfully",
deletedPaymentMethodId: paymentMethodId,
};
} catch (error) {
console.error("Error deleting payment method:", error);
throw error;
}
};
Edge Controllers Summary
| Function Name | Method | Path | Login Required |
|---|---|---|---|
handlePaymentSuccess |
GET |
`` | No |
deletePaymentMethod |
GET |
/payment-methods/delete/:paymentMethodId |
Yes |
onSubscriptionPaymentDone |
GET |
`` | No |
This document was generated from the service library configuration and should be kept in sync with design changes.