-
-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathbilling.subscription.model.mongoose.js
More file actions
149 lines (140 loc) · 3.83 KB
/
Copy pathbilling.subscription.model.mongoose.js
File metadata and controls
149 lines (140 loc) · 3.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/**
* Module dependencies
*/
import mongoose from 'mongoose';
import config from '../../../config/index.js';
const Schema = mongoose.Schema;
/**
* Data Model Mongoose
*/
const SubscriptionMongoose = new Schema(
{
organization: {
type: Schema.ObjectId,
ref: 'Organization',
unique: true,
required: true,
},
stripeCustomerId: {
type: String,
unique: true,
sparse: true,
},
stripeSubscriptionId: {
type: String,
unique: true,
sparse: true,
},
plan: {
type: String,
enum: config.billing.plans,
default: 'free',
},
status: {
type: String,
enum: config.billing.statuses,
default: 'active',
},
// ── Meter fields (sparse — backward-compatible additions) ────────────────
/**
* The plan version active on this subscription (e.g. "v1", "v2").
* Only populated when meterMode is enabled.
*/
planVersion: {
type: String,
sparse: true,
},
/**
* Start of the current billing period. Used to detect period changes
* in webhook handlers and trigger meter period resets.
*/
currentPeriodStart: {
type: Date,
default: null,
},
/**
* Timestamp when the subscription first entered past_due status.
* Used to enforce the 7-day grace period before degraded mode.
*/
pastDueSince: {
type: Date,
default: null,
},
/**
* Timestamp of the last successful weekly meter reset sweep.
* Used by the scheduler to recover after delayed or missed runs.
*/
lastResetAt: {
type: Date,
default: null,
},
// ── Per-family event ordering guards ─────────────────────────────────────
// Separate trackers for 'subscription' vs 'invoice' event families prevent
// same-second cross-family deliveries from cancelling each other's state.
/**
* Stripe event.created (Unix seconds) of the last processed customer.subscription.* event.
*/
lastSubscriptionEventCreatedAt: {
type: Number,
default: null,
},
/**
* Stripe event.id of the last processed customer.subscription.* event (same-second tiebreaker).
*/
lastSubscriptionEventId: {
type: String,
default: null,
},
/**
* Stripe event.created (Unix seconds) of the last processed invoice.* event.
*/
lastInvoiceEventCreatedAt: {
type: Number,
default: null,
},
/**
* Stripe event.id of the last processed invoice.* event (same-second tiebreaker).
*/
lastInvoiceEventId: {
type: String,
default: null,
},
// ── Admin override audit trail ───────────────────────────────────────────
/**
* Timestamp of the last admin-initiated plan change (via adminUpdatePlanOnly).
* Distinct from updatedAt (touched by every webhook). Used by audit + ops UI to
* surface when a subscription's plan was overridden manually.
*/
adminUpdatedAt: {
type: Date,
default: null,
},
/**
* ObjectId of the admin user who triggered the last plan override.
*/
adminUpdatedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
default: null,
},
},
{
timestamps: true,
},
);
/**
* Returns the hex string representation of the document ObjectId.
* @returns {string} Hex string of the ObjectId.
*/
function addID() {
return this._id.toHexString();
}
/**
* Model configuration
*/
SubscriptionMongoose.virtual('id').get(addID);
// Ensure virtual fields are serialised.
SubscriptionMongoose.set('toJSON', {
virtuals: true,
});
mongoose.model('Subscription', SubscriptionMongoose);