@windrun-huaiin/backend-core 28.0.0 → 28.0.1

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.
@@ -0,0 +1,13 @@
1
+ import type { CreditOverviewTranslations } from '@windrun-huaiin/third-ui/main/credit';
2
+ import { NextResponse } from 'next/server';
3
+ export type CreditOverviewTranslationsResolver = (locale: string) => CreditOverviewTranslations | Promise<CreditOverviewTranslations>;
4
+ export interface CreateGETOptions {
5
+ defaultLocale?: string;
6
+ localePrefixAsNeeded?: boolean;
7
+ resolveTranslations: CreditOverviewTranslationsResolver;
8
+ pricingPath?: string;
9
+ checkoutApiEndpoint?: string;
10
+ customerPortalApiEndpoint?: string;
11
+ }
12
+ export declare function createGET(options: CreateGETOptions): (request: Request) => Promise<NextResponse<import("@core/lib/credit-overview").CreditOverviewPayload | null>>;
13
+ //# sourceMappingURL=route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/app/api/user/credit-overview/route.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AACvF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,MAAM,MAAM,kCAAkC,GAAG,CAC/C,MAAM,EAAE,MAAM,KACX,0BAA0B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAAC;AAEtE,MAAM,WAAW,gBAAgB;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,mBAAmB,EAAE,kCAAkC,CAAC;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,IACvB,SAAS,OAAO,6FAiB3C"}
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ var tslib = require('tslib');
4
+ var server = require('next/server');
5
+ var creditOverview = require('../../../../lib/credit-overview.js');
6
+
7
+ /* eslint-disable @typescript-eslint/no-explicit-any */
8
+ // Fix BigInt serialization issue
9
+ BigInt.prototype.toJSON = function () {
10
+ return this.toString();
11
+ };
12
+ function createGET(options) {
13
+ return function GET(request) {
14
+ return tslib.__awaiter(this, void 0, void 0, function* () {
15
+ const { searchParams } = new URL(request.url);
16
+ const locale = searchParams.get('locale') || options.defaultLocale || 'en';
17
+ const translations = yield options.resolveTranslations(locale);
18
+ const payload = yield creditOverview.buildCreditOverviewPayload({
19
+ locale,
20
+ defaultLocale: options.defaultLocale,
21
+ localePrefixAsNeeded: options.localePrefixAsNeeded,
22
+ translations,
23
+ pricingPath: options.pricingPath,
24
+ checkoutApiEndpoint: options.checkoutApiEndpoint,
25
+ customerPortalApiEndpoint: options.customerPortalApiEndpoint,
26
+ });
27
+ return server.NextResponse.json(payload);
28
+ });
29
+ };
30
+ }
31
+
32
+ exports.createGET = createGET;
@@ -0,0 +1,30 @@
1
+ import { __awaiter } from 'tslib';
2
+ import { NextResponse } from 'next/server';
3
+ import { buildCreditOverviewPayload } from '../../../../lib/credit-overview.mjs';
4
+
5
+ /* eslint-disable @typescript-eslint/no-explicit-any */
6
+ // Fix BigInt serialization issue
7
+ BigInt.prototype.toJSON = function () {
8
+ return this.toString();
9
+ };
10
+ function createGET(options) {
11
+ return function GET(request) {
12
+ return __awaiter(this, void 0, void 0, function* () {
13
+ const { searchParams } = new URL(request.url);
14
+ const locale = searchParams.get('locale') || options.defaultLocale || 'en';
15
+ const translations = yield options.resolveTranslations(locale);
16
+ const payload = yield buildCreditOverviewPayload({
17
+ locale,
18
+ defaultLocale: options.defaultLocale,
19
+ localePrefixAsNeeded: options.localePrefixAsNeeded,
20
+ translations,
21
+ pricingPath: options.pricingPath,
22
+ checkoutApiEndpoint: options.checkoutApiEndpoint,
23
+ customerPortalApiEndpoint: options.customerPortalApiEndpoint,
24
+ });
25
+ return NextResponse.json(payload);
26
+ });
27
+ };
28
+ }
29
+
30
+ export { createGET };
@@ -0,0 +1,3 @@
1
+ import { NextResponse } from 'next/server';
2
+ export declare function GET(): Promise<NextResponse<import("@windrun-huaiin/third-ui/main/server").InitUserContext>>;
3
+ //# sourceMappingURL=route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/app/api/user/pricing-context/route.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,wBAAsB,GAAG,0FAGxB"}
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ var tslib = require('tslib');
4
+ var moneyPriceHelper = require('../../../../lib/money-price-helper.js');
5
+ var server = require('next/server');
6
+
7
+ /* eslint-disable @typescript-eslint/no-explicit-any */
8
+ // Fix BigInt serialization issue
9
+ BigInt.prototype.toJSON = function () {
10
+ return this.toString();
11
+ };
12
+ function GET() {
13
+ return tslib.__awaiter(this, void 0, void 0, function* () {
14
+ const initUserContext = yield moneyPriceHelper.getMoneyPriceInitUserContext();
15
+ return server.NextResponse.json(initUserContext);
16
+ });
17
+ }
18
+
19
+ exports.GET = GET;
@@ -0,0 +1,17 @@
1
+ import { __awaiter } from 'tslib';
2
+ import { getMoneyPriceInitUserContext } from '../../../../lib/money-price-helper.mjs';
3
+ import { NextResponse } from 'next/server';
4
+
5
+ /* eslint-disable @typescript-eslint/no-explicit-any */
6
+ // Fix BigInt serialization issue
7
+ BigInt.prototype.toJSON = function () {
8
+ return this.toString();
9
+ };
10
+ function GET() {
11
+ return __awaiter(this, void 0, void 0, function* () {
12
+ const initUserContext = yield getMoneyPriceInitUserContext();
13
+ return NextResponse.json(initUserContext);
14
+ });
15
+ }
16
+
17
+ export { GET };
@@ -1,2 +1,3 @@
1
1
  export * from '../lib/credit-init';
