const Schema = require('mongoose').Schema; const SubStatus = Object.freeze({ ACTIVE: "active", PAST_DUE: "past_due", UNPAID: "unpaid", CANCELED: "canceled", INCOMPLETE: "incomplete", INCOMPLETE_EXPIRED: "incomplete_expired", TRIALING: "trialing", PAUSED: "paused" }); const InvStatus = Object.freeze({ DRAFT: 'draft', OPEN: 'open', PAID: 'paid', UNCOLLECTIBLE: 'uncollectible', VOID: 'void' }); const ChrgStatus = Object.freeze({ SUCCEEDED: 'succeeded', PENDING: 'pending', FAILED: 'failed' }); const IntentStatus = Object.freeze({ REQUIRES_CONFIRMATION: 'requires_confirmation', REQUIRES_ACTION: 'requires_action', REQUIRES_SOURCE_ACTION: 'requires_source_action', REQUIRES_PAYMENT_METHOD: 'requires_payment_method', SUCCEEDED: 'succeeded', PROCESSING: 'processing' }); const SubType = Object.freeze({ PACKAGE: "package", ADDON: "addon" }); const RecurType = Object.freeze({ MONTH: "month", YEAR: "year", WEEK: "week", day: "day" }); const AutoTaxStatus = Object.freeze({ SUPPORTED: 'supported', NOT_COLLECTING: 'not_collecting' }); const SubFields = Object.freeze({ ID: 'id', METADATA: 'metadata', TYPE: 'type', PRICE: 'price', QUANTITY: 'quantity', TIER: 'tier', MAX_VEHICLES: 'maxVehicles', // Custom, only use in metadata MAX_ACRES: 'maxAcres', // Custom, only use in metadata LEVEL: 'level', STATUS: 'status', TAX: 'tax', REFUNDED: 'refunded', LIMIT: 'limit', DEFAULT_PAYMENT_METHOD: 'default_payment_method', DEFAULT_SOURCE: 'default_source', NAME: 'name', BILLING_DETAILS: 'billing_details', // PM Object's billing_details CARD: 'card', EXP_MONTH: 'exp_month', EXP_YEAR: 'exp_year' }); const Events = Object.freeze({ CUST_SUB_TRIAL_WILL_END: 'customer.subscription.trial_will_end', CUST_SUB_DELETED: "customer.subscription.deleted", CUST_SUB_CREATED: "customer.subscription.created", CUST_SUB_UPDATED: "customer.subscription.updated", INV_UPCOMING: 'invoice.upcoming', INV_PAID: "invoice.paid", INV_PM_SUCCEEDED: "invoice.payment_succeeded", INV_PM_FAILED: "invoice.payment_failed", INV_FIN_FAILED: "invoice.finalization_failed", INV_PM_ACT_REQUIRED: "invoice.payment_action_required", CHARGE_RFD_UPDATED: "charge.refund.updated", CHARGE_SUCCEEDED: "charge.succeeded", // Subscription Schedule events SUB_SCHEDULE_COMPLETED: "subscription_schedule.completed", SUB_SCHEDULE_RELEASED: "subscription_schedule.released", SUB_SCHEDULE_CANCELED: "subscription_schedule.canceled", SUB_SCHEDULE_UPDATED: "subscription_schedule.updated", COUPON_DELETED: "coupon.deleted" }); const BillReasons = Object.freeze({ SUB_CREATE: 'subscription_create', SUB_UPDATE: 'subscription_update', SUB_PRORATION: 'subscription_proration', SUB_SCHEDULED_UPDATE: 'subscription_scheduled_update', SUB_RESTORE: 'subscription_restore', SUB_TRIAL_END: 'subscription_trial_end', SUB_CYCLE: 'subscription_cycle' }); function isTaxableCountry(country) { return country && country == 'CA'; } function isValidTaxStatus(status) { return [AutoTaxStatus.SUPPORTED, AutoTaxStatus.NOT_COLLECTING].includes(status); } function isFinalSubStatus(status) { return [SubStatus.CANCELED, SubStatus.INCOMPLETE_EXPIRED].includes(status); } /* Subscription information [ { id: subscription id, type: 'PACKAGE'/'ADDON', status: subscription status, // Need to handle user action when value other than 'active', especially 'incomplete' for user actions periodStart: subscription current_period_start, periodEnd: subscription current_period_end, items: [ price: 'lookup_key', quantity: '' // optional for package or can be 1 ], } ] */ const subscriptionSchema = new Schema({ id: String, type: { type: String, enum: { values: Object.values(SubType), message: '{VALUE} for subscription \'type\' is not supported' } }, status: { type: String, enum: { values: Object.values(SubStatus), message: '{VALUE} for subscription \'status\' is not supported' } }, periodStart: Number, periodEnd: Number, items: [ { price: String, quantity: { type: Number, required: false, min: [1, 'quantity must be at least 1'], max: [100, 'quantity must be at max of 100'] }, metadata: Schema.Types.Mixed } ], // billing cycles: monthly, yearly, quarterly, etc. recurring: { interval: { type: String, enum: { values: Object.values(RecurType), message: '{VALUE} for subscription \'recurring\' is not supported' } }, intervalCount: { type: Number, default: 1 } }, cancelAtPeriodEnd: Boolean, trialEnd: Number, // Promo tracking - which promo is applied to this subscription promoId: { type: String, required: false, default: null }, // Schedule tracking - ID of active SubscriptionSchedule managing this subscription scheduleId: { type: String, required: false, default: null } }); module.exports = { subscriptionSchema, SubStatus, SubType, SubFields, Events, InvStatus, ChrgStatus, IntentStatus, isTaxableCountry, AutoTaxStatus, isValidTaxStatus, isFinalSubStatus, BillReasons }