backend-manager 2.5.89 → 2.5.91
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backend-manager",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.91",
|
|
4
4
|
"description": "Quick tools for developing Firebase functions",
|
|
5
5
|
"main": "src/manager/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"bm": "./bin/bem"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"
|
|
11
|
+
"_test": "npm run prepare && ./node_modules/mocha/bin/mocha test/ --recursive --timeout=10000",
|
|
12
|
+
"test": "./node_modules/mocha/bin/mocha test/ --recursive --timeout=10000",
|
|
12
13
|
"start": "node src/index.js"
|
|
13
14
|
},
|
|
14
15
|
"repository": {
|
|
@@ -46,6 +47,7 @@
|
|
|
46
47
|
"lodash": "^4.17.21",
|
|
47
48
|
"lowdb": "^1.0.0",
|
|
48
49
|
"mailchimp-api-v3": "^1.15.0",
|
|
50
|
+
"mocha": "^8.4.0",
|
|
49
51
|
"moment": "^2.29.4",
|
|
50
52
|
"nanoid": "^3.3.6",
|
|
51
53
|
"node-fetch": "^2.6.9",
|
|
@@ -21,19 +21,27 @@ Module.prototype.main = function () {
|
|
|
21
21
|
|
|
22
22
|
let count = 0;
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
try {
|
|
25
|
+
await self.signOutOfSession(uid, session)
|
|
26
|
+
.then(r => count += r)
|
|
27
|
+
|
|
28
|
+
// Legacy for somiibo and old electron-manager
|
|
29
|
+
await self.signOutOfSession(uid, 'gatherings/online')
|
|
30
|
+
.then(r => count += r)
|
|
31
|
+
|
|
32
|
+
await self.libraries.admin
|
|
33
|
+
.auth()
|
|
34
|
+
.revokeRefreshTokens(uid)
|
|
35
|
+
.then(() => {
|
|
36
|
+
return resolve({data: {sessions: count, message: `Successfully signed ${uid} out of all sessions`}});
|
|
37
|
+
})
|
|
38
|
+
.catch(e => {
|
|
39
|
+
return reject(assistant.errorManager(`Failed to sign out of all sessions: ${e}`, {code: 500, sentry: false, send: false, log: false}).error)
|
|
40
|
+
})
|
|
41
|
+
} catch (e) {
|
|
42
|
+
assistant.console.log(`@temp sign-out-all-sessions error: ${e}`);
|
|
43
|
+
return reject(assistant.errorManager(`Failed to sign out of all sessions: ${e}`, {code: 500, sentry: false, send: false, log: false}).error)
|
|
44
|
+
}
|
|
37
45
|
})
|
|
38
46
|
.catch(e => {
|
|
39
47
|
return reject(e);
|
|
@@ -94,11 +94,11 @@ Module.prototype.main = function() {
|
|
|
94
94
|
})
|
|
95
95
|
.catch(e => {
|
|
96
96
|
// console.log('---e', e);
|
|
97
|
-
self.payload.response.status = e.code
|
|
97
|
+
self.payload.response.status = e && e.code ? e.code : 500;
|
|
98
98
|
self.payload.response.error = e || new Error('Unknown error occured');
|
|
99
99
|
})
|
|
100
100
|
} catch (e) {
|
|
101
|
-
self.payload.response.status = 500;
|
|
101
|
+
self.payload.response.status = e && e.code ? e.code : 500;
|
|
102
102
|
self.payload.response.error = e || new Error('Unknown error occured');
|
|
103
103
|
}
|
|
104
104
|
})
|
|
@@ -123,7 +123,7 @@ Module.prototype.main = function() {
|
|
|
123
123
|
return resolve();
|
|
124
124
|
}
|
|
125
125
|
} else {
|
|
126
|
-
|
|
126
|
+
self.assistant.error(`Error executing ${resolved.command} @ ${resolved.path} (status=${self.payload.response.status}):`, self.payload.response.error)
|
|
127
127
|
// return res.send(self.payload.response.error.message);
|
|
128
128
|
res.send(`${self.payload.response.error}`)
|
|
129
129
|
return reject(self.payload.response.error);
|
|
@@ -5,8 +5,8 @@ function SubscriptionResolver(Manager, profile, resource) {
|
|
|
5
5
|
const self = this;
|
|
6
6
|
|
|
7
7
|
self.Manager = Manager;
|
|
8
|
-
self.profile = profile;
|
|
9
|
-
self.resource = resource;
|
|
8
|
+
self.profile = profile || {};
|
|
9
|
+
self.resource = resource || {};
|
|
10
10
|
|
|
11
11
|
return self;
|
|
12
12
|
}
|
|
@@ -15,8 +15,8 @@ SubscriptionResolver.prototype.resolve = function (options) {
|
|
|
15
15
|
const self = this;
|
|
16
16
|
|
|
17
17
|
const resolved = {
|
|
18
|
-
status: '
|
|
19
|
-
frequency: '
|
|
18
|
+
status: '',
|
|
19
|
+
frequency: '',
|
|
20
20
|
resource: {
|
|
21
21
|
id: '',
|
|
22
22
|
},
|
|
@@ -53,372 +53,123 @@ SubscriptionResolver.prototype.resolve = function (options) {
|
|
|
53
53
|
const resource = self.resource;
|
|
54
54
|
|
|
55
55
|
// Set defaults
|
|
56
|
+
profile.type = profile.type || null;
|
|
56
57
|
profile.details = profile.details || {};
|
|
57
58
|
profile.details.planFrequency = profile.details.planFrequency || null;
|
|
58
59
|
|
|
59
60
|
// Set
|
|
60
61
|
options = options || {};
|
|
62
|
+
options.log = typeof options.log === 'undefined' ? false : options.log;
|
|
63
|
+
options.resolveProcessor = typeof options.resolveProcessor === 'undefined' ? false : options.resolveProcessor;
|
|
64
|
+
options.resolveType = typeof options.resolveType === 'undefined' ? false : options.resolveType;
|
|
65
|
+
options.today = typeof options.today === 'undefined' ? moment() : moment(options.today);
|
|
61
66
|
|
|
62
67
|
// Set provider if not set
|
|
63
68
|
if (!profile.processor) {
|
|
69
|
+
/*** PayPal ***/
|
|
70
|
+
// Order
|
|
64
71
|
if (
|
|
72
|
+
resource.purchase_units
|
|
73
|
+
) {
|
|
74
|
+
profile.processor = 'paypal';
|
|
75
|
+
profile.type = profile.type || 'order';
|
|
76
|
+
// Order
|
|
77
|
+
} else if (
|
|
65
78
|
// resource.billing_info
|
|
66
79
|
resource.create_time
|
|
67
80
|
) {
|
|
68
81
|
profile.processor = 'paypal';
|
|
82
|
+
profile.type = profile.type || 'subscription';
|
|
83
|
+
|
|
84
|
+
/*** Chargebee ***/
|
|
85
|
+
// Order
|
|
86
|
+
} else if (
|
|
87
|
+
resource.line_items
|
|
88
|
+
) {
|
|
89
|
+
profile.processor = 'chargebee';
|
|
90
|
+
profile.type = profile.type || 'order';
|
|
91
|
+
// Subscription
|
|
69
92
|
} else if (
|
|
70
93
|
resource.billing_period_unit
|
|
71
94
|
) {
|
|
72
95
|
profile.processor = 'chargebee';
|
|
96
|
+
profile.type = profile.type || 'subscription';
|
|
97
|
+
|
|
98
|
+
/*** Stripe ***/
|
|
99
|
+
// Order
|
|
73
100
|
} else if (
|
|
74
|
-
resource.
|
|
101
|
+
resource.object === 'charge'
|
|
75
102
|
) {
|
|
76
103
|
profile.processor = 'stripe';
|
|
104
|
+
profile.type = profile.type || 'order';
|
|
105
|
+
// Subscription
|
|
106
|
+
} else if (
|
|
107
|
+
resource.object === 'subscription'
|
|
108
|
+
) {
|
|
109
|
+
profile.processor = 'stripe';
|
|
110
|
+
profile.type = profile.type || 'subscription';
|
|
111
|
+
|
|
112
|
+
/*** Coinbase ***/
|
|
113
|
+
// Order AND Subscription
|
|
77
114
|
} else if (
|
|
78
115
|
resource.addresses
|
|
79
116
|
) {
|
|
80
117
|
profile.processor = 'coinbase';
|
|
118
|
+
// profile.type = profile.type || 'subscription';
|
|
119
|
+
|
|
120
|
+
/*** Error ***/
|
|
81
121
|
} else {
|
|
82
122
|
throw new Error('Unable to determine subscription provider');
|
|
83
123
|
}
|
|
84
124
|
}
|
|
85
125
|
|
|
126
|
+
// Set profile.type
|
|
127
|
+
if (!profile.type) {
|
|
128
|
+
profile.type = profile.type || 'subscription';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Set processor if needed
|
|
132
|
+
if (options.resolveProcessor) {
|
|
133
|
+
resolved.processor = profile.processor;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Set type if needed
|
|
137
|
+
if (options.resolveType) {
|
|
138
|
+
resolved.type = profile.type;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Set frequency if order
|
|
142
|
+
if (profile.type === 'order') {
|
|
143
|
+
resolved.frequency = 'single';
|
|
144
|
+
}
|
|
145
|
+
|
|
86
146
|
// Log if requested
|
|
87
147
|
if (options.log) {
|
|
88
148
|
console.log('profile', profile);
|
|
89
149
|
console.log('resource', resource);
|
|
90
150
|
}
|
|
91
151
|
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
subscription: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_get
|
|
97
|
-
APPROVAL_PENDING. The subscription is created but not yet approved by the buyer.
|
|
98
|
-
APPROVED. The buyer has approved the subscription.
|
|
99
|
-
ACTIVE. The subscription is active.
|
|
100
|
-
SUSPENDED. The subscription is suspended.
|
|
101
|
-
CANCELLED. The subscription is cancelled.
|
|
102
|
-
EXPIRED. The subscription is expired.
|
|
103
|
-
|
|
104
|
-
order: https://developer.paypal.com/docs/api/orders/v2/#orders_get
|
|
105
|
-
CREATED. The order was created with the specified context.
|
|
106
|
-
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.
|
|
107
|
-
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.
|
|
108
|
-
VOIDED. All purchase units in the order are voided. COMPLETED. The payment was authorized or the authorized payment was captured for the order.
|
|
109
|
-
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.
|
|
110
|
-
*/
|
|
111
|
-
if (['ACTIVE'].includes(resource.status)) {
|
|
112
|
-
resolved.status = 'active';
|
|
113
|
-
} else if (['SUSPENDED'].includes(resource.status)) {
|
|
114
|
-
resolved.status = 'suspended';
|
|
115
|
-
} else {
|
|
116
|
-
resolved.status = 'cancelled';
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Set resource ID
|
|
120
|
-
resolved.resource.id = resource.id;
|
|
121
|
-
|
|
122
|
-
// Set start
|
|
123
|
-
resolved.start.timestamp = moment(
|
|
124
|
-
get(resource, 'start_time', 0)
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
// Set expiration
|
|
128
|
-
resolved.expires.timestamp = moment(
|
|
129
|
-
get(resource, 'billing_info.last_payment.time', 0)
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
// Set cancelled
|
|
133
|
-
if (resolved.status === 'cancelled') {
|
|
134
|
-
resolved.cancelled.timestamp = moment(
|
|
135
|
-
get(resource, 'status_update_time', 0)
|
|
136
|
-
)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Set last payment
|
|
140
|
-
if (get(resource, 'billing_info.last_payment')) {
|
|
141
|
-
resolved.lastPayment.amount = parseFloat(resource.billing_info.last_payment.amount.value);
|
|
142
|
-
resolved.lastPayment.date.timestamp = moment(resource.billing_info.last_payment.time);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Get trial
|
|
146
|
-
const trialTenure = get(resource, 'plan.billing_cycles', []).find((cycle) => cycle.tenure_type === 'TRIAL');
|
|
147
|
-
const regularTenure = get(resource, 'plan.billing_cycles', []).find((cycle) => cycle.tenure_type === 'REGULAR');
|
|
148
|
-
|
|
149
|
-
// Resolve trial
|
|
150
|
-
/*
|
|
151
|
-
Special condition for PayPal
|
|
152
|
-
Because you cannot remove trial on a sub-level, you have to charge a prorated amount for the "trial".
|
|
153
|
-
Even if charged, it is still considered a trial period by paypal.
|
|
154
|
-
Thus, we must remove the trial indicator if the user has been charged.
|
|
155
|
-
*/
|
|
156
|
-
if (
|
|
157
|
-
resolved.status === 'active'
|
|
158
|
-
&& (trialTenure && regularTenure && regularTenure.total_cycles === 0)
|
|
159
|
-
&& resolved.lastPayment.amount === 0
|
|
160
|
-
) {
|
|
161
|
-
resolved.trial.active = true;
|
|
162
|
-
|
|
163
|
-
// Set expiration
|
|
164
|
-
resolved.expires.timestamp = moment(
|
|
165
|
-
get(resource, 'billing_info.next_billing_time', 0)
|
|
166
|
-
)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Resolve frequency
|
|
170
|
-
const unit = regularTenure.frequency.interval_unit;
|
|
171
|
-
if (unit === 'YEAR') {
|
|
172
|
-
resolved.frequency = 'annually';
|
|
173
|
-
} else if (unit === 'MONTH') {
|
|
174
|
-
resolved.frequency = 'monthly';
|
|
175
|
-
} else if (unit === 'WEEK') {
|
|
176
|
-
resolved.frequency = 'weekly';
|
|
177
|
-
} else if (unit === 'DAY') {
|
|
178
|
-
resolved.frequency = 'daily';
|
|
179
|
-
} else {
|
|
180
|
-
throw new Error('Unknown frequency');
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Set completed
|
|
184
|
-
if (resource.plan_id) {
|
|
185
|
-
resolved.payment.completed = !['APPROVAL_PENDING', 'APPROVED'].includes(resource.status);
|
|
186
|
-
} else {
|
|
187
|
-
resolved.payment.completed = !['CREATED', 'SAVED', 'APPROVED', 'VOIDED', 'PAYER_ACTION_REQUIRED'].includes(resource.status);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
} else if (profile.processor === 'chargebee') {
|
|
191
|
-
// Set status
|
|
192
|
-
// subscription: https://apidocs.chargebee.com/docs/api/subscriptions?prod_cat_ver=2#subscription_status
|
|
193
|
-
// future The subscription is scheduled to start at a future date.
|
|
194
|
-
// in_trial The subscription is in trial.
|
|
195
|
-
// active The subscription is active and will be charged for automatically based on the items in it.
|
|
196
|
-
// non_renewing The subscription will be canceled at the end of the current term.
|
|
197
|
-
// paused The subscription is paused. The subscription will not renew while in this state.
|
|
198
|
-
// cancelled The subscription has been canceled and is no longer in service.
|
|
199
|
-
if (['in_trial', 'active'].includes(resource.status)) {
|
|
200
|
-
resolved.status = 'active';
|
|
201
|
-
|
|
202
|
-
// If there's a due invoice, it's suspended
|
|
203
|
-
if (resource.total_dues > 0) {
|
|
204
|
-
resolved.status = 'suspended';
|
|
205
|
-
}
|
|
206
|
-
} else if (['paused'].includes(resource.status)) {
|
|
207
|
-
resolved.status = 'suspended';
|
|
208
|
-
} else {
|
|
209
|
-
resolved.status = 'cancelled';
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Set resource ID
|
|
213
|
-
resolved.resource.id = resource.id;
|
|
214
|
-
|
|
215
|
-
// Set start
|
|
216
|
-
resolved.start.timestamp = moment(
|
|
217
|
-
get(resource, 'created_at', 0) * 1000
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
// Set expiration
|
|
221
|
-
resolved.expires.timestamp = moment(
|
|
222
|
-
(
|
|
223
|
-
get(resource, 'current_term_start', 0)
|
|
224
|
-
) * 1000
|
|
225
|
-
)
|
|
226
|
-
// if (resource.total_dues > 0) {
|
|
227
|
-
// resolved.expires.timestamp = moment(0);
|
|
228
|
-
// } else {
|
|
229
|
-
// resolved.expires.timestamp = moment(
|
|
230
|
-
// (
|
|
231
|
-
// get(resource, 'current_term_start', 0)
|
|
232
|
-
// ) * 1000
|
|
233
|
-
// )
|
|
234
|
-
// }
|
|
235
|
-
|
|
236
|
-
// Set cancelled
|
|
237
|
-
if (resolved.status === 'cancelled') {
|
|
238
|
-
resolved.cancelled.timestamp = moment(
|
|
239
|
-
get(resource, 'cancelled_at', 0) * 1000
|
|
240
|
-
)
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Set last payment
|
|
244
|
-
if (resource.total_dues > 0) {
|
|
245
|
-
resolved.lastPayment.amount = 0;
|
|
246
|
-
resolved.lastPayment.date.timestamp = moment(
|
|
247
|
-
(resource.due_since || 0) * 1000
|
|
248
|
-
);
|
|
249
|
-
} else {
|
|
250
|
-
resolved.lastPayment.amount = resource.plan_amount / 100;
|
|
251
|
-
resolved.lastPayment.date.timestamp = moment(
|
|
252
|
-
(resource.current_term_start || 0) * 1000
|
|
253
|
-
);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Get trial
|
|
257
|
-
if (resource.status === 'in_trial') {
|
|
258
|
-
resolved.trial.active = true;
|
|
259
|
-
|
|
260
|
-
// Set expiration
|
|
261
|
-
resolved.expires.timestamp = moment(
|
|
262
|
-
(
|
|
263
|
-
get(resource, 'trial_end', 0)
|
|
264
|
-
) * 1000
|
|
265
|
-
)
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Resolve frequency
|
|
269
|
-
const unit = resource.billing_period_unit;
|
|
270
|
-
if (unit === 'year') {
|
|
271
|
-
resolved.frequency = 'annually';
|
|
272
|
-
} else if (unit === 'month') {
|
|
273
|
-
resolved.frequency = 'monthly';
|
|
274
|
-
} else if (unit === 'week') {
|
|
275
|
-
resolved.frequency = 'weekly';
|
|
276
|
-
} else if (unit === 'day') {
|
|
277
|
-
resolved.frequency = 'daily';
|
|
278
|
-
} else {
|
|
279
|
-
throw new Error('Unknown frequency');
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Set completed
|
|
283
|
-
if (true) {
|
|
284
|
-
resolved.payment.completed = !['future'].includes(resource.status);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
} else if (profile.processor === 'stripe') {
|
|
288
|
-
// Subscription: https://stripe.com/docs/api/subscriptions/object#subscription_object-status
|
|
289
|
-
// incomplete, incomplete_expired, trialing, active, past_due, canceled, or unpaid
|
|
290
|
-
|
|
291
|
-
// Charge: https://stripe.com/docs/api/payment_intents/object#payment_intent_object-status
|
|
292
|
-
// requires_payment_method, requires_confirmation, requires_action, processing, requires_capture, canceled, or succeeded
|
|
293
|
-
// Set status
|
|
294
|
-
if (['trialing', 'active'].includes(resource.status)) {
|
|
295
|
-
resolved.status = 'active';
|
|
296
|
-
} else if (['past_due', 'unpaid'].includes(resource.status)) {
|
|
297
|
-
resolved.status = 'suspended';
|
|
298
|
-
} else {
|
|
299
|
-
resolved.status = 'cancelled';
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Set resource ID
|
|
303
|
-
resolved.resource.id = resource.id;
|
|
304
|
-
|
|
305
|
-
// Set start
|
|
306
|
-
resolved.start.timestamp = moment(
|
|
307
|
-
get(resource, 'start_date', 0) * 1000
|
|
308
|
-
);
|
|
309
|
-
|
|
310
|
-
// Set expiration
|
|
311
|
-
resolved.expires.timestamp = moment(
|
|
312
|
-
get(resource, 'current_period_start', 0) * 1000
|
|
313
|
-
);
|
|
314
|
-
|
|
315
|
-
// Set cancelled
|
|
316
|
-
if (resolved.status === 'cancelled') {
|
|
317
|
-
resolved.cancelled.timestamp = moment(
|
|
318
|
-
get(resource, 'canceled_at', 0) * 1000
|
|
319
|
-
)
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Set last payment
|
|
323
|
-
// TODO: check if suspended payments are handled correctly when using resource.latest_invoice.amount_paid
|
|
324
|
-
if (resource.latest_invoice) {
|
|
325
|
-
resolved.lastPayment.amount = resource.latest_invoice.amount_paid / 100;
|
|
326
|
-
resolved.lastPayment.date.timestamp = moment(
|
|
327
|
-
get(resource, 'latest_invoice.created', 0) * 1000
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Get trial
|
|
332
|
-
if (resource.status === 'trialing') {
|
|
333
|
-
resolved.trial.active = true;
|
|
334
|
-
|
|
335
|
-
// Set expiration
|
|
336
|
-
resolved.expires.timestamp = moment(
|
|
337
|
-
(
|
|
338
|
-
get(resource, 'trial_end', 0)
|
|
339
|
-
) * 1000
|
|
340
|
-
)
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// Resolve frequency
|
|
344
|
-
const unit = resource.plan.interval;
|
|
345
|
-
if (unit === 'year') {
|
|
346
|
-
resolved.frequency = 'annually';
|
|
347
|
-
} else if (unit === 'month') {
|
|
348
|
-
resolved.frequency = 'monthly';
|
|
349
|
-
} else if (unit === 'week') {
|
|
350
|
-
resolved.frequency = 'weekly';
|
|
351
|
-
} else if (unit === 'day') {
|
|
352
|
-
resolved.frequency = 'daily';
|
|
353
|
-
} else {
|
|
354
|
-
throw new Error('Unknown frequency');
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// Set completed
|
|
358
|
-
if (resource.object === 'subscription') {
|
|
359
|
-
resolved.payment.completed = !['incomplete', 'incomplete_expired'].includes(resource.status);
|
|
360
|
-
} else if (resource.object === 'payment_intent') {
|
|
361
|
-
resolved.payment.completed = !['requires_payment_method', 'requires_confirmation', 'requires_action', 'processing', 'requires_capture', 'canceled'].includes(resource.status);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
} else if (profile.processor === 'coinbase') {
|
|
365
|
-
// Set status
|
|
366
|
-
resolved.status = 'cancelled';
|
|
367
|
-
|
|
368
|
-
// Set resource ID
|
|
369
|
-
resolved.resource.id = resource.id;
|
|
370
|
-
|
|
371
|
-
// Set start
|
|
372
|
-
resolved.start.timestamp = moment(
|
|
373
|
-
get(resource, 'created_at', 0)
|
|
374
|
-
);
|
|
375
|
-
|
|
376
|
-
// Set expiration
|
|
377
|
-
resolved.expires.timestamp = moment(
|
|
378
|
-
get(resource, 'created_at', 0)
|
|
379
|
-
);
|
|
380
|
-
|
|
381
|
-
// Set cancelled
|
|
382
|
-
resolved.cancelled.timestamp = moment(
|
|
383
|
-
get(resource, 'created_at', 0)
|
|
384
|
-
)
|
|
385
|
-
|
|
386
|
-
// Retrieve last payment
|
|
387
|
-
const lastPayment = resource.payments.find(p => p.status === 'CONFIRMED');
|
|
388
|
-
|
|
389
|
-
// Set last payment
|
|
390
|
-
if (lastPayment) {
|
|
391
|
-
resolved.lastPayment.amount = parseFloat(lastPayment.value.local.amount);
|
|
392
|
-
resolved.lastPayment.date.timestamp = moment(lastPayment.detected_at);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
// Get trial
|
|
396
|
-
if (true) {
|
|
397
|
-
resolved.trial.active = false;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// Resolve frequency
|
|
401
|
-
const unit = profile.details.planFrequency;
|
|
402
|
-
if (unit) {
|
|
403
|
-
resolved.frequency = unit;
|
|
404
|
-
} else {
|
|
405
|
-
throw new Error('Unknown frequency');
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// Set completed
|
|
409
|
-
if (true) {
|
|
410
|
-
resolved.payment.completed = !!lastPayment;
|
|
411
|
-
}
|
|
412
|
-
|
|
152
|
+
// Resolve
|
|
153
|
+
const processor = self[`resolve_${profile.processor}`];
|
|
154
|
+
if (processor) {
|
|
155
|
+
processor(profile, resource, resolved);
|
|
413
156
|
} else {
|
|
414
157
|
throw new Error('Unknown processor');
|
|
415
158
|
}
|
|
416
159
|
|
|
160
|
+
// console.log('---resolved', resolved);
|
|
161
|
+
|
|
162
|
+
// Check for frequency
|
|
163
|
+
if (!resolved.frequency) {
|
|
164
|
+
throw new Error('Unknown frequency');
|
|
165
|
+
}
|
|
166
|
+
|
|
417
167
|
// Fix expiry by adding time to the date of last payment
|
|
168
|
+
// 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);
|
|
418
169
|
if (resolved.status === 'active') {
|
|
419
170
|
// Set days left
|
|
420
171
|
if (resolved.trial.active) {
|
|
421
|
-
resolved.trial.daysLeft = resolved.expires.timestamp.diff(
|
|
172
|
+
resolved.trial.daysLeft = resolved.expires.timestamp.diff(options.today, 'days');
|
|
422
173
|
}
|
|
423
174
|
|
|
424
175
|
// Set expiration
|
|
@@ -437,6 +188,7 @@ SubscriptionResolver.prototype.resolve = function (options) {
|
|
|
437
188
|
}
|
|
438
189
|
}
|
|
439
190
|
}
|
|
191
|
+
// 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);
|
|
440
192
|
|
|
441
193
|
// If they are not trialing AND there was NEVER any payment sent OR the last payment failed, then set the expiration to 0
|
|
442
194
|
if (
|
|
@@ -445,13 +197,14 @@ SubscriptionResolver.prototype.resolve = function (options) {
|
|
|
445
197
|
) {
|
|
446
198
|
resolved.expires.timestamp = moment(0);
|
|
447
199
|
}
|
|
200
|
+
// 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);
|
|
448
201
|
|
|
449
202
|
// Fix timestamps
|
|
450
203
|
resolved.start.timestampUNIX = resolved.start.timestamp.unix();
|
|
451
204
|
resolved.start.timestamp = resolved.start.timestamp.toISOString();
|
|
452
205
|
|
|
453
206
|
resolved.expires.timestampUNIX = resolved.expires.timestamp.unix();
|
|
454
|
-
resolved.expires.timestamp = resolved.expires.timestamp.toISOString();
|
|
207
|
+
resolved.expires.timestamp = resolved.expires.timestamp.toISOString ? resolved.expires.timestamp.toISOString() : resolved.expires.timestamp;
|
|
455
208
|
|
|
456
209
|
resolved.cancelled.timestampUNIX = resolved.cancelled.timestamp.unix();
|
|
457
210
|
resolved.cancelled.timestamp = resolved.cancelled.timestamp.toISOString();
|
|
@@ -469,8 +222,476 @@ SubscriptionResolver.prototype.resolve = function (options) {
|
|
|
469
222
|
}
|
|
470
223
|
|
|
471
224
|
self.resolved = resolved;
|
|
225
|
+
// console.log('----expires 5', resolved.resource.id, resolved.status, resolved.frequency, resolved.trial.active, resolved.expires.timestamp.toISOString ? resolved.expires.timestamp.toISOString() : resolved.expires.timestamp);
|
|
472
226
|
|
|
473
227
|
return resolved;
|
|
474
228
|
};
|
|
475
229
|
|
|
230
|
+
SubscriptionResolver.prototype.resolve_paypal = function (profile, resource, resolved) {
|
|
231
|
+
const self = this;
|
|
232
|
+
|
|
233
|
+
// Set status
|
|
234
|
+
/*
|
|
235
|
+
subscription: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_get
|
|
236
|
+
APPROVAL_PENDING. The subscription is created but not yet approved by the buyer.
|
|
237
|
+
APPROVED. The buyer has approved the subscription.
|
|
238
|
+
ACTIVE. The subscription is active.
|
|
239
|
+
SUSPENDED. The subscription is suspended.
|
|
240
|
+
CANCELLED. The subscription is cancelled.
|
|
241
|
+
EXPIRED. The subscription is expired.
|
|
242
|
+
|
|
243
|
+
order: https://developer.paypal.com/docs/api/orders/v2/#orders_get
|
|
244
|
+
CREATED The order was created with the specified context.
|
|
245
|
+
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.
|
|
246
|
+
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.
|
|
247
|
+
VOIDED All purchase units in the order are voided.
|
|
248
|
+
COMPLETED The payment was authorized or the authorized payment was captured for the order.
|
|
249
|
+
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.
|
|
250
|
+
*/
|
|
251
|
+
if (['ACTIVE'].includes(resource.status)) {
|
|
252
|
+
resolved.status = 'active';
|
|
253
|
+
} else if (['SUSPENDED'].includes(resource.status)) {
|
|
254
|
+
resolved.status = 'suspended';
|
|
255
|
+
} else {
|
|
256
|
+
resolved.status = 'cancelled';
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Set resource ID
|
|
260
|
+
resolved.resource.id = resource.id;
|
|
261
|
+
|
|
262
|
+
// Set start
|
|
263
|
+
resolved.start.timestamp = moment(
|
|
264
|
+
(
|
|
265
|
+
// Subscription
|
|
266
|
+
get(resource, 'start_time', 0)
|
|
267
|
+
|
|
268
|
+
// Order
|
|
269
|
+
|| get(resource, 'create_time', 0)
|
|
270
|
+
)
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
// Set expiration
|
|
274
|
+
resolved.expires.timestamp = moment(
|
|
275
|
+
(
|
|
276
|
+
// Subscription
|
|
277
|
+
get(resource, 'billing_info.last_payment.time', 0)
|
|
278
|
+
|
|
279
|
+
// Order
|
|
280
|
+
|| get(resource, 'create_time', 0)
|
|
281
|
+
)
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
// Set cancelled
|
|
285
|
+
if (resolved.status === 'cancelled') {
|
|
286
|
+
resolved.cancelled.timestamp = moment(
|
|
287
|
+
(
|
|
288
|
+
// Subscription
|
|
289
|
+
get(resource, 'status_update_time', 0)
|
|
290
|
+
|
|
291
|
+
// Order
|
|
292
|
+
|| get(resource, 'create_time', 0)
|
|
293
|
+
)
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Set last payment
|
|
298
|
+
const order = get(resource, 'purchase_units[0].payments.captures[0]');
|
|
299
|
+
const subscription = get(resource, 'billing_info.last_payment');
|
|
300
|
+
if (order) {
|
|
301
|
+
resolved.lastPayment.amount = parseFloat(
|
|
302
|
+
get(order, 'amount.value', '0.00')
|
|
303
|
+
);
|
|
304
|
+
resolved.lastPayment.date.timestamp = moment(
|
|
305
|
+
order.create_time || 0
|
|
306
|
+
);
|
|
307
|
+
} else if (subscription) {
|
|
308
|
+
resolved.lastPayment.amount = parseFloat(subscription.amount.value);
|
|
309
|
+
resolved.lastPayment.date.timestamp = moment(subscription.time);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Get trial
|
|
313
|
+
const trialTenure = get(resource, 'plan.billing_cycles', []).find((cycle) => cycle.tenure_type === 'TRIAL');
|
|
314
|
+
const regularTenure = get(resource, 'plan.billing_cycles', []).find((cycle) => cycle.tenure_type === 'REGULAR');
|
|
315
|
+
|
|
316
|
+
// Resolve trial
|
|
317
|
+
/*
|
|
318
|
+
Special condition for PayPal
|
|
319
|
+
Because you cannot remove trial on a sub-level, you have to charge a prorated amount for the "trial".
|
|
320
|
+
Even if charged, it is still considered a trial period by paypal.
|
|
321
|
+
Thus, we must remove the trial indicator if the user has been charged.
|
|
322
|
+
*/
|
|
323
|
+
if (
|
|
324
|
+
resolved.status === 'active'
|
|
325
|
+
&& (trialTenure && regularTenure && regularTenure.total_cycles === 0)
|
|
326
|
+
&& resolved.lastPayment.amount === 0
|
|
327
|
+
) {
|
|
328
|
+
resolved.trial.active = true;
|
|
329
|
+
|
|
330
|
+
// Set expiration
|
|
331
|
+
resolved.expires.timestamp = moment(
|
|
332
|
+
get(resource, 'billing_info.next_billing_time', 0)
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Resolve frequency
|
|
337
|
+
const unit = get(regularTenure, 'frequency.interval_unit');
|
|
338
|
+
if (unit === 'YEAR') {
|
|
339
|
+
resolved.frequency = 'annually';
|
|
340
|
+
} else if (unit === 'MONTH') {
|
|
341
|
+
resolved.frequency = 'monthly';
|
|
342
|
+
} else if (unit === 'WEEK') {
|
|
343
|
+
resolved.frequency = 'weekly';
|
|
344
|
+
} else if (unit === 'DAY') {
|
|
345
|
+
resolved.frequency = 'daily';
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Set completed
|
|
349
|
+
if (!resource.billing_info) {
|
|
350
|
+
resolved.payment.completed = !['CREATED', 'SAVED', 'APPROVED', 'VOIDED', 'PAYER_ACTION_REQUIRED'].includes(resource.status);
|
|
351
|
+
} else {
|
|
352
|
+
resolved.payment.completed = !['APPROVAL_PENDING', 'APPROVED'].includes(resource.status);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return resolved;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
SubscriptionResolver.prototype.resolve_chargebee = function (profile, resource, resolved) {
|
|
359
|
+
const self = this;
|
|
360
|
+
|
|
361
|
+
// Set status
|
|
362
|
+
// subscription: https://apidocs.chargebee.com/docs/api/subscriptions?prod_cat_ver=2#subscription_status
|
|
363
|
+
// future The subscription is scheduled to start at a future date.
|
|
364
|
+
// in_trial The subscription is in trial.
|
|
365
|
+
// active The subscription is active and will be charged for automatically based on the items in it.
|
|
366
|
+
// non_renewing The subscription will be canceled at the end of the current term.
|
|
367
|
+
// paused The subscription is paused. The subscription will not renew while in this state.
|
|
368
|
+
// cancelled The subscription has been canceled and is no longer in service.
|
|
369
|
+
|
|
370
|
+
// order: https://apidocs.chargebee.com/docs/api/invoices?prod_cat_ver=2#invoice_status
|
|
371
|
+
// paid: Indicates a paid invoice.
|
|
372
|
+
// posted: Indicates the payment is not yet collected and will be in this state till the due date to indicate the due period.
|
|
373
|
+
// payment_due: Indicates the payment is not yet collected and is being retried as per retry settings.
|
|
374
|
+
// not_paid: Indicates the payment is not made and all attempts to collect is failed.
|
|
375
|
+
// voided: Indicates a voided invoice.
|
|
376
|
+
// 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.
|
|
377
|
+
|
|
378
|
+
if (['in_trial', 'active'].includes(resource.status)) {
|
|
379
|
+
resolved.status = 'active';
|
|
380
|
+
|
|
381
|
+
// If there's a due invoice, it's suspended
|
|
382
|
+
if (resource.total_dues > 0) {
|
|
383
|
+
resolved.status = 'suspended';
|
|
384
|
+
}
|
|
385
|
+
} else if (['paused'].includes(resource.status)) {
|
|
386
|
+
resolved.status = 'suspended';
|
|
387
|
+
} else {
|
|
388
|
+
resolved.status = 'cancelled';
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Set resource ID
|
|
392
|
+
resolved.resource.id = resource.id;
|
|
393
|
+
|
|
394
|
+
// Set start
|
|
395
|
+
resolved.start.timestamp = moment(
|
|
396
|
+
(
|
|
397
|
+
// Order
|
|
398
|
+
get(resource, 'date', 0)
|
|
399
|
+
|
|
400
|
+
// Subscription
|
|
401
|
+
|| get(resource, 'created_at', 0)
|
|
402
|
+
) * 1000
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
// Set expiration
|
|
406
|
+
resolved.expires.timestamp = moment(
|
|
407
|
+
(
|
|
408
|
+
// Order
|
|
409
|
+
get(resource, 'date', 0)
|
|
410
|
+
|
|
411
|
+
// Subscription
|
|
412
|
+
|| get(resource, 'current_term_start', 0)
|
|
413
|
+
) * 1000
|
|
414
|
+
)
|
|
415
|
+
// console.log('---resolved.expires 1', resolved.expires);
|
|
416
|
+
// if (resource.total_dues > 0) {
|
|
417
|
+
// resolved.expires.timestamp = moment(0);
|
|
418
|
+
// } else {
|
|
419
|
+
// resolved.expires.timestamp = moment(
|
|
420
|
+
// (
|
|
421
|
+
// get(resource, 'current_term_start', 0)
|
|
422
|
+
// ) * 1000
|
|
423
|
+
// )
|
|
424
|
+
// }
|
|
425
|
+
|
|
426
|
+
// Set cancelled
|
|
427
|
+
if (resolved.status === 'cancelled') {
|
|
428
|
+
resolved.cancelled.timestamp = moment(
|
|
429
|
+
(
|
|
430
|
+
// Order
|
|
431
|
+
get(resource, 'date', 0)
|
|
432
|
+
|
|
433
|
+
// Subscription
|
|
434
|
+
|| get(resource, 'cancelled_at', 0)
|
|
435
|
+
) * 1000
|
|
436
|
+
)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Set last payment
|
|
440
|
+
if (
|
|
441
|
+
// Order
|
|
442
|
+
resource.amount_due > 0
|
|
443
|
+
|
|
444
|
+
// Subscription
|
|
445
|
+
|| resource.total_dues > 0
|
|
446
|
+
) {
|
|
447
|
+
resolved.lastPayment.amount = 0;
|
|
448
|
+
resolved.lastPayment.date.timestamp = moment(
|
|
449
|
+
(
|
|
450
|
+
// Order
|
|
451
|
+
(resource.date || 0)
|
|
452
|
+
|
|
453
|
+
// Subscription
|
|
454
|
+
|| (resource.due_since || 0)
|
|
455
|
+
) * 1000
|
|
456
|
+
);
|
|
457
|
+
} else {
|
|
458
|
+
resolved.lastPayment.amount = (
|
|
459
|
+
(
|
|
460
|
+
// Order
|
|
461
|
+
(resource.amount_paid)
|
|
462
|
+
|
|
463
|
+
// Order
|
|
464
|
+
|| (resource.plan_amount)
|
|
465
|
+
) / 100
|
|
466
|
+
)
|
|
467
|
+
resolved.lastPayment.date.timestamp = moment(
|
|
468
|
+
(
|
|
469
|
+
// Order
|
|
470
|
+
(resource.date || 0)
|
|
471
|
+
|
|
472
|
+
// Subscription
|
|
473
|
+
|| (resource.current_term_start || 0)
|
|
474
|
+
) * 1000
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Get trial
|
|
479
|
+
if (resource.status === 'in_trial') {
|
|
480
|
+
resolved.trial.active = true;
|
|
481
|
+
|
|
482
|
+
// Set expiration
|
|
483
|
+
resolved.expires.timestamp = moment(
|
|
484
|
+
(
|
|
485
|
+
get(resource, 'trial_end', 0)
|
|
486
|
+
) * 1000
|
|
487
|
+
)
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Resolve frequency
|
|
491
|
+
const unit = get(resource, 'billing_period_unit');
|
|
492
|
+
if (unit === 'year') {
|
|
493
|
+
resolved.frequency = 'annually';
|
|
494
|
+
} else if (unit === 'month') {
|
|
495
|
+
resolved.frequency = 'monthly';
|
|
496
|
+
} else if (unit === 'week') {
|
|
497
|
+
resolved.frequency = 'weekly';
|
|
498
|
+
} else if (unit === 'day') {
|
|
499
|
+
resolved.frequency = 'daily';
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Set completed
|
|
503
|
+
if (profile.type === 'order') {
|
|
504
|
+
resolved.payment.completed = !['posted', 'payment_due', 'not_paid', 'voided', 'pending'].includes(resource.status);
|
|
505
|
+
} else {
|
|
506
|
+
resolved.payment.completed = !['future'].includes(resource.status);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Special chargebee reset lastPayment
|
|
510
|
+
// If trial is active OR if it was cancelled after the trial has ended
|
|
511
|
+
const trialEnd = get(resource, 'trial_end', 0);
|
|
512
|
+
const cancelledAt = get(resource, 'cancelled_at', 0);
|
|
513
|
+
if (
|
|
514
|
+
resolved.trial.active
|
|
515
|
+
|| (trialEnd > 0 && cancelledAt > 0 && cancelledAt === trialEnd)
|
|
516
|
+
) {
|
|
517
|
+
resolved.lastPayment.amount = 0;
|
|
518
|
+
resolved.lastPayment.date.timestamp = moment(0);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// console.log('----expires 1', resolved.resource.id, resolved.status, resolved.frequency, resolved.trial.active, resolved.expires.timestamp.toISOString ? resolved.expires.timestamp.toISOString() : resolved.expires.timestamp);
|
|
522
|
+
|
|
523
|
+
return resolved;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
SubscriptionResolver.prototype.resolve_stripe = function (profile, resource, resolved) {
|
|
527
|
+
const self = this;
|
|
528
|
+
|
|
529
|
+
// Subscription: https://stripe.com/docs/api/subscriptions/object#subscription_object-status
|
|
530
|
+
// incomplete
|
|
531
|
+
// incomplete_expired
|
|
532
|
+
// trialing
|
|
533
|
+
// active
|
|
534
|
+
// past_due
|
|
535
|
+
// canceled
|
|
536
|
+
// unpaid
|
|
537
|
+
|
|
538
|
+
// Charge: https://stripe.com/docs/api/payment_intents/object#payment_intent_object-status
|
|
539
|
+
// requires_payment_method
|
|
540
|
+
// requires_confirmation
|
|
541
|
+
// requires_action
|
|
542
|
+
// processing
|
|
543
|
+
// requires_capture
|
|
544
|
+
// canceled
|
|
545
|
+
// succeeded
|
|
546
|
+
// Set status
|
|
547
|
+
if (['trialing', 'active'].includes(resource.status)) {
|
|
548
|
+
resolved.status = 'active';
|
|
549
|
+
} else if (['past_due', 'unpaid'].includes(resource.status)) {
|
|
550
|
+
resolved.status = 'suspended';
|
|
551
|
+
} else {
|
|
552
|
+
resolved.status = 'cancelled';
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Set resource ID
|
|
556
|
+
resolved.resource.id = resource.id;
|
|
557
|
+
|
|
558
|
+
// Set start
|
|
559
|
+
resolved.start.timestamp = moment(
|
|
560
|
+
(
|
|
561
|
+
// Order
|
|
562
|
+
get(resource, 'created', 0)
|
|
563
|
+
|
|
564
|
+
// Subscription
|
|
565
|
+
|| get(resource, 'start_date', 0)
|
|
566
|
+
) * 1000
|
|
567
|
+
);
|
|
568
|
+
|
|
569
|
+
// Set expiration
|
|
570
|
+
resolved.expires.timestamp = moment(
|
|
571
|
+
(
|
|
572
|
+
// Order
|
|
573
|
+
get(resource, 'created', 0)
|
|
574
|
+
|
|
575
|
+
// Subscription
|
|
576
|
+
|| get(resource, 'current_period_start', 0)
|
|
577
|
+
) * 1000
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
// Set cancelled
|
|
581
|
+
if (resolved.status === 'cancelled') {
|
|
582
|
+
resolved.cancelled.timestamp = moment(
|
|
583
|
+
(
|
|
584
|
+
// Order
|
|
585
|
+
get(resource, 'created', 0)
|
|
586
|
+
|
|
587
|
+
// Subscription
|
|
588
|
+
|| get(resource, 'canceled_at', 0)
|
|
589
|
+
) * 1000
|
|
590
|
+
)
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Set last payment
|
|
594
|
+
// TODO: check if suspended payments are handled correctly when using resource.latest_invoice.amount_paid
|
|
595
|
+
const order = resource.object === 'charge' ? resource : null;
|
|
596
|
+
const subscription = get(resource, 'latest_invoice');
|
|
597
|
+
if (order) {
|
|
598
|
+
resolved.lastPayment.amount = order.amount_captured / 100;
|
|
599
|
+
resolved.lastPayment.date.timestamp = moment(
|
|
600
|
+
(order.created || 0) * 1000
|
|
601
|
+
);
|
|
602
|
+
} else if (subscription) {
|
|
603
|
+
resolved.lastPayment.amount = subscription.amount_paid / 100;
|
|
604
|
+
resolved.lastPayment.date.timestamp = moment(
|
|
605
|
+
(subscription.created || 0) * 1000
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Get trial
|
|
610
|
+
if (resource.status === 'trialing') {
|
|
611
|
+
resolved.trial.active = true;
|
|
612
|
+
|
|
613
|
+
// Set expiration
|
|
614
|
+
resolved.expires.timestamp = moment(
|
|
615
|
+
(
|
|
616
|
+
get(resource, 'trial_end', 0)
|
|
617
|
+
) * 1000
|
|
618
|
+
)
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Resolve frequency
|
|
622
|
+
const unit = get(resource, 'plan.interval');
|
|
623
|
+
if (unit === 'year') {
|
|
624
|
+
resolved.frequency = 'annually';
|
|
625
|
+
} else if (unit === 'month') {
|
|
626
|
+
resolved.frequency = 'monthly';
|
|
627
|
+
} else if (unit === 'week') {
|
|
628
|
+
resolved.frequency = 'weekly';
|
|
629
|
+
} else if (unit === 'day') {
|
|
630
|
+
resolved.frequency = 'daily';
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Set completed
|
|
634
|
+
if (resource.object === 'charge') {
|
|
635
|
+
resolved.payment.completed = !['requires_payment_method', 'requires_confirmation', 'requires_action', 'processing', 'requires_capture', 'canceled'].includes(resource.status);
|
|
636
|
+
} else {
|
|
637
|
+
resolved.payment.completed = !['incomplete', 'incomplete_expired'].includes(resource.status);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
return resolved;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
SubscriptionResolver.prototype.resolve_coinbase = function (profile, resource, resolved) {
|
|
644
|
+
const self = this;
|
|
645
|
+
|
|
646
|
+
// Set status
|
|
647
|
+
resolved.status = 'cancelled';
|
|
648
|
+
|
|
649
|
+
// Set resource ID
|
|
650
|
+
resolved.resource.id = resource.id;
|
|
651
|
+
|
|
652
|
+
// Set start
|
|
653
|
+
resolved.start.timestamp = moment(
|
|
654
|
+
get(resource, 'created_at', 0)
|
|
655
|
+
);
|
|
656
|
+
|
|
657
|
+
// Set expiration
|
|
658
|
+
resolved.expires.timestamp = moment(
|
|
659
|
+
get(resource, 'created_at', 0)
|
|
660
|
+
);
|
|
661
|
+
|
|
662
|
+
// Set cancelled
|
|
663
|
+
resolved.cancelled.timestamp = moment(
|
|
664
|
+
get(resource, 'created_at', 0)
|
|
665
|
+
)
|
|
666
|
+
|
|
667
|
+
// Retrieve last payment
|
|
668
|
+
const lastPayment = resource.payments.find(p => p.status === 'CONFIRMED');
|
|
669
|
+
|
|
670
|
+
// Set last payment
|
|
671
|
+
if (lastPayment) {
|
|
672
|
+
resolved.lastPayment.amount = parseFloat(lastPayment.value.local.amount);
|
|
673
|
+
resolved.lastPayment.date.timestamp = moment(lastPayment.detected_at);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// Get trial
|
|
677
|
+
if (true) {
|
|
678
|
+
resolved.trial.active = false;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// Resolve frequency
|
|
682
|
+
const unit = profile.details.planFrequency;
|
|
683
|
+
if (unit) {
|
|
684
|
+
resolved.frequency = unit;
|
|
685
|
+
} else {
|
|
686
|
+
resolved.frequency = 'single';
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Set completed
|
|
690
|
+
if (true) {
|
|
691
|
+
resolved.payment.completed = !!lastPayment;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
return resolved;
|
|
695
|
+
}
|
|
696
|
+
|
|
476
697
|
module.exports = SubscriptionResolver;
|