2
+ export * from '../lib/credit-overview';
2
3
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/credit/server.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/credit/server.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC"}
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var creditInit = require('../lib/credit-init.js');
4
+ var creditOverview = require('../lib/credit-overview.js');
4
5
 
5
6
 
6
7
 
@@ -9,3 +10,4 @@ exports.freeAmount = creditInit.freeAmount;
9
10
  exports.freeExpiredDays = creditInit.freeExpiredDays;
10
11
  exports.freeRegisterAmount = creditInit.freeRegisterAmount;
11
12
  exports.oneTimeExpiredDays = creditInit.oneTimeExpiredDays;
13
+ exports.buildCreditOverviewPayload = creditOverview.buildCreditOverviewPayload;
@@ -1 +1,2 @@
1
1
  export { creditsConfig, freeAmount, freeExpiredDays, freeRegisterAmount, oneTimeExpiredDays } from '../lib/credit-init.mjs';
2
+ export { buildCreditOverviewPayload } from '../lib/credit-overview.mjs';
@@ -0,0 +1,17 @@
1
+ import type { CreditOverviewData, CreditOverviewTranslations } from '@windrun-huaiin/third-ui/main/credit';
2
+ export interface BuildCreditOverviewPayloadOptions {
3
+ locale: string;
4
+ defaultLocale?: string;
5
+ localePrefixAsNeeded?: boolean;
6
+ translations: CreditOverviewTranslations;
7
+ pricingPath?: string;
8
+ checkoutApiEndpoint?: string;
9
+ customerPortalApiEndpoint?: string;
10
+ }
11
+ export interface CreditOverviewPayload {
12
+ data: CreditOverviewData;
13
+ totalLabel: string;
14
+ translations: CreditOverviewTranslations;
15
+ }
16
+ export declare function buildCreditOverviewPayload(options: BuildCreditOverviewPayloadOptions): Promise<CreditOverviewPayload | null>;
17
+ //# sourceMappingURL=credit-overview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credit-overview.d.ts","sourceRoot":"","sources":["../../src/lib/credit-overview.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAE3G,MAAM,WAAW,iCAAiC;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,YAAY,EAAE,0BAA0B,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,kBAAkB,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,0BAA0B,CAAC;CAC1C;AAED,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,iCAAiC,GACzC,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAuHvC"}
@@ -0,0 +1,117 @@
1
+ 'use strict';
2
+
3
+ var tslib = require('tslib');
4
+ var authUtils = require('../auth/auth-utils.js');
5
+ var moneyPriceConfig = require('./money-price-config.js');
6
+ require('../prisma/prisma.js');
7
+ require('../core-prisma/client.js');
8
+ var subscription_service = require('../services/database/subscription.service.js');
9
+ var credit_service = require('../services/database/credit.service.js');
10
+ var userContextService = require('../services/context/user-context-service.js');
11
+ var utils = require('@windrun-huaiin/lib/utils');
12
+ var server = require('@windrun-huaiin/third-ui/main/money-price/server');
13
+
14
+ /* eslint-disable @typescript-eslint/no-explicit-any */
15
+ // Fix BigInt serialization issue
16
+ BigInt.prototype.toJSON = function () {
17
+ return this.toString();
18
+ };
19
+ function buildCreditOverviewPayload(options) {
20
+ return tslib.__awaiter(this, void 0, void 0, function* () {
21
+ var _a, _b, _c, _d;
22
+ const authUser = yield authUtils.getOptionalServerAuthUser();
23
+ if (!authUser) {
24
+ return null;
25
+ }
26
+ const { locale, defaultLocale = 'en', localePrefixAsNeeded = true, translations, pricingPath = '/pricing', checkoutApiEndpoint = '/api/stripe/checkout', customerPortalApiEndpoint = '/api/stripe/customer-portal', } = options;
27
+ const { user } = authUser;
28
+ const enableSubscriptionUpgrade = process.env.ENABLE_STRIPE_SUBSCRIPTION_UPGRADE !== 'false';
29
+ const [credit, subscription, moneyPriceData] = yield Promise.all([
30
+ credit_service.creditService.getCredit(user.userId),
31
+ subscription_service.subscriptionService.getActiveSubscription(user.userId),
32
+ server.buildMoneyPriceData({
33
+ locale,
34
+ currency: moneyPriceConfig.moneyPriceConfig.display.currency,
35
+ enabledBillingTypes: ['monthly', 'yearly', 'onetime'],
36
+ }),
37
+ ]);
38
+ if (!credit) {
39
+ return null;
40
+ }
41
+ const initUserContext = userContextService.buildInitUserContextFromEntities({
42
+ user,
43
+ credit,
44
+ subscription,
45
+ });
46
+ const totalBalance = ((_a = credit.balanceFree) !== null && _a !== void 0 ? _a : 0) +
47
+ ((_b = credit.balancePaid) !== null && _b !== void 0 ? _b : 0) +
48
+ ((_c = credit.balanceOneTimePaid) !== null && _c !== void 0 ? _c : 0);
49
+ const buckets = [
50
+ ...(credit.balancePaid > 0
51
+ ? [{
52
+ kind: 'subscription',
53
+ balance: credit.balancePaid,
54
+ limit: credit.totalPaidLimit,
55
+ expiresAt: utils.viewLocalTime(credit.paidEnd),
56
+ }]
57
+ : []),
58
+ ...(credit.balanceOneTimePaid > 0
59
+ ? [{
60
+ kind: 'onetime',
61
+ balance: credit.balanceOneTimePaid,
62
+ limit: credit.totalOneTimePaidLimit,
63
+ expiresAt: utils.viewLocalTime(credit.oneTimePaidEnd),
64
+ }]
65
+ : []),
66
+ ...(credit.balanceFree > 0
67
+ ? [{
68
+ kind: 'free',
69
+ balance: credit.balanceFree,
70
+ limit: credit.totalFreeLimit,
71
+ expiresAt: utils.viewLocalTime(credit.freeEnd),
72
+ }]
73
+ : []),
74
+ ];
75
+ const pricingPageBaseUrl = utils.getAsNeededLocalizedUrl(locale, pricingPath, localePrefixAsNeeded, defaultLocale);
76
+ const data = {
77
+ totalBalance,
78
+ buckets,
79
+ pricingContext: {
80
+ moneyPriceData,
81
+ moneyPriceConfig: moneyPriceConfig.moneyPriceConfig,
82
+ checkoutApiEndpoint,
83
+ customerPortalApiEndpoint,
84
+ enableSubscriptionUpgrade,
85
+ initUserContext,
86
+ },
87
+ ctaBehaviors: {
88
+ subscribe: {
89
+ desktop: { kind: 'modal', mode: 'subscription' },
90
+ mobile: { kind: 'redirect', url: `${pricingPageBaseUrl}?initialBillingType=subscription` },
91
+ },
92
+ manage: {
93
+ desktop: { kind: 'auth' },
94
+ mobile: { kind: 'auth' },
95
+ },
96
+ onetime: {
97
+ desktop: { kind: 'modal', mode: 'onetime' },
98
+ mobile: { kind: 'redirect', url: `${pricingPageBaseUrl}?initialBillingType=onetime` },
99
+ },
100
+ },
101
+ };
102
+ if (subscription) {
103
+ data.subscription = {
104
+ planName: (_d = subscription.priceName) !== null && _d !== void 0 ? _d : '',
105
+ periodStart: utils.viewLocalTime(subscription.subPeriodStart),
106
+ periodEnd: utils.viewLocalTime(subscription.subPeriodEnd),
107
+ };
108
+ }
109
+ return {
110
+ data,
111
+ totalLabel: translations.totalLabel,
112
+ translations,
113
+ };
114
+ });
115
+ }
116
+
117
+ exports.buildCreditOverviewPayload = buildCreditOverviewPayload;
@@ -0,0 +1,115 @@
1
+ import { __awaiter } from 'tslib';
2
+ import { getOptionalServerAuthUser } from '../auth/auth-utils.mjs';
3
+ import { moneyPriceConfig } from './money-price-config.mjs';
4
+ import '../prisma/prisma.mjs';
5
+ import '../core-prisma/client.mjs';
6
+ import { subscriptionService } from '../services/database/subscription.service.mjs';
7
+ import { creditService } from '../services/database/credit.service.mjs';
8
+ import { buildInitUserContextFromEntities } from '../services/context/user-context-service.mjs';
9
+ import { viewLocalTime, getAsNeededLocalizedUrl } from '@windrun-huaiin/lib/utils';
10
+ import { buildMoneyPriceData } from '@windrun-huaiin/third-ui/main/money-price/server';
11
+
12
+ /* eslint-disable @typescript-eslint/no-explicit-any */
13
+ // Fix BigInt serialization issue
14
+ BigInt.prototype.toJSON = function () {
15
+ return this.toString();
16
+ };
17
+ function buildCreditOverviewPayload(options) {
18
+ return __awaiter(this, void 0, void 0, function* () {
19
+ var _a, _b, _c, _d;
20
+ const authUser = yield getOptionalServerAuthUser();
21
+ if (!authUser) {
22
+ return null;
23
+ }
24
+ const { locale, defaultLocale = 'en', localePrefixAsNeeded = true, translations, pricingPath = '/pricing', checkoutApiEndpoint = '/api/stripe/checkout', customerPortalApiEndpoint = '/api/stripe/customer-portal', } = options;
25
+ const { user } = authUser;
26
+ const enableSubscriptionUpgrade = process.env.ENABLE_STRIPE_SUBSCRIPTION_UPGRADE !== 'false';
27
+ const [credit, subscription, moneyPriceData] = yield Promise.all([
28
+ creditService.getCredit(user.userId),
29
+ subscriptionService.getActiveSubscription(user.userId),
30
+ buildMoneyPriceData({
31
+ locale,
32
+ currency: moneyPriceConfig.display.currency,
33
+ enabledBillingTypes: ['monthly', 'yearly', 'onetime'],
34
+ }),
35
+ ]);
36
+ if (!credit) {
37
+ return null;
38
+ }
39
+ const initUserContext = buildInitUserContextFromEntities({
40
+ user,
41
+ credit,
42
+ subscription,
43
+ });
44
+ const totalBalance = ((_a = credit.balanceFree) !== null && _a !== void 0 ? _a : 0) +
45
+ ((_b = credit.balancePaid) !== null && _b !== void 0 ? _b : 0) +
46
+ ((_c = credit.balanceOneTimePaid) !== null && _c !== void 0 ? _c : 0);
47
+ const buckets = [
48
+ ...(credit.balancePaid > 0
49
+ ? [{
50
+ kind: 'subscription',
51
+ balance: credit.balancePaid,
52
+ limit: credit.totalPaidLimit,
53
+ expiresAt: viewLocalTime(credit.paidEnd),
54
+ }]
55
+ : []),
56
+ ...(credit.balanceOneTimePaid > 0
57
+ ? [{
58
+ kind: 'onetime',
59
+ balance: credit.balanceOneTimePaid,
60
+ limit: credit.totalOneTimePaidLimit,
61
+ expiresAt: viewLocalTime(credit.oneTimePaidEnd),
62
+ }]
63
+ : []),
64
+ ...(credit.balanceFree > 0
65
+ ? [{
66
+ kind: 'free',
67
+ balance: credit.balanceFree,
68
+ limit: credit.totalFreeLimit,
69
+ expiresAt: viewLocalTime(credit.freeEnd),
70
+ }]
71
+ : []),
72
+ ];
73
+ const pricingPageBaseUrl = getAsNeededLocalizedUrl(locale, pricingPath, localePrefixAsNeeded, defaultLocale);
74
+ const data = {
75
+ totalBalance,
76
+ buckets,
77
+ pricingContext: {
78
+ moneyPriceData,
79
+ moneyPriceConfig,
80
+ checkoutApiEndpoint,
81
+ customerPortalApiEndpoint,
82
+ enableSubscriptionUpgrade,
83
+ initUserContext,
84
+ },
85
+ ctaBehaviors: {
86
+ subscribe: {
87
+ desktop: { kind: 'modal', mode: 'subscription' },
88
+ mobile: { kind: 'redirect', url: `${pricingPageBaseUrl}?initialBillingType=subscription` },
89
+ },
90
+ manage: {
91
+ desktop: { kind: 'auth' },
92
+ mobile: { kind: 'auth' },
93
+ },
94
+ onetime: {
95
+ desktop: { kind: 'modal', mode: 'onetime' },
96
+ mobile: { kind: 'redirect', url: `${pricingPageBaseUrl}?initialBillingType=onetime` },
97
+ },
98
+ },
99
+ };
100
+ if (subscription) {
101
+ data.subscription = {
102
+ planName: (_d = subscription.priceName) !== null && _d !== void 0 ? _d : '',
103
+ periodStart: viewLocalTime(subscription.subPeriodStart),
104
+ periodEnd: viewLocalTime(subscription.subPeriodEnd),
105
+ };
106
+ }
107
+ return {
108
+ data,
109
+ totalLabel: translations.totalLabel,
110
+ translations,
111
+ };
112
+ });
113
+ }
114
+
115
+ export { buildCreditOverviewPayload };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windrun-huaiin/backend-core",
3
- "version": "28.0.0",
3
+ "version": "28.0.1",
4
4
  "description": "Shared backend primitives: Prisma schema/client, database services, routing helpers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -107,6 +107,16 @@
