417 lines
11 KiB
Markdown
417 lines
11 KiB
Markdown
# Constants Reference Guide
|
|
|
|
## Overview
|
|
|
|
This document provides a comprehensive reference for all frozen constants defined in `helpers/constants.js`. These constants ensure type safety and consistency across the application.
|
|
|
|
**Last Updated**: January 28, 2026
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
- [Promo System Constants](#promo-system-constants)
|
|
- [Stripe Constants](#stripe-constants)
|
|
- [Error Codes](#error-codes)
|
|
- [User and Partner Constants](#user-and-partner-constants)
|
|
- [Other Constants](#other-constants)
|
|
|
|
---
|
|
|
|
## Promo System Constants
|
|
|
|
### PromoModes
|
|
|
|
Controls whether the promotional system is globally enabled or disabled (kill switch).
|
|
|
|
```javascript
|
|
const PromoModes = Object.freeze({
|
|
ENABLED: 'enabled', // Promotions enabled (targeting controlled by PromoEligibility)
|
|
DISABLED: 'disabled' // Kill switch: disable all automatic promos
|
|
});
|
|
```
|
|
|
|
**Usage**:
|
|
```javascript
|
|
const { PromoModes } = require('./helpers/constants');
|
|
|
|
if (env.PROMO_MODE === PromoModes.DISABLED) {
|
|
return res.json({ promos: [] });
|
|
}
|
|
```
|
|
|
|
**Environment Variable**: `PROMO_MODE=enabled` (default)
|
|
|
|
**Related Documentation**: [PROMO_ENHANCEMENTS_V3.md](./PROMO_ENHANCEMENTS_V3.md)
|
|
|
|
---
|
|
|
|
### PromoEligibility
|
|
|
|
Defines customer eligibility criteria for promotional offers.
|
|
|
|
```javascript
|
|
const PromoEligibility = Object.freeze({
|
|
ALL: 'all', // Any customer can use promo
|
|
NEW_ONLY: 'new_only', // Only first-time customers (no subscription history)
|
|
RENEW_ONLY: 'renew_only' // Only returning customers (has subscription history)
|
|
});
|
|
```
|
|
|
|
**Usage**:
|
|
```javascript
|
|
const { PromoEligibility } = require('./helpers/constants');
|
|
|
|
const promo = {
|
|
eligibility: PromoEligibility.NEW_ONLY, // Only for new customers
|
|
// ...
|
|
};
|
|
```
|
|
|
|
**Related Documentation**: [PROMO_ENHANCEMENTS_V2.md](./PROMO_ENHANCEMENTS_V2.md)
|
|
|
|
---
|
|
|
|
### CouponDuration
|
|
|
|
Stripe coupon duration types supported in the promotional system.
|
|
|
|
```javascript
|
|
const CouponDuration = Object.freeze({
|
|
FOREVER: 'forever', // Coupon applies indefinitely
|
|
REPEATING: 'repeating', // Coupon applies for N months (uses durationInMonths)
|
|
ONCE: 'once' // Coupon applies once (NOT supported in V2+)
|
|
});
|
|
```
|
|
|
|
**Usage**:
|
|
```javascript
|
|
const { CouponDuration } = require('./helpers/constants');
|
|
|
|
if (stripeCoupon.duration === CouponDuration.REPEATING) {
|
|
promo.durationInMonths = stripeCoupon.duration_in_months;
|
|
}
|
|
|
|
// Validation example
|
|
if (stripeCoupon.duration === CouponDuration.ONCE) {
|
|
throw new AppParamError(Errors.INVALID_PARAM, 'ONCE duration not supported');
|
|
}
|
|
```
|
|
|
|
**Files Using This Constant**:
|
|
- `controllers/main.js` - Coupon validation in `addSubscriptionPromo_post()`
|
|
- `controllers/subscription.js` - Promo application logic
|
|
|
|
---
|
|
|
|
## Stripe Constants
|
|
|
|
### StripeErrorTypes
|
|
|
|
Stripe API error type identifiers for consistent error handling.
|
|
|
|
```javascript
|
|
const StripeErrorTypes = Object.freeze({
|
|
CARD_ERROR: 'StripeCardError', // Card-related errors (declined, insufficient funds, etc.)
|
|
INVALID_REQUEST: 'StripeInvalidRequestError', // Invalid parameters or request
|
|
API_ERROR: 'StripeAPIError', // Stripe API errors
|
|
CONNECTION_ERROR: 'StripeConnectionError', // Network connection errors
|
|
AUTHENTICATION_ERROR: 'StripeAuthenticationError', // Authentication errors
|
|
RATE_LIMIT_ERROR: 'StripeRateLimitError' // Rate limiting errors
|
|
});
|
|
```
|
|
|
|
**Usage**:
|
|
```javascript
|
|
const { StripeErrorTypes } = require('./helpers/constants');
|
|
|
|
try {
|
|
const stripeCoupon = await stripe.coupons.retrieve(couponId);
|
|
} catch (stripeError) {
|
|
if (stripeError.type === StripeErrorTypes.INVALID_REQUEST) {
|
|
throw new AppParamError(Errors.INVALID_PARAM, `Invalid coupon: ${stripeError.message}`);
|
|
}
|
|
if (stripeError.type === StripeErrorTypes.CARD_ERROR) {
|
|
throw new AppMembershipError(Errors.PAYMENT_FAILED, `Card error: ${stripeError.message}`);
|
|
}
|
|
throw stripeError; // Re-throw other errors
|
|
}
|
|
```
|
|
|
|
**Files Using This Constant**:
|
|
- `controllers/main.js` - Coupon retrieval error handling
|
|
- `controllers/subscription.js` - SetupIntent and payment error handling
|
|
- `model/customer.js` - Customer deletion error handling
|
|
- `tests/test_setup_intent.js` - Card validation testing
|
|
|
|
**Related Documentation**: [PAYMENT_FAILURE_HANDLING.md](./PAYMENT_FAILURE_HANDLING.md)
|
|
|
|
---
|
|
|
|
## Error Codes
|
|
|
|
### Promo Error Codes
|
|
|
|
Specific error codes for promotional system operations.
|
|
|
|
```javascript
|
|
const Errors = Object.freeze({
|
|
// ... existing error codes ...
|
|
|
|
// Promo error codes
|
|
PROMO_NOT_FOUND: 'promo_not_found',
|
|
PROMO_IN_USE_VALID_UNTIL_REQUIRED: 'promo_in_use_valid_until_required',
|
|
PROMO_VALID_UNTIL_TOO_SOON: 'promo_valid_until_too_soon',
|
|
PROMO_INVALID_VALID_UNTIL: 'promo_invalid_valid_until',
|
|
PROMO_DUPLICATE_TYPE_PRICEKEY: 'promo_duplicate_type_pricekey',
|
|
PROMO_DUPLICATE_COUPON: 'promo_duplicate_coupon',
|
|
PROMO_OVERLAPPING_DATES: 'promo_overlapping_dates',
|
|
PROMO_COUPON_NOT_FOUND: 'promo_coupon_not_found',
|
|
PROMO_INVALID_COUPON: 'promo_invalid_coupon',
|
|
});
|
|
```
|
|
|
|
**Usage Examples**:
|
|
|
|
**1. Invalid Coupon/Promotion Code**:
|
|
```javascript
|
|
const { Errors } = require('./helpers/constants');
|
|
|
|
// Invalid or restricted coupon/promotion code
|
|
if (!coupon) {
|
|
throw new AppParamError(Errors.PROMO_INVALID_COUPON,
|
|
`Invalid coupon or promotion code: ${code}`);
|
|
}
|
|
|
|
// Customer restriction
|
|
if (promoCode?.customer && promoCode.customer !== customerId) {
|
|
throw new AppParamError(Errors.PROMO_INVALID_COUPON,
|
|
`Promotion code "${code}" is not available for this customer`);
|
|
}
|
|
```
|
|
|
|
**2. Duplicate Type/PriceKey**:
|
|
```javascript
|
|
if (duplicateTypePrice) {
|
|
throw new AppParamError(Errors.PROMO_DUPLICATE_TYPE_PRICEKEY,
|
|
`Active promo already exists for ${type}/${priceKey}: "${duplicate.name}"`);
|
|
}
|
|
```
|
|
|
|
**2. Duplicate Coupon ID**:
|
|
```javascript
|
|
if (duplicateCoupon) {
|
|
throw new AppParamError(Errors.PROMO_DUPLICATE_COUPON,
|
|
`Active promo already uses coupon ${couponId}: "${duplicate.name}"`);
|
|
}
|
|
```
|
|
|
|
**3. Overlapping Dates**:
|
|
```javascript
|
|
if (overlapping) {
|
|
throw new AppParamError(Errors.PROMO_OVERLAPPING_DATES,
|
|
`Overlapping promo period for ${type}/${priceKey}: "${overlapping.name}"`);
|
|
}
|
|
```
|
|
|
|
**API Response Format**:
|
|
```json
|
|
{
|
|
"error": {
|
|
".tag": "promo_duplicate_type_pricekey",
|
|
"message": "Active promo already exists for package/ess_1: 'First Package Free'"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Related Documentation**: [PROMO_ENHANCEMENTS_V3.md](./PROMO_ENHANCEMENTS_V3.md)
|
|
|
|
---
|
|
|
|
### General Error Codes
|
|
|
|
```javascript
|
|
const Errors = Object.freeze({
|
|
// Authentication & Authorization
|
|
NOT_AUTHORIZED: 'not_authorized',
|
|
INVALID_TOKEN: 'invalid_token',
|
|
TOKEN_EXPIRED: 'token_expired',
|
|
ACC_INACTIVE: 'acc_inactive',
|
|
|
|
// Parameter & Input Validation
|
|
INVALID_PARAM: 'invalid_param',
|
|
INVALID_INPUT: 'invalid_input',
|
|
INVALID_REQUEST: 'invalid_request',
|
|
|
|
// Resource Not Found
|
|
NOT_FOUND: 'not_found',
|
|
USER_NOT_FOUND: 'user_not_found',
|
|
JOB_NOT_FOUND: 'job_not_found',
|
|
|
|
// Subscription & Payment
|
|
SUBSCRIPTION_NOT_FOUND: 'subscription_not_found',
|
|
PAYMENT_FAILED: 'payment_failed',
|
|
PAYMENT_EXPIRED: 'payment_expired',
|
|
INVALID_PAYMENT_METHOD: 'invalid_payment_method',
|
|
|
|
// ... (see helpers/constants.js for complete list)
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## User and Partner Constants
|
|
|
|
### UserTypes
|
|
|
|
```javascript
|
|
const UserTypes = Object.freeze({
|
|
ADMIN: "0",
|
|
APP: "1", // Applicator
|
|
APP_ADM: "2", // Applicator Admin
|
|
CLIENT: "3",
|
|
OFFICER: "4",
|
|
PILOT: "5",
|
|
INSPECTOR: "6",
|
|
DEVICE: "9",
|
|
PARTNER: "20", // Partner Organization
|
|
PARTNER_SYSTEM_USER: "21" // Partner System User (customer credentials)
|
|
});
|
|
```
|
|
|
|
### PartnerCodes
|
|
|
|
```javascript
|
|
const PartnerCodes = Object.freeze({
|
|
SATLOC: 'SATLOC',
|
|
AGIDRONEX: 'AGIDRONEX',
|
|
// Add other partner codes as needed
|
|
});
|
|
```
|
|
|
|
### AuthMethods
|
|
|
|
```javascript
|
|
const AuthMethods = Object.freeze({
|
|
API_KEY: 'api_key',
|
|
USERNAME_PASSWORD: 'username_password',
|
|
OAUTH: 'oauth',
|
|
BEARER_TOKEN: 'bearer_token'
|
|
});
|
|
```
|
|
|
|
**Related Documentation**: [README_PARTNER_INTEGRATION.md](../README_PARTNER_INTEGRATION.md)
|
|
|
|
---
|
|
|
|
## Other Constants
|
|
|
|
### APIActions
|
|
|
|
Standard action labels for API mutation responses.
|
|
|
|
```javascript
|
|
const APIActions = Object.freeze({
|
|
DISABLED: 'disabled',
|
|
DELETED: 'deleted',
|
|
UPDATED: 'updated'
|
|
});
|
|
```
|
|
|
|
**Usage**:
|
|
```javascript
|
|
return res.json({
|
|
action: APIActions.DELETED,
|
|
promo: { _id: promoId, name: promo.name }
|
|
});
|
|
```
|
|
|
|
### HttpStatus
|
|
|
|
```javascript
|
|
const HttpStatus = Object.freeze({
|
|
OK: 200,
|
|
CREATED: 201,
|
|
NO_CONTENT: 204,
|
|
BAD_REQUEST: 400,
|
|
UNAUTHORIZED: 401,
|
|
PAYMENT_REQUIRED: 402,
|
|
FORBIDDEN: 403,
|
|
NOT_FOUND: 404,
|
|
CONFLICT: 409,
|
|
GONE: 410,
|
|
INTERNAL_SERVER_ERROR: 500,
|
|
SERVICE_UNAVAILABLE: 503
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Migration Guide
|
|
|
|
### Using New Constants in Existing Code
|
|
|
|
**Before** (String Literals):
|
|
```javascript
|
|
// ❌ Old approach - string literals
|
|
if (promo.mode === 'enabled') { ... }
|
|
if (stripeCoupon.duration === 'forever') { ... }
|
|
if (error.type === 'StripeCardError') { ... }
|
|
throw new AppParamError(Errors.INVALID_PARAM, 'Duplicate promo');
|
|
```
|
|
|
|
**After** (Frozen Constants):
|
|
```javascript
|
|
// ✅ New approach - frozen constants
|
|
const { PromoModes, CouponDuration, StripeErrorTypes, Errors } = require('./helpers/constants');
|
|
|
|
if (promo.mode === PromoModes.ENABLED) { ... }
|
|
if (stripeCoupon.duration === CouponDuration.FOREVER) { ... }
|
|
if (error.type === StripeErrorTypes.CARD_ERROR) { ... }
|
|
throw new AppParamError(Errors.PROMO_DUPLICATE_TYPE_PRICEKEY, 'Duplicate promo');
|
|
```
|
|
|
|
**Benefits**:
|
|
- ✅ Type safety - catch typos at runtime
|
|
- ✅ IDE autocomplete support
|
|
- ✅ Easier refactoring - change once, update everywhere
|
|
- ✅ Self-documenting code
|
|
- ✅ Prevents accidental string modifications
|
|
|
|
---
|
|
|
|
## Best Practices
|
|
|
|
1. **Always Import Constants**: Never use string literals when a constant is available
|
|
2. **Add Comments**: Document the purpose of new constants
|
|
3. **Use Object.freeze()**: Prevent accidental modifications
|
|
4. **Export Consistently**: Add new constants to module.exports
|
|
5. **Update Documentation**: Update this file when adding new constants
|
|
|
|
---
|
|
|
|
## Files Modified (January 28, 2026)
|
|
|
|
**Constants Added**:
|
|
1. `CouponDuration` - Stripe coupon duration types
|
|
2. `StripeErrorTypes` - Stripe error type identifiers
|
|
3. `PROMO_DUPLICATE_TYPE_PRICEKEY` - Duplicate type/priceKey error
|
|
4. `PROMO_DUPLICATE_COUPON` - Duplicate couponId error
|
|
5. `PROMO_OVERLAPPING_DATES` - Overlapping validUntil error
|
|
|
|
**Files Updated to Use New Constants**:
|
|
- `controllers/main.js`
|
|
- `controllers/subscription.js`
|
|
- `model/customer.js`
|
|
- `tests/test_setup_intent.js`
|
|
- `tests/test_duplicate_promo_validation.js`
|
|
|
|
---
|
|
|
|
## See Also
|
|
|
|
- [helpers/constants.js](../helpers/constants.js) - Source file with all constants
|
|
- [PROMO_ENHANCEMENTS_V3.md](./PROMO_ENHANCEMENTS_V3.md) - Promo system v3.0 changes
|
|
- [PAYMENT_FAILURE_HANDLING.md](./PAYMENT_FAILURE_HANDLING.md) - Payment error handling
|
|
- [README_PARTNER_INTEGRATION.md](../README_PARTNER_INTEGRATION.md) - Partner integration guide
|