backend-manager 3.0.13 → 3.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
package/src/cli/cli.js
CHANGED
|
@@ -412,6 +412,8 @@ Main.prototype.setup = async function () {
|
|
|
412
412
|
return false;
|
|
413
413
|
}
|
|
414
414
|
|
|
415
|
+
self.bemConfigJSON = bemConfig;
|
|
416
|
+
|
|
415
417
|
return !!pass;
|
|
416
418
|
}, fix_bemConfig);
|
|
417
419
|
|
|
@@ -535,7 +537,12 @@ Main.prototype.setup = async function () {
|
|
|
535
537
|
await self.test('hosting is set to dedicated folder in JSON', function () {
|
|
536
538
|
let hosting = _.get(self.firebaseJSON, 'hosting', {});
|
|
537
539
|
return (hosting.public && (hosting.public === 'public' || hosting.public !== '.'))
|
|
538
|
-
},
|
|
540
|
+
}, fix_firebaseHostingFolder);
|
|
541
|
+
|
|
542
|
+
// Hosting
|
|
543
|
+
await self.test('hosting has', async function () {
|
|
544
|
+
return await fix_firebaseHostingAuth(self);
|
|
545
|
+
}, NOFIX);
|
|
539
546
|
|
|
540
547
|
await self.test('update backend-manager-tests.js', function () {
|
|
541
548
|
jetpack.write(`${self.firebaseProjectPath}/test/backend-manager-tests.js`,
|
|
@@ -1065,7 +1072,7 @@ function fix_remoteconfigTemplateFile(self) {
|
|
|
1065
1072
|
|
|
1066
1073
|
|
|
1067
1074
|
// Hosting
|
|
1068
|
-
function
|
|
1075
|
+
function fix_firebaseHostingFolder(self) {
|
|
1069
1076
|
return new Promise(function(resolve, reject) {
|
|
1070
1077
|
_.set(self.firebaseJSON, 'hosting.public', 'public')
|
|
1071
1078
|
jetpack.write(`${self.firebaseProjectPath}/firebase.json`, JSON.stringify(self.firebaseJSON, null, 2));
|
|
@@ -1073,6 +1080,23 @@ function fix_firebaseHosting(self) {
|
|
|
1073
1080
|
});
|
|
1074
1081
|
};
|
|
1075
1082
|
|
|
1083
|
+
function fix_firebaseHostingAuth(self) {
|
|
1084
|
+
return new Promise(async function(resolve, reject) {
|
|
1085
|
+
await fetch(`${self.bemConfigJSON.brand.url}/server/auth/handler?cb=${new Date().getTime()}`, {
|
|
1086
|
+
method: 'get',
|
|
1087
|
+
tries: 2,
|
|
1088
|
+
response: 'text',
|
|
1089
|
+
})
|
|
1090
|
+
.then(async (text) => {
|
|
1091
|
+
// Save to file
|
|
1092
|
+
jetpack.write(`${self.firebaseProjectPath}/public/auth/handler/index.html`, text);
|
|
1093
|
+
|
|
1094
|
+
resolve(true)
|
|
1095
|
+
})
|
|
1096
|
+
.catch(reject)
|
|
1097
|
+
});
|
|
1098
|
+
};
|
|
1099
|
+
|
|
1076
1100
|
function getPkgVersion(package) {
|
|
1077
1101
|
return new Promise(async function(resolve, reject) {
|
|
1078
1102
|
let npm = new Npm();
|
|
@@ -0,0 +1,828 @@
|
|
|
1
|
+
const moment = require('moment');
|
|
2
|
+
const { get } = require('lodash');
|
|
3
|
+
|
|
4
|
+
function SubscriptionResolver(Manager, profile, resource) {
|
|
5
|
+
const self = this;
|
|
6
|
+
|
|
7
|
+
self.Manager = Manager;
|
|
8
|
+
self.profile = profile || {};
|
|
9
|
+
self.resource = resource || {};
|
|
10
|
+
|
|
11
|
+
return self;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
SubscriptionResolver.prototype.resolve = function (options) {
|
|
15
|
+
const self = this;
|
|
16
|
+
|
|
17
|
+
const resolved = {
|
|
18
|
+
status: '',
|
|
19
|
+
frequency: '',
|
|
20
|
+
resource: {
|
|
21
|
+
id: '',
|
|
22
|
+
},
|
|
23
|
+
payment: {
|
|
24
|
+
completed: false,
|
|
25
|
+
refunded: false,
|
|
26
|
+
},
|
|
27
|
+
start: {
|
|
28
|
+
timestamp: moment(0),
|
|
29
|
+
timestampUNIX: moment(0),
|
|
30
|
+
},
|
|
31
|
+
expires: {
|
|
32
|
+
timestamp: moment(0),
|
|
33
|
+
timestampUNIX: moment(0),
|
|
34
|
+
},
|
|
35
|
+
cancelled: {
|
|
36
|
+
timestamp: moment(0),
|
|
37
|
+
timestampUNIX: moment(0),
|
|
38
|
+
},
|
|
39
|
+
lastPayment: {
|
|
40
|
+
amount: 0,
|
|
41
|
+
date: {
|
|
42
|
+
timestamp: moment(0),
|
|
43
|
+
timestampUNIX: moment(0),
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
trial: {
|
|
47
|
+
claimed: false,
|
|
48
|
+
active: false,
|
|
49
|
+
daysLeft: 0,
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Set
|
|
54
|
+
const profile = self.profile;
|
|
55
|
+
const resource = self.resource;
|
|
56
|
+
|
|
57
|
+
// Set defaults
|
|
58
|
+
profile.type = profile.type || null;
|
|
59
|
+
profile.details = profile.details || {};
|
|
60
|
+
profile.details.planFrequency = profile.details.planFrequency || null;
|
|
61
|
+
profile.authorization = profile.authorization || {};
|
|
62
|
+
profile.authorization.status = profile.authorization.status || 'pending';
|
|
63
|
+
|
|
64
|
+
// Set
|
|
65
|
+
options = options || {};
|
|
66
|
+
options.log = typeof options.log === 'undefined' ? false : options.log;
|
|
67
|
+
options.resolveProcessor = typeof options.resolveProcessor === 'undefined' ? false : options.resolveProcessor;
|
|
68
|
+
options.resolveType = typeof options.resolveType === 'undefined' ? false : options.resolveType;
|
|
69
|
+
options.today = typeof options.today === 'undefined' ? moment() : moment(options.today);
|
|
70
|
+
|
|
71
|
+
// Set provider if not set
|
|
72
|
+
if (!profile.processor) {
|
|
73
|
+
/*** PayPal ***/
|
|
74
|
+
// Order
|
|
75
|
+
if (
|
|
76
|
+
resource.purchase_units
|
|
77
|
+
) {
|
|
78
|
+
profile.processor = 'paypal';
|
|
79
|
+
profile.type = profile.type || 'order';
|
|
80
|
+
// Subscription
|
|
81
|
+
} else if (
|
|
82
|
+
// resource.billing_info
|
|
83
|
+
resource.create_time
|
|
84
|
+
) {
|
|
85
|
+
profile.processor = 'paypal';
|
|
86
|
+
profile.type = profile.type || 'subscription';
|
|
87
|
+
|
|
88
|
+
/*** Chargebee ***/
|
|
89
|
+
// Order
|
|
90
|
+
} else if (
|
|
91
|
+
resource.line_items
|
|
92
|
+
) {
|
|
93
|
+
profile.processor = 'chargebee';
|
|
94
|
+
profile.type = profile.type || 'order';
|
|
95
|
+
// Subscription
|
|
96
|
+
} else if (
|
|
97
|
+
resource.billing_period_unit
|
|
98
|
+
) {
|
|
99
|
+
profile.processor = 'chargebee';
|
|
100
|
+
profile.type = profile.type || 'subscription';
|
|
101
|
+
|
|
102
|
+
/*** Stripe ***/
|
|
103
|
+
// Order
|
|
104
|
+
} else if (
|
|
105
|
+
resource.object === 'charge'
|
|
106
|
+
) {
|
|
107
|
+
profile.processor = 'stripe';
|
|
108
|
+
profile.type = profile.type || 'order';
|
|
109
|
+
// Subscription
|
|
110
|
+
} else if (
|
|
111
|
+
resource.object === 'subscription'
|
|
112
|
+
) {
|
|
113
|
+
profile.processor = 'stripe';
|
|
114
|
+
profile.type = profile.type || 'subscription';
|
|
115
|
+
|
|
116
|
+
/*** Coinbase ***/
|
|
117
|
+
// Order AND Subscription
|
|
118
|
+
} else if (
|
|
119
|
+
resource.addresses
|
|
120
|
+
) {
|
|
121
|
+
profile.processor = 'coinbase';
|
|
122
|
+
// profile.type = profile.type || 'subscription';
|
|
123
|
+
|
|
124
|
+
/*** Error ***/
|
|
125
|
+
} else {
|
|
126
|
+
throw new Error('Unable to determine subscription provider');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Set profile.type
|
|
131
|
+
if (!profile.type) {
|
|
132
|
+
profile.type = profile.type || 'subscription';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Set processor if needed
|
|
136
|
+
if (options.resolveProcessor) {
|
|
137
|
+
resolved.processor = profile.processor;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Set type if needed
|
|
141
|
+
if (options.resolveType) {
|
|
142
|
+
resolved.type = profile.type;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Set frequency if order
|
|
146
|
+
if (profile.type === 'order') {
|
|
147
|
+
resolved.frequency = 'single';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Log if requested
|
|
151
|
+
if (options.log) {
|
|
152
|
+
console.log('profile', profile);
|
|
153
|
+
console.log('resource', resource);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Resolve
|
|
157
|
+
const processor = self[`resolve_${profile.processor}`];
|
|
158
|
+
if (processor) {
|
|
159
|
+
processor(profile, resource, resolved, options);
|
|
160
|
+
} else {
|
|
161
|
+
throw new Error('Unknown processor');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// console.log('---resolved', resolved);
|
|
165
|
+
|
|
166
|
+
// Check for frequency
|
|
167
|
+
if (!resolved.frequency) {
|
|
168
|
+
throw new Error('Unknown frequency');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// console.log('----expires 3', resolved.resource.id, resolved.status, resolved.frequency, resolved.trial.active, resolved.expires.timestamp.toISOString ? resolved.expires.timestamp.toISOString() : resolved.expires.timestamp);
|
|
172
|
+
console.log('--- 1', resolved.resource.id, resolved.status, resolved.expires.timestamp);
|
|
173
|
+
console.log('---', resolved.trial.active, resolved.trial.claimed, resolved.payment.completed, resolved.lastPayment.amount);
|
|
174
|
+
|
|
175
|
+
// If they are not trialing AND there was NEVER any payment sent OR the last payment failed, then set the expiration to 0
|
|
176
|
+
if (
|
|
177
|
+
!resolved.trial.active && resolved.trial.claimed
|
|
178
|
+
&& (!resolved.payment.completed || resolved.lastPayment.amount === 0)
|
|
179
|
+
// && (resolved.status === 'active' || resolved.status === 'suspended')
|
|
180
|
+
) {
|
|
181
|
+
// resolved.expires.timestamp = moment(0);
|
|
182
|
+
if (resolved.trial.claimed) {
|
|
183
|
+
resolved.status = 'suspended';
|
|
184
|
+
} else {
|
|
185
|
+
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// If they are trialing and the authorization charge is failed, set to suspended
|
|
190
|
+
if (
|
|
191
|
+
resolved.trial.active
|
|
192
|
+
&& profile.authorization.status === 'failed'
|
|
193
|
+
) {
|
|
194
|
+
resolved.status = 'suspended';
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// If they got a refund, set the expiration to 0
|
|
198
|
+
if (resolved.payment.refunded) {
|
|
199
|
+
// resolved.expires.timestamp = moment(0);
|
|
200
|
+
resolved.status = 'suspended';
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// If they are suspended, set the expiration to 0
|
|
204
|
+
if (resolved.status === 'suspended') {
|
|
205
|
+
resolved.expires.timestamp = moment(0);
|
|
206
|
+
}
|
|
207
|
+
console.log('--- 2', resolved.resource.id, resolved.status, resolved.expires.timestamp);
|
|
208
|
+
|
|
209
|
+
// console.log('----expires 4', resolved.resource.id, resolved.status, resolved.frequency, resolved.trial.active, resolved.expires.timestamp.toISOString ? resolved.expires.timestamp.toISOString() : resolved.expires.timestamp);
|
|
210
|
+
|
|
211
|
+
// Fix expiry by adding time to the date of last payment
|
|
212
|
+
// console.log('----expires 2', resolved.resource.id, resolved.status, resolved.frequency, resolved.trial.active, resolved.expires.timestamp.toISOString ? resolved.expires.timestamp.toISOString() : resolved.expires.timestamp);
|
|
213
|
+
if (resolved.status === 'active') {
|
|
214
|
+
// Set days left
|
|
215
|
+
if (resolved.trial.active) {
|
|
216
|
+
resolved.trial.daysLeft = Math.abs(resolved.expires.timestamp.diff(options.today, 'days'));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Set expiration
|
|
220
|
+
resolved.expires.timestamp.add(1, 'year').add(30, 'days');
|
|
221
|
+
} else if (resolved.status === 'cancelled') {
|
|
222
|
+
// If trial, it's already set to the trial end above
|
|
223
|
+
if (!resolved.trial.active) {
|
|
224
|
+
// if (!resolved.trial.claimed) {
|
|
225
|
+
if (resolved.frequency === 'annually') {
|
|
226
|
+
resolved.expires.timestamp.add(1, 'year');
|
|
227
|
+
} else if (resolved.frequency === 'monthly') {
|
|
228
|
+
resolved.expires.timestamp.add(1, 'month');
|
|
229
|
+
} else if (resolved.frequency === 'weekly') {
|
|
230
|
+
resolved.expires.timestamp.add(1, 'week');
|
|
231
|
+
} else if (resolved.frequency === 'daily') {
|
|
232
|
+
resolved.expires.timestamp.add(1, 'day');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Fix timestamps
|
|
238
|
+
resolved.start.timestampUNIX = resolved.start.timestamp.unix();
|
|
239
|
+
resolved.start.timestamp = resolved.start.timestamp.toISOString();
|
|
240
|
+
|
|
241
|
+
resolved.expires.timestampUNIX = resolved.expires.timestamp.unix();
|
|
242
|
+
resolved.expires.timestamp = resolved.expires.timestamp.toISOString ? resolved.expires.timestamp.toISOString() : resolved.expires.timestamp;
|
|
243
|
+
|
|
244
|
+
resolved.cancelled.timestampUNIX = resolved.cancelled.timestamp.unix();
|
|
245
|
+
resolved.cancelled.timestamp = resolved.cancelled.timestamp.toISOString();
|
|
246
|
+
|
|
247
|
+
// Fix trial days
|
|
248
|
+
resolved.trial.daysLeft = resolved.trial.daysLeft < 0 ? 0 : resolved.trial.daysLeft;
|
|
249
|
+
|
|
250
|
+
// Set last payment
|
|
251
|
+
resolved.lastPayment.date.timestampUNIX = moment(resolved.lastPayment.date.timestamp).unix();
|
|
252
|
+
resolved.lastPayment.date.timestamp = resolved.lastPayment.date.timestamp.toISOString();
|
|
253
|
+
|
|
254
|
+
// Log if needed
|
|
255
|
+
console.log('--- 3', resolved.resource.id, resolved.status, resolved.expires.timestamp);
|
|
256
|
+
if (options.log) {
|
|
257
|
+
console.log('resolved', resolved);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
self.resolved = resolved;
|
|
261
|
+
// console.log('----expires 6', resolved.resource.id, resolved.status, resolved.frequency, resolved.trial.active, resolved.expires.timestamp.toISOString ? resolved.expires.timestamp.toISOString() : resolved.expires.timestamp);
|
|
262
|
+
|
|
263
|
+
return resolved;
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
SubscriptionResolver.prototype.resolve_paypal = function (profile, resource, resolved, options) {
|
|
267
|
+
const self = this;
|
|
268
|
+
|
|
269
|
+
// Set status
|
|
270
|
+
/*
|
|
271
|
+
subscription: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_get
|
|
272
|
+
APPROVAL_PENDING. The subscription is created but not yet approved by the buyer.
|
|
273
|
+
APPROVED. The buyer has approved the subscription.
|
|
274
|
+
ACTIVE. The subscription is active.
|
|
275
|
+
SUSPENDED. The subscription is suspended.
|
|
276
|
+
CANCELLED. The subscription is cancelled.
|
|
277
|
+
EXPIRED. The subscription is expired.
|
|
278
|
+
|
|
279
|
+
order: https://developer.paypal.com/docs/api/orders/v2/#orders_get
|
|
280
|
+
CREATED The order was created with the specified context.
|
|
281
|
+
SAVED The order was saved and persisted. The order status continues to be in progress until a capture is made with final_capture = true for all purchase units within the order.
|
|
282
|
+
APPROVED The customer approved the payment through the PayPal wallet or another form of guest or unbranded payment. For example, a card, bank account, or so on.
|
|
283
|
+
VOIDED All purchase units in the order are voided.
|
|
284
|
+
COMPLETED The payment was authorized or the authorized payment was captured for the order.
|
|
285
|
+
PAYER_ACTION_REQUIRED The order requires an action from the payer (e.g. 3DS authentication). Redirect the payer to the "rel":"payer-action" HATEOAS link returned as part of the response prior to authorizing or capturing the order.
|
|
286
|
+
*/
|
|
287
|
+
if (['ACTIVE'].includes(resource.status)) {
|
|
288
|
+
resolved.status = 'active';
|
|
289
|
+
|
|
290
|
+
// Check for failed payments
|
|
291
|
+
/*
|
|
292
|
+
Special condition for PayPal
|
|
293
|
+
Because I set the payment_failure_threshold to 0, it will not automatically set the status to suspended.
|
|
294
|
+
We must check for failed payments and set the status to suspended if there are any.
|
|
295
|
+
*/
|
|
296
|
+
if (get(resource, 'billing_info.failed_payments_count', 0) > 0) {
|
|
297
|
+
resolved.status = 'suspended';
|
|
298
|
+
}
|
|
299
|
+
} else if (['SUSPENDED'].includes(resource.status)) {
|
|
300
|
+
resolved.status = 'suspended';
|
|
301
|
+
} else {
|
|
302
|
+
resolved.status = 'cancelled';
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Setup preliminary variables
|
|
306
|
+
const order = get(resource, 'purchase_units[0].payments.captures[0]');
|
|
307
|
+
const subscription = get(resource, 'billing_info.last_payment');
|
|
308
|
+
const isOrder = !!order;
|
|
309
|
+
|
|
310
|
+
// Set resource ID
|
|
311
|
+
resolved.resource.id = resource.id;
|
|
312
|
+
|
|
313
|
+
// Set start
|
|
314
|
+
resolved.start.timestamp = moment(
|
|
315
|
+
(
|
|
316
|
+
isOrder
|
|
317
|
+
// Order
|
|
318
|
+
? get(resource, 'create_time', 0)
|
|
319
|
+
|
|
320
|
+
// Subscription
|
|
321
|
+
: get(resource, 'start_time', 0)
|
|
322
|
+
)
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
// Set expiration
|
|
326
|
+
resolved.expires.timestamp = moment(
|
|
327
|
+
(
|
|
328
|
+
isOrder
|
|
329
|
+
// Order
|
|
330
|
+
? get(resource, 'create_time', 0)
|
|
331
|
+
|
|
332
|
+
// Subscription
|
|
333
|
+
: get(resource, 'billing_info.last_payment.time', 0)
|
|
334
|
+
)
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
// Set cancelled
|
|
338
|
+
if (resolved.status === 'cancelled') {
|
|
339
|
+
resolved.cancelled.timestamp = moment(
|
|
340
|
+
(
|
|
341
|
+
isOrder
|
|
342
|
+
// Order
|
|
343
|
+
? get(resource, 'create_time', 0)
|
|
344
|
+
|
|
345
|
+
// Subscription
|
|
346
|
+
: get(resource, 'status_update_time', 0)
|
|
347
|
+
)
|
|
348
|
+
)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Set last payment
|
|
352
|
+
if (order) {
|
|
353
|
+
resolved.lastPayment.amount = parseFloat(
|
|
354
|
+
get(order, 'amount.value', '0.00')
|
|
355
|
+
);
|
|
356
|
+
resolved.lastPayment.date.timestamp = moment(
|
|
357
|
+
order.create_time || 0
|
|
358
|
+
);
|
|
359
|
+
} else if (subscription) {
|
|
360
|
+
resolved.lastPayment.amount = parseFloat(subscription.amount.value);
|
|
361
|
+
resolved.lastPayment.date.timestamp = moment(subscription.time);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Get trial
|
|
365
|
+
const trialTenure = get(resource, 'plan.billing_cycles', []).find((cycle) => cycle.tenure_type === 'TRIAL');
|
|
366
|
+
const regularTenure = get(resource, 'plan.billing_cycles', []).find((cycle) => cycle.tenure_type === 'REGULAR');
|
|
367
|
+
const trialClaimed = !!trialTenure && parseFloat(get(trialTenure, 'pricing_scheme.fixed_price.value', '0.00')) === 0;
|
|
368
|
+
|
|
369
|
+
// Resolve trial
|
|
370
|
+
/*
|
|
371
|
+
Special condition for PayPal
|
|
372
|
+
Because you cannot remove trial on a sub-level, you have to charge a prorated amount for the "trial".
|
|
373
|
+
Even if charged, it is still considered a trial period by paypal.
|
|
374
|
+
Thus, we must remove the trial indicator if the user has been charged.
|
|
375
|
+
*/
|
|
376
|
+
if (
|
|
377
|
+
resolved.status === 'active'
|
|
378
|
+
&& (trialTenure && regularTenure && regularTenure.total_cycles === 0)
|
|
379
|
+
&& resolved.lastPayment.amount === 0
|
|
380
|
+
) {
|
|
381
|
+
resolved.trial.active = true;
|
|
382
|
+
|
|
383
|
+
// Set expiration
|
|
384
|
+
resolved.expires.timestamp = moment(
|
|
385
|
+
get(resource, 'billing_info.next_billing_time', 0)
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
/*
|
|
389
|
+
Special condition for PayPal #2
|
|
390
|
+
I want to put the subscription in a suspended state if it's even one day past due
|
|
391
|
+
*/
|
|
392
|
+
const trialLength = get(trialTenure, 'frequency.interval_count', 0);
|
|
393
|
+
const daysSinceStart = Math.abs(moment(options.today).diff(moment(resolved.start.timestamp), 'days'));
|
|
394
|
+
if (daysSinceStart > trialLength) {
|
|
395
|
+
resolved.status = 'suspended';
|
|
396
|
+
resolved.trial.active = false;
|
|
397
|
+
}
|
|
398
|
+
// console.log('----resolved.resource.id', resolved.resource.id);
|
|
399
|
+
// console.log('----resolved.start.timestamp', resolved.start.timestamp);
|
|
400
|
+
// console.log('----options.today', options.today);
|
|
401
|
+
// console.log('======daysSinceStart', daysSinceStart);
|
|
402
|
+
// console.log('======trialLength', trialLength);
|
|
403
|
+
}
|
|
404
|
+
resolved.trial.claimed = trialClaimed;
|
|
405
|
+
|
|
406
|
+
// Resolve frequency
|
|
407
|
+
const unit = get(regularTenure, 'frequency.interval_unit');
|
|
408
|
+
if (unit === 'YEAR') {
|
|
409
|
+
resolved.frequency = 'annually';
|
|
410
|
+
} else if (unit === 'MONTH') {
|
|
411
|
+
resolved.frequency = 'monthly';
|
|
412
|
+
} else if (unit === 'WEEK') {
|
|
413
|
+
resolved.frequency = 'weekly';
|
|
414
|
+
} else if (unit === 'DAY') {
|
|
415
|
+
resolved.frequency = 'daily';
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Set completed
|
|
419
|
+
if (!resource.plan) {
|
|
420
|
+
resolved.payment.completed = !['CREATED', 'SAVED', 'APPROVED', 'VOIDED', 'PAYER_ACTION_REQUIRED'].includes(resource.status);
|
|
421
|
+
} else {
|
|
422
|
+
resolved.payment.completed = !['APPROVAL_PENDING', 'APPROVED'].includes(resource.status);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Check if refunded
|
|
426
|
+
if (!resource.plan) {
|
|
427
|
+
// resolved.payment.refunded = false; // @@@ TODO: check if this is correct
|
|
428
|
+
} else {
|
|
429
|
+
const transactions = get(resource, 'transactions', []);
|
|
430
|
+
|
|
431
|
+
resolved.payment.refunded = transactions.some(t => t.status === 'REFUNDED');
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return resolved;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
SubscriptionResolver.prototype.resolve_chargebee = function (profile, resource, resolved, options) {
|
|
438
|
+
const self = this;
|
|
439
|
+
|
|
440
|
+
// Set status
|
|
441
|
+
// subscription: https://apidocs.chargebee.com/docs/api/subscriptions?prod_cat_ver=2#subscription_status
|
|
442
|
+
// future The subscription is scheduled to start at a future date.
|
|
443
|
+
// in_trial The subscription is in trial.
|
|
444
|
+
// active The subscription is active and will be charged for automatically based on the items in it.
|
|
445
|
+
// non_renewing The subscription will be canceled at the end of the current term.
|
|
446
|
+
// paused The subscription is paused. The subscription will not renew while in this state.
|
|
447
|
+
// cancelled The subscription has been canceled and is no longer in service.
|
|
448
|
+
|
|
449
|
+
// order: https://apidocs.chargebee.com/docs/api/invoices?prod_cat_ver=2#invoice_status
|
|
450
|
+
// paid: Indicates a paid invoice.
|
|
451
|
+
// posted: Indicates the payment is not yet collected and will be in this state till the due date to indicate the due period.
|
|
452
|
+
// payment_due: Indicates the payment is not yet collected and is being retried as per retry settings.
|
|
453
|
+
// not_paid: Indicates the payment is not made and all attempts to collect is failed.
|
|
454
|
+
// voided: Indicates a voided invoice.
|
|
455
|
+
// pending: The invoice is yet to be closed (sent for payment collection). An invoice is generated with this status when it has line items that belong to items that are metered or when the subscription.create_pending_invoicesattribute is set to true.
|
|
456
|
+
|
|
457
|
+
if (['in_trial', 'active'].includes(resource.status)) {
|
|
458
|
+
resolved.status = 'active';
|
|
459
|
+
|
|
460
|
+
// If there's a due invoice, it's suspended
|
|
461
|
+
if (resource.total_dues > 0) {
|
|
462
|
+
resolved.status = 'suspended';
|
|
463
|
+
}
|
|
464
|
+
} else if (['paused'].includes(resource.status)) {
|
|
465
|
+
resolved.status = 'suspended';
|
|
466
|
+
} else {
|
|
467
|
+
resolved.status = 'cancelled';
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Setup preliminary variables
|
|
471
|
+
const isOrder = profile.type === 'order';
|
|
472
|
+
|
|
473
|
+
// Set resource ID
|
|
474
|
+
resolved.resource.id = resource.id;
|
|
475
|
+
|
|
476
|
+
// Set start
|
|
477
|
+
resolved.start.timestamp = moment(
|
|
478
|
+
(
|
|
479
|
+
isOrder
|
|
480
|
+
// Order
|
|
481
|
+
? get(resource, 'date', 0)
|
|
482
|
+
|
|
483
|
+
// Subscription
|
|
484
|
+
: get(resource, 'created_at', 0)
|
|
485
|
+
) * 1000
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
// Set expiration
|
|
489
|
+
resolved.expires.timestamp = moment(
|
|
490
|
+
(
|
|
491
|
+
isOrder
|
|
492
|
+
// Order
|
|
493
|
+
? get(resource, 'date', 0)
|
|
494
|
+
|
|
495
|
+
// Subscription
|
|
496
|
+
: get(resource, 'current_term_start', 0)
|
|
497
|
+
) * 1000
|
|
498
|
+
)
|
|
499
|
+
// console.log('---resolved.expires 1', resolved.expires);
|
|
500
|
+
// if (resource.total_dues > 0) {
|
|
501
|
+
// resolved.expires.timestamp = moment(0);
|
|
502
|
+
// } else {
|
|
503
|
+
// resolved.expires.timestamp = moment(
|
|
504
|
+
// (
|
|
505
|
+
// get(resource, 'current_term_start', 0)
|
|
506
|
+
// ) * 1000
|
|
507
|
+
// )
|
|
508
|
+
// }
|
|
509
|
+
|
|
510
|
+
// Set cancelled
|
|
511
|
+
if (resolved.status === 'cancelled') {
|
|
512
|
+
resolved.cancelled.timestamp = moment(
|
|
513
|
+
(
|
|
514
|
+
isOrder
|
|
515
|
+
// Order
|
|
516
|
+
? get(resource, 'date', 0)
|
|
517
|
+
|
|
518
|
+
// Subscription
|
|
519
|
+
: get(resource, 'cancelled_at', 0)
|
|
520
|
+
) * 1000
|
|
521
|
+
)
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Set last payment
|
|
525
|
+
if (
|
|
526
|
+
// Order
|
|
527
|
+
resource.amount_due > 0
|
|
528
|
+
|
|
529
|
+
// Subscription
|
|
530
|
+
|| resource.total_dues > 0
|
|
531
|
+
) {
|
|
532
|
+
resolved.lastPayment.amount = 0;
|
|
533
|
+
resolved.lastPayment.date.timestamp = moment(
|
|
534
|
+
(
|
|
535
|
+
isOrder
|
|
536
|
+
// Order
|
|
537
|
+
? (resource.date || 0)
|
|
538
|
+
|
|
539
|
+
// Subscription
|
|
540
|
+
: (resource.due_since || 0)
|
|
541
|
+
) * 1000
|
|
542
|
+
);
|
|
543
|
+
} else {
|
|
544
|
+
resolved.lastPayment.amount = (
|
|
545
|
+
(
|
|
546
|
+
isOrder
|
|
547
|
+
// Order
|
|
548
|
+
? (resource.amount_paid)
|
|
549
|
+
|
|
550
|
+
// Subscription
|
|
551
|
+
: (resource.plan_amount)
|
|
552
|
+
) / 100
|
|
553
|
+
)
|
|
554
|
+
resolved.lastPayment.date.timestamp = moment(
|
|
555
|
+
(
|
|
556
|
+
isOrder
|
|
557
|
+
// Order
|
|
558
|
+
? (resource.date || 0)
|
|
559
|
+
|
|
560
|
+
// Subscription
|
|
561
|
+
: (resource.current_term_start || 0)
|
|
562
|
+
) * 1000
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Get trial
|
|
567
|
+
if (resource.status === 'in_trial') {
|
|
568
|
+
resolved.trial.active = true;
|
|
569
|
+
|
|
570
|
+
// Set expiration
|
|
571
|
+
resolved.expires.timestamp = moment(
|
|
572
|
+
(
|
|
573
|
+
get(resource, 'trial_end', 0)
|
|
574
|
+
) * 1000
|
|
575
|
+
)
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Resolve frequency
|
|
579
|
+
const unit = get(resource, 'billing_period_unit');
|
|
580
|
+
if (unit === 'year') {
|
|
581
|
+
resolved.frequency = 'annually';
|
|
582
|
+
} else if (unit === 'month') {
|
|
583
|
+
resolved.frequency = 'monthly';
|
|
584
|
+
} else if (unit === 'week') {
|
|
585
|
+
resolved.frequency = 'weekly';
|
|
586
|
+
} else if (unit === 'day') {
|
|
587
|
+
resolved.frequency = 'daily';
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Set completed
|
|
591
|
+
if (isOrder) {
|
|
592
|
+
resolved.payment.completed = !['posted', 'payment_due', 'not_paid', 'voided', 'pending'].includes(resource.status);
|
|
593
|
+
} else {
|
|
594
|
+
resolved.payment.completed = !['future'].includes(resource.status);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Check if refunded
|
|
598
|
+
if (isOrder) {
|
|
599
|
+
resolved.payment.refunded = false; // @@@ TODO: check if this is correct
|
|
600
|
+
} else {
|
|
601
|
+
const invoices = get(resource, 'invoices', []);
|
|
602
|
+
|
|
603
|
+
resolved.payment.refunded = invoices.some(invoice => {
|
|
604
|
+
const creditNotes = get(invoice, 'invoice.issued_credit_notes', []);
|
|
605
|
+
return creditNotes.some(creditNote => {
|
|
606
|
+
return creditNote.cn_status === 'refunded'
|
|
607
|
+
})
|
|
608
|
+
})
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Special chargebee reset lastPayment
|
|
612
|
+
// If trial is active OR if it was cancelled after the trial has ended
|
|
613
|
+
const trialStart = get(resource, 'trial_start', 0) * 1000;
|
|
614
|
+
const trialEnd = get(resource, 'trial_end', 0) * 1000;
|
|
615
|
+
const cancelledAt = get(resource, 'cancelled_at', 0) * 1000;
|
|
616
|
+
const trialDaysDifference = Math.abs(moment(trialEnd).diff(moment(trialStart), 'days'));
|
|
617
|
+
const trialClaimed = !!trialStart && !!trialEnd && trialDaysDifference > 1;
|
|
618
|
+
if (
|
|
619
|
+
resolved.trial.active
|
|
620
|
+
|| (trialEnd > 0 && cancelledAt > 0 && cancelledAt === trialEnd)
|
|
621
|
+
) {
|
|
622
|
+
resolved.lastPayment.amount = 0;
|
|
623
|
+
resolved.lastPayment.date.timestamp = moment(0);
|
|
624
|
+
}
|
|
625
|
+
resolved.trial.claimed = trialClaimed;
|
|
626
|
+
|
|
627
|
+
return resolved;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
SubscriptionResolver.prototype.resolve_stripe = function (profile, resource, resolved, options) {
|
|
631
|
+
const self = this;
|
|
632
|
+
|
|
633
|
+
// Subscription: https://stripe.com/docs/api/subscriptions/object#subscription_object-status
|
|
634
|
+
// incomplete
|
|
635
|
+
// incomplete_expired
|
|
636
|
+
// trialing
|
|
637
|
+
// active
|
|
638
|
+
// past_due
|
|
639
|
+
// canceled
|
|
640
|
+
// unpaid
|
|
641
|
+
|
|
642
|
+
// Charge: https://stripe.com/docs/api/payment_intents/object#payment_intent_object-status
|
|
643
|
+
// requires_payment_method
|
|
644
|
+
// requires_confirmation
|
|
645
|
+
// requires_action
|
|
646
|
+
// processing
|
|
647
|
+
// requires_capture
|
|
648
|
+
// canceled
|
|
649
|
+
// succeeded
|
|
650
|
+
// Set status
|
|
651
|
+
if (['trialing', 'active'].includes(resource.status)) {
|
|
652
|
+
resolved.status = 'active';
|
|
653
|
+
} else if (['past_due', 'unpaid'].includes(resource.status)) {
|
|
654
|
+
resolved.status = 'suspended';
|
|
655
|
+
} else {
|
|
656
|
+
resolved.status = 'cancelled';
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Setup preliminary variables
|
|
660
|
+
const order = resource.object === 'charge' ? resource : null;
|
|
661
|
+
const subscription = get(resource, 'latest_invoice');
|
|
662
|
+
const isOrder = !!order;
|
|
663
|
+
|
|
664
|
+
// Set resource ID
|
|
665
|
+
resolved.resource.id = resource.id;
|
|
666
|
+
|
|
667
|
+
// Set start
|
|
668
|
+
resolved.start.timestamp = moment(
|
|
669
|
+
(
|
|
670
|
+
isOrder
|
|
671
|
+
// Order
|
|
672
|
+
? get(resource, 'created', 0)
|
|
673
|
+
|
|
674
|
+
// Subscription
|
|
675
|
+
: get(resource, 'start_date', 0)
|
|
676
|
+
) * 1000
|
|
677
|
+
);
|
|
678
|
+
|
|
679
|
+
// Set expiration
|
|
680
|
+
resolved.expires.timestamp = moment(
|
|
681
|
+
(
|
|
682
|
+
isOrder
|
|
683
|
+
// Order
|
|
684
|
+
? get(resource, 'created', 0)
|
|
685
|
+
|
|
686
|
+
// Subscription
|
|
687
|
+
: get(resource, 'current_period_start', 0)
|
|
688
|
+
) * 1000
|
|
689
|
+
);
|
|
690
|
+
|
|
691
|
+
// Set cancelled
|
|
692
|
+
if (resolved.status === 'cancelled') {
|
|
693
|
+
resolved.cancelled.timestamp = moment(
|
|
694
|
+
(
|
|
695
|
+
isOrder
|
|
696
|
+
// Order
|
|
697
|
+
? get(resource, 'created', 0)
|
|
698
|
+
|
|
699
|
+
// Subscription
|
|
700
|
+
: get(resource, 'canceled_at', 0)
|
|
701
|
+
) * 1000
|
|
702
|
+
)
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// Set last payment
|
|
706
|
+
// TODO: check if suspended payments are handled correctly when using resource.latest_invoice.amount_paid
|
|
707
|
+
if (order) {
|
|
708
|
+
resolved.lastPayment.amount = order.amount_captured / 100;
|
|
709
|
+
resolved.lastPayment.date.timestamp = moment(
|
|
710
|
+
(order.created || 0) * 1000
|
|
711
|
+
);
|
|
712
|
+
} else if (subscription) {
|
|
713
|
+
resolved.lastPayment.amount = subscription.amount_paid / 100;
|
|
714
|
+
resolved.lastPayment.date.timestamp = moment(
|
|
715
|
+
(subscription.created || 0) * 1000
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Get trial
|
|
720
|
+
const trialStart = get(resource, 'trial_start', 0) * 1000;
|
|
721
|
+
const trialEnd = get(resource, 'trial_end', 0) * 1000;
|
|
722
|
+
const trialDaysDifference = Math.abs(moment(trialEnd).diff(moment(trialStart), 'days'));
|
|
723
|
+
const trialClaimed = !!trialStart && !!trialEnd && trialDaysDifference > 1;
|
|
724
|
+
if (resource.status === 'trialing') {
|
|
725
|
+
resolved.trial.active = true;
|
|
726
|
+
|
|
727
|
+
// Set expiration
|
|
728
|
+
resolved.expires.timestamp = moment(
|
|
729
|
+
(
|
|
730
|
+
trialEnd
|
|
731
|
+
)
|
|
732
|
+
)
|
|
733
|
+
}
|
|
734
|
+
resolved.trial.claimed = trialClaimed;
|
|
735
|
+
|
|
736
|
+
// Resolve frequency
|
|
737
|
+
const unit = get(resource, 'plan.interval');
|
|
738
|
+
if (unit === 'year') {
|
|
739
|
+
resolved.frequency = 'annually';
|
|
740
|
+
} else if (unit === 'month') {
|
|
741
|
+
resolved.frequency = 'monthly';
|
|
742
|
+
} else if (unit === 'week') {
|
|
743
|
+
resolved.frequency = 'weekly';
|
|
744
|
+
} else if (unit === 'day') {
|
|
745
|
+
resolved.frequency = 'daily';
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// Set completed
|
|
749
|
+
if (resource.object === 'charge') {
|
|
750
|
+
resolved.payment.completed = !['requires_payment_method', 'requires_confirmation', 'requires_action', 'processing', 'requires_capture', 'canceled'].includes(resource.status);
|
|
751
|
+
} else {
|
|
752
|
+
resolved.payment.completed = !['incomplete', 'incomplete_expired'].includes(resource.status);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Check if refunded
|
|
756
|
+
if (resource.object === 'charge') {
|
|
757
|
+
resolved.payment.refunded = resource.refunded;
|
|
758
|
+
} else {
|
|
759
|
+
resolved.payment.refunded = get(resource, 'latest_invoice.charge.refunded', false);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
return resolved;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
SubscriptionResolver.prototype.resolve_coinbase = function (profile, resource, resolved, options) {
|
|
766
|
+
const self = this;
|
|
767
|
+
|
|
768
|
+
// Setup preliminary variables
|
|
769
|
+
const isOrder = profile.type === 'order';
|
|
770
|
+
|
|
771
|
+
// Set status
|
|
772
|
+
resolved.status = 'cancelled';
|
|
773
|
+
|
|
774
|
+
// Set resource ID
|
|
775
|
+
resolved.resource.id = resource.id;
|
|
776
|
+
|
|
777
|
+
// Set start
|
|
778
|
+
resolved.start.timestamp = moment(
|
|
779
|
+
get(resource, 'created_at', 0)
|
|
780
|
+
);
|
|
781
|
+
|
|
782
|
+
// Set expiration
|
|
783
|
+
resolved.expires.timestamp = moment(
|
|
784
|
+
get(resource, 'created_at', 0)
|
|
785
|
+
);
|
|
786
|
+
|
|
787
|
+
// Set cancelled
|
|
788
|
+
resolved.cancelled.timestamp = moment(
|
|
789
|
+
get(resource, 'created_at', 0)
|
|
790
|
+
)
|
|
791
|
+
|
|
792
|
+
// Retrieve last payment
|
|
793
|
+
const lastPayment = resource.payments.find(p => p.status === 'CONFIRMED');
|
|
794
|
+
|
|
795
|
+
// Set last payment
|
|
796
|
+
if (lastPayment) {
|
|
797
|
+
resolved.lastPayment.amount = parseFloat(lastPayment.value.local.amount);
|
|
798
|
+
resolved.lastPayment.date.timestamp = moment(lastPayment.detected_at);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// Get trial
|
|
802
|
+
if (true) {
|
|
803
|
+
resolved.trial.active = false;
|
|
804
|
+
}
|
|
805
|
+
resolved.trial.claimed = false;
|
|
806
|
+
|
|
807
|
+
// Resolve frequency
|
|
808
|
+
const unit = profile.details.planFrequency;
|
|
809
|
+
if (unit) {
|
|
810
|
+
resolved.frequency = unit;
|
|
811
|
+
} else {
|
|
812
|
+
resolved.frequency = 'single';
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// Set completed
|
|
816
|
+
if (true) {
|
|
817
|
+
resolved.payment.completed = !!lastPayment;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// Check if refunded
|
|
821
|
+
if (true) {
|
|
822
|
+
resolved.payment.refunded = false;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
return resolved;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
module.exports = SubscriptionResolver;
|
|
@@ -47,6 +47,9 @@ SubscriptionResolver.prototype.resolve = function (options) {
|
|
|
47
47
|
claimed: false,
|
|
48
48
|
active: false,
|
|
49
49
|
daysLeft: 0,
|
|
50
|
+
},
|
|
51
|
+
details: {
|
|
52
|
+
message: '',
|
|
50
53
|
}
|
|
51
54
|
}
|
|
52
55
|
|
|
@@ -58,6 +61,8 @@ SubscriptionResolver.prototype.resolve = function (options) {
|
|
|
58
61
|
profile.type = profile.type || null;
|
|
59
62
|
profile.details = profile.details || {};
|
|
60
63
|
profile.details.planFrequency = profile.details.planFrequency || null;
|
|
64
|
+
profile.authorization = profile.authorization || {};
|
|
65
|
+
profile.authorization.status = profile.authorization.status || 'pending';
|
|
61
66
|
|
|
62
67
|
// Set
|
|
63
68
|
options = options || {};
|
|
@@ -65,6 +70,7 @@ SubscriptionResolver.prototype.resolve = function (options) {
|
|
|
65
70
|
options.resolveProcessor = typeof options.resolveProcessor === 'undefined' ? false : options.resolveProcessor;
|
|
66
71
|
options.resolveType = typeof options.resolveType === 'undefined' ? false : options.resolveType;
|
|
67
72
|
options.today = typeof options.today === 'undefined' ? moment() : moment(options.today);
|
|
73
|
+
options.message = typeof options.message === 'undefined' ? true : options.message;
|
|
68
74
|
|
|
69
75
|
// Set provider if not set
|
|
70
76
|
if (!profile.processor) {
|
|
@@ -199,11 +205,22 @@ SubscriptionResolver.prototype.resolve = function (options) {
|
|
|
199
205
|
) {
|
|
200
206
|
resolved.expires.timestamp = moment(0);
|
|
201
207
|
// resolved.cancelled.timestamp = moment(0);
|
|
208
|
+
resolved.details.message = 'Most recent payment failed because there is no working payment method on file.'
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// If they are trialing and the authorization charge is failed, set to suspended
|
|
212
|
+
if (
|
|
213
|
+
resolved.trial.active
|
|
214
|
+
&& profile.authorization.status === 'failed'
|
|
215
|
+
) {
|
|
216
|
+
resolved.status = 'suspended';
|
|
217
|
+
resolved.details.message = 'Pre-payment authorization failed because there is no working payment method on file.'
|
|
202
218
|
}
|
|
203
219
|
|
|
204
220
|
// If they got a refund, set the expiration to 0
|
|
205
221
|
if (resolved.payment.refunded) {
|
|
206
222
|
resolved.expires.timestamp = moment(0);
|
|
223
|
+
resolved.details.message = 'Refund was issued so subscription is inactive.'
|
|
207
224
|
}
|
|
208
225
|
|
|
209
226
|
// If they are suspended, set the expiration to 0
|
|
@@ -235,6 +252,10 @@ SubscriptionResolver.prototype.resolve = function (options) {
|
|
|
235
252
|
console.log('resolved', resolved);
|
|
236
253
|
}
|
|
237
254
|
|
|
255
|
+
if (!options.message) {
|
|
256
|
+
resolved.details.message = '[REDACTED]';
|
|
257
|
+
}
|
|
258
|
+
|
|
238
259
|
self.resolved = resolved;
|
|
239
260
|
// console.log('----expires 6', resolved.resource.id, resolved.status, resolved.frequency, resolved.trial.active, resolved.expires.timestamp.toISOString ? resolved.expires.timestamp.toISOString() : resolved.expires.timestamp);
|
|
240
261
|
|