107
107
  "import": "./dist/app/api/user/anonymous/init/route.mjs",
108
108
  "require": "./dist/app/api/user/anonymous/init/route.js"
109
109
  },
110
+ "./app/api/user/credit-overview/route": {
111
+ "types": "./dist/app/api/user/credit-overview/route.d.ts",
112
+ "import": "./dist/app/api/user/credit-overview/route.mjs",
113
+ "require": "./dist/app/api/user/credit-overview/route.js"
114
+ },
115
+ "./app/api/user/pricing-context/route": {
116
+ "types": "./dist/app/api/user/pricing-context/route.d.ts",
117
+ "import": "./dist/app/api/user/pricing-context/route.mjs",
118
+ "require": "./dist/app/api/user/pricing-context/route.js"
119
+ },
110
120
  "./app/api/stripe/checkout/route": {
111
121
  "types": "./dist/app/api/stripe/checkout/route.d.ts",
112
122
  "import": "./dist/app/api/stripe/checkout/route.mjs",
@@ -144,8 +154,8 @@
144
154
  "tslib": "^2.8.1",
145
155
  "zod": "^4.3.6",
146
156
  "@windrun-huaiin/contracts": "^28.0.0",
147
- "@windrun-huaiin/lib": "^28.0.0",
148
- "@windrun-huaiin/third-ui": "^28.0.0"
157
+ "@windrun-huaiin/third-ui": "^28.0.0",
158
+ "@windrun-huaiin/lib": "^28.0.0"
149
159
  },
150
160
  "devDependencies": {
151
161
  "@rollup/plugin-alias": "^5.1.1",
@@ -0,0 +1,43 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ // Fix BigInt serialization issue
4
+ (BigInt.prototype as any).toJSON = function () {
5
+ return this.toString();
6
+ };
7
+
8
+ import type { CreditOverviewTranslations } from '@windrun-huaiin/third-ui/main/credit';
9
+ import { NextResponse } from 'next/server';
10
+ import { buildCreditOverviewPayload } from '@core/lib/credit-overview';
11
+
12
+ export type CreditOverviewTranslationsResolver = (
13
+ locale: string,
14
+ ) => CreditOverviewTranslations | Promise<CreditOverviewTranslations>;
15
+
16
+ export interface CreateGETOptions {
17
+ defaultLocale?: string;
18
+ localePrefixAsNeeded?: boolean;
19
+ resolveTranslations: CreditOverviewTranslationsResolver;
20
+ pricingPath?: string;
21
+ checkoutApiEndpoint?: string;
22
+ customerPortalApiEndpoint?: string;
23
+ }
24
+
25
+ export function createGET(options: CreateGETOptions) {
26
+ return async function GET(request: Request) {
27
+ const { searchParams } = new URL(request.url);
28
+ const locale = searchParams.get('locale') || options.defaultLocale || 'en';
29
+ const translations = await options.resolveTranslations(locale);
30
+
31
+ const payload = await buildCreditOverviewPayload({
32
+ locale,
33
+ defaultLocale: options.defaultLocale,
34
+ localePrefixAsNeeded: options.localePrefixAsNeeded,
35
+ translations,
36
+ pricingPath: options.pricingPath,
37
+ checkoutApiEndpoint: options.checkoutApiEndpoint,
38
+ customerPortalApiEndpoint: options.customerPortalApiEndpoint,
39
+ });
40
+
41
+ return NextResponse.json(payload);
42
+ };
43
+ }
@@ -0,0 +1,14 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ // Fix BigInt serialization issue
4
+ (BigInt.prototype as any).toJSON = function () {
5
+ return this.toString();
6
+ };
7
+
8
+ import { getMoneyPriceInitUserContext } from '@core/lib/money-price-helper';
9
+ import { NextResponse } from 'next/server';
10
+
11
+ export async function GET() {
12
+ const initUserContext = await getMoneyPriceInitUserContext();
13
+ return NextResponse.json(initUserContext);
14
+ }
@@ -1 +1,2 @@
1
1
  export * from '../lib/credit-init';
2
+ export * from '../lib/credit-overview';
@@ -0,0 +1,153 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ // Fix BigInt serialization issue
4
+ (BigInt.prototype as any).toJSON = function () {
5
+ return this.toString();
6
+ };
7
+
8
+ import { getOptionalServerAuthUser } from '@core/auth/auth-utils';
9
+ import { moneyPriceConfig } from '@core/lib/money-price-config';
10
+ import { creditService, subscriptionService } from '@core/services/database';
11
+ import { buildInitUserContextFromEntities } from '@core/services/context';
12
+ import { getAsNeededLocalizedUrl, viewLocalTime } from '@windrun-huaiin/lib/utils';
13
+ import { buildMoneyPriceData } from '@windrun-huaiin/third-ui/main/money-price/server';
14
+ import type { CreditOverviewData, CreditOverviewTranslations } from '@windrun-huaiin/third-ui/main/credit';
15
+
16
+ export interface BuildCreditOverviewPayloadOptions {
17
+ locale: string;
18
+ defaultLocale?: string;
19
+ localePrefixAsNeeded?: boolean;
20
+ translations: CreditOverviewTranslations;
21
+ pricingPath?: string;
22
+ checkoutApiEndpoint?: string;
23
+ customerPortalApiEndpoint?: string;
24
+ }
25
+
26
+ export interface CreditOverviewPayload {
27
+ data: CreditOverviewData;
28
+ totalLabel: string;
29
+ translations: CreditOverviewTranslations;
30
+ }
31
+
32
+ export async function buildCreditOverviewPayload(
33
+ options: BuildCreditOverviewPayloadOptions,
34
+ ): Promise<CreditOverviewPayload | null> {
35
+ const authUser = await getOptionalServerAuthUser();
36
+
37
+ if (!authUser) {
38
+ return null;
39
+ }
40
+
41
+ const {
42
+ locale,
43
+ defaultLocale = 'en',
44
+ localePrefixAsNeeded = true,
45
+ translations,
46
+ pricingPath = '/pricing',
47
+ checkoutApiEndpoint = '/api/stripe/checkout',
48
+ customerPortalApiEndpoint = '/api/stripe/customer-portal',
49
+ } = options;
50
+
51
+ const { user } = authUser;
52
+ const enableSubscriptionUpgrade = process.env.ENABLE_STRIPE_SUBSCRIPTION_UPGRADE !== 'false';
53
+
54
+ const [credit, subscription, moneyPriceData] = await Promise.all([
55
+ creditService.getCredit(user.userId),
56
+ subscriptionService.getActiveSubscription(user.userId),
57
+ buildMoneyPriceData({
58
+ locale,
59
+ currency: moneyPriceConfig.display.currency,
60
+ enabledBillingTypes: ['monthly', 'yearly', 'onetime'],
61
+ }),
62
+ ]);
63
+
64
+ if (!credit) {
65
+ return null;
66
+ }
67
+
68
+ const initUserContext = buildInitUserContextFromEntities({
69
+ user,
70
+ credit,
71
+ subscription,
72
+ });
73
+
74
+ const totalBalance =
75
+ (credit.balanceFree ?? 0) +
76
+ (credit.balancePaid ?? 0) +
77
+ (credit.balanceOneTimePaid ?? 0);
78
+
79
+ const buckets = [
80
+ ...(credit.balancePaid > 0
81
+ ? [{
82
+ kind: 'subscription' as const,
83
+ balance: credit.balancePaid,
84
+ limit: credit.totalPaidLimit,
85
+ expiresAt: viewLocalTime(credit.paidEnd),
86
+ }]
87
+ : []),
88
+ ...(credit.balanceOneTimePaid > 0
89
+ ? [{
90
+ kind: 'onetime' as const,
91
+ balance: credit.balanceOneTimePaid,
92
+ limit: credit.totalOneTimePaidLimit,
93
+ expiresAt: viewLocalTime(credit.oneTimePaidEnd),
94
+ }]
95
+ : []),
96
+ ...(credit.balanceFree > 0
97
+ ? [{
98
+ kind: 'free' as const,
99
+ balance: credit.balanceFree,
100
+ limit: credit.totalFreeLimit,
101
+ expiresAt: viewLocalTime(credit.freeEnd),
102
+ }]
103
+ : []),
104
+ ];
105
+
106
+ const pricingPageBaseUrl = getAsNeededLocalizedUrl(
107
+ locale,
108
+ pricingPath,
109
+ localePrefixAsNeeded,
110
+ defaultLocale,
111
+ );
112
+
113
+ const data: CreditOverviewData = {
114
+ totalBalance,
115
+ buckets,
116
+ pricingContext: {
117
+ moneyPriceData,
118
+ moneyPriceConfig,
119
+ checkoutApiEndpoint,
120
+ customerPortalApiEndpoint,
121
+ enableSubscriptionUpgrade,
122
+ initUserContext,
123
+ },
124
+ ctaBehaviors: {
125
+ subscribe: {
126
+ desktop: { kind: 'modal', mode: 'subscription' },
127
+ mobile: { kind: 'redirect', url: `${pricingPageBaseUrl}?initialBillingType=subscription` },
128
+ },
129
+ manage: {
130
+ desktop: { kind: 'auth' },
131
+ mobile: { kind: 'auth' },
132
+ },
133
+ onetime: {
134
+ desktop: { kind: 'modal', mode: 'onetime' },
135
+ mobile: { kind: 'redirect', url: `${pricingPageBaseUrl}?initialBillingType=onetime` },
136
+ },
137
+ },
138
+ };
139
+
140
+ if (subscription) {
141
+ data.subscription = {
142
+ planName: subscription.priceName ?? '',
143
+ periodStart: viewLocalTime(subscription.subPeriodStart),
144
+ periodEnd: viewLocalTime(subscription.subPeriodEnd),
145
+ };
146
+ }
147
+
148
+ return {
149
+ data,
150
+ totalLabel: translations.totalLabel,
151
+ translations,
152
+ };
153
+ }