
# 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`

```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`

```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


```js
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


```js
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


```js
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.*
