backend-manager 5.0.167 → 5.0.169
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/CHANGELOG.md +4 -0
- package/TODO-2.md +18 -0
- package/package.json +1 -1
- package/src/manager/cron/frequent/abandoned-carts.js +2 -2
- package/src/manager/events/firestore/payments-webhooks/on-write.js +10 -0
- package/src/manager/helpers/middleware.js +3 -1
- package/src/manager/helpers/usage.js +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
14
14
|
- `Fixed` for any bug fixes.
|
|
15
15
|
- `Security` in case of vulnerabilities.
|
|
16
16
|
|
|
17
|
+
# [5.0.168] - 2026-03-21
|
|
18
|
+
### Fixed
|
|
19
|
+
- Immediately suspend subscription on payment denial (PAYMENT.SALE.DENIED, invoice.payment_failed) instead of waiting for the processor to give up retrying — recovery via PAYMENT.SALE.COMPLETED restores active status
|
|
20
|
+
|
|
17
21
|
# [5.0.167] - 2026-03-20
|
|
18
22
|
### Changed
|
|
19
23
|
- Extracted `resolveTemperature()` helper for consistency with `resolveFormatting()` and `resolveReasoning()`
|
package/TODO-2.md
CHANGED
|
@@ -10,6 +10,24 @@ payments/reactivate
|
|
|
10
10
|
payments/upgrade
|
|
11
11
|
* takes a subscription id and a new plan id and upgrades the user's subscription to the new plan. this can only be done if the user has an active subscription.
|
|
12
12
|
|
|
13
|
+
|
|
14
|
+
-------
|
|
15
|
+
USER OBJECT UPDGRADE --> INSTANCE?
|
|
16
|
+
|
|
17
|
+
const User = require('backend-manager/src/manager/helpers/user');
|
|
18
|
+
+
|
|
19
|
+
User.resolveSubscription(self.request.user);
|
|
20
|
+
-->
|
|
21
|
+
one object that cna do resolveSubscription(), getAccount(), etc
|
|
22
|
+
|
|
23
|
+
since you changed some things, do all tests in all projects align still?
|
|
24
|
+
|
|
25
|
+
after that, udpate README, CLAUDE.md and ANY TOPLEVLE CLAUDE SKILLS so that we never make this mistake again
|
|
26
|
+
|
|
27
|
+
including
|
|
28
|
+
|
|
29
|
+
---------
|
|
30
|
+
|
|
13
31
|
TEST NEWSLETTER
|
|
14
32
|
POST /admin/cron { id: 'daily/marketing-newsletter-generate' }
|
|
15
33
|
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const powertools = require('node-powertools');
|
|
2
2
|
const { REMINDER_DELAYS, COLLECTION } = require('../../libraries/abandoned-cart-config.js');
|
|
3
|
+
const User = require('../../helpers/user.js');
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Abandoned cart reminder cron job
|
|
@@ -49,8 +50,7 @@ module.exports = async ({ Manager, assistant, context, libraries }) => {
|
|
|
49
50
|
const userDoc = userSnap.data();
|
|
50
51
|
|
|
51
52
|
// Belt-and-suspenders: skip if user already has active paid subscription
|
|
52
|
-
if (userDoc.
|
|
53
|
-
&& userDoc.subscription?.product?.id !== 'basic') {
|
|
53
|
+
if (User.resolveSubscription(userDoc).active) {
|
|
54
54
|
assistant.log(`User ${uid} now has active subscription, marking cart completed`);
|
|
55
55
|
await markCompleted(doc, admin, nowUNIX);
|
|
56
56
|
skipped++;
|
|
@@ -218,6 +218,16 @@ async function processPaymentEvent({ category, library, resource, resourceType,
|
|
|
218
218
|
? library.toUnifiedSubscription(resource, transformOptions)
|
|
219
219
|
: library.toUnifiedOneTime(resource, transformOptions);
|
|
220
220
|
|
|
221
|
+
// Override: immediately suspend on payment denial
|
|
222
|
+
// Processors keep the sub active while retrying, but we revoke access right away.
|
|
223
|
+
// If the retry succeeds (e.g. PAYMENT.SALE.COMPLETED), it will restore active status.
|
|
224
|
+
// PayPal: PAYMENT.SALE.DENIED, Stripe: invoice.payment_failed, Chargebee: payment_failed
|
|
225
|
+
const PAYMENT_DENIED_EVENTS = ['PAYMENT.SALE.DENIED', 'invoice.payment_failed', 'payment_failed'];
|
|
226
|
+
if (isSubscription && PAYMENT_DENIED_EVENTS.includes(eventType) && unified.status === 'active') {
|
|
227
|
+
assistant.log(`Overriding status to suspended: ${eventType} received but provider still says active`);
|
|
228
|
+
unified.status = 'suspended';
|
|
229
|
+
}
|
|
230
|
+
|
|
221
231
|
assistant.log(`Unified ${category}: product=${unified.product.id}, status=${unified.status}`, unified);
|
|
222
232
|
|
|
223
233
|
// Read checkout context from payments-intents (attribution, discount, supplemental)
|
|
@@ -7,6 +7,7 @@ const path = require('path');
|
|
|
7
7
|
const powertools = require('node-powertools');
|
|
8
8
|
const { merge } = require('lodash');
|
|
9
9
|
const JSON5 = require('json5');
|
|
10
|
+
const User = require('./user.js');
|
|
10
11
|
|
|
11
12
|
function Middleware(m, req, res) {
|
|
12
13
|
const self = this;
|
|
@@ -126,7 +127,8 @@ Middleware.prototype.run = function (libPath, options) {
|
|
|
126
127
|
|
|
127
128
|
// Log working user
|
|
128
129
|
const workingUser = assistant.getUser();
|
|
129
|
-
|
|
130
|
+
const resolvedSub = User.resolveSubscription(workingUser);
|
|
131
|
+
assistant.log(`Middleware.process(): User (${workingUser.auth.uid}, ${workingUser.auth.email}, ${workingUser.subscription.product.id}=${workingUser.subscription.status} (resolved: ${resolvedSub.plan})):`, safeStringify(workingUser));
|
|
130
132
|
|
|
131
133
|
// Setup analytics
|
|
132
134
|
if (options.setupAnalytics) {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
const moment = require('moment');
|
|
9
9
|
const _ = require('lodash');
|
|
10
10
|
const hcaptcha = require('hcaptcha');
|
|
11
|
+
const User = require('./user.js');
|
|
11
12
|
|
|
12
13
|
function Usage(m) {
|
|
13
14
|
const self = this;
|
|
@@ -319,8 +320,8 @@ Usage.prototype.getProduct = function (id) {
|
|
|
319
320
|
|
|
320
321
|
const products = Manager.config.payment?.products || [];
|
|
321
322
|
|
|
322
|
-
// Look up by provided ID, or fall back to user's
|
|
323
|
-
id = id || self.user.
|
|
323
|
+
// Look up by provided ID, or fall back to user's resolved plan
|
|
324
|
+
id = id || User.resolveSubscription(self.user).plan;
|
|
324
325
|
|
|
325
326
|
return products.find(p => p.id === id)
|
|
326
327
|
|| products.find(p => p.id === 'basic')
|