-
-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathbilling.processedStripeEvent.model.mongoose.js
More file actions
103 lines (97 loc) · 2.44 KB
/
Copy pathbilling.processedStripeEvent.model.mongoose.js
File metadata and controls
103 lines (97 loc) · 2.44 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
/**
* Module dependencies
*/
import mongoose from 'mongoose';
const Schema = mongoose.Schema;
/**
* ProcessedStripeEvent Data Model Mongoose
*
* Idempotency store for Stripe webhook events.
* TTL of 30 days — events older than that can safely be re-processed
* (Stripe's webhook retry window is 3 days).
*/
const ProcessedStripeEventMongoose = new Schema(
{
eventId: {
type: String,
required: true,
unique: true,
trim: true,
},
type: {
type: String,
required: true,
trim: true,
},
processedAt: {
type: Date,
required: true,
default: () => new Date(),
},
/**
* Number of handler execution attempts (including failed ones).
* Incremented on each handler exception before deciding to rollback or dead-letter.
*/
attempts: {
type: Number,
default: 0,
},
/**
* Last handler error message — set when the handler throws.
*/
lastError: {
type: String,
default: null,
},
/**
* Timestamp of the last handler error.
*/
lastErrorAt: {
type: Date,
default: null,
},
/**
* When true, the event has exceeded max retry attempts.
* The claim is kept permanently so Stripe stops retrying.
*/
deadLetter: {
type: Boolean,
default: false,
},
},
{
timestamps: false,
// Disable _id virtuals to keep the document lean
},
);
/**
* TTL index: automatically remove documents 30 days after processedAt.
*
* Excludes dead-letter documents via partial filter: dead-lettered events are
* permanent rejections (Stripe was told to stop retrying via 200 response) and
* must NEVER be purged. If purged, a manual replay from the Stripe dashboard
* after 30 days would re-process the event as if new — causing potential
* double-credit on extras packs or double subscription resets.
*/
ProcessedStripeEventMongoose.index(
{ processedAt: 1 },
{
expireAfterSeconds: 30 * 24 * 60 * 60,
partialFilterExpression: { deadLetter: { $ne: 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
*/
ProcessedStripeEventMongoose.virtual('id').get(addID);
ProcessedStripeEventMongoose.set('toJSON', {
virtuals: true,
});
mongoose.model('ProcessedStripeEvent', ProcessedStripeEventMongoose);