@saasicat/nest 1.0.0-rc.2 → 1.0.0-rc.21
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/README.md +44 -8
- package/dist/.build-stamp +1 -1
- package/dist/_entries.cjs +10214 -4796
- package/dist/admin/index.d.cts +5 -4
- package/dist/admin/index.d.ts +5 -4
- package/dist/admin/index.js +13 -9
- package/dist/{admin-resources.module-CpvzcfCc.d.cts → admin-resources.module-B4XNvdH-.d.cts} +1 -2
- package/dist/{admin-resources.module-DF5_Q7RD.d.ts → admin-resources.module-CWqdI9BV.d.ts} +1 -2
- package/dist/{admin-stats.service-TYiSUwIR.d.ts → admin-stats.service-CsLNt19Z.d.ts} +11 -7
- package/dist/{admin-stats.service-DfKupywR.d.cts → admin-stats.service-DrZrU8IJ.d.cts} +11 -7
- package/dist/{aggregation-B3CD0v_d.d.cts → aggregation-ChpZDh0g.d.ts} +39 -6
- package/dist/{aggregation-BFzW07DE.d.ts → aggregation-OpnhwzF5.d.cts} +39 -6
- package/dist/billing/index.d.cts +448 -108
- package/dist/billing/index.d.ts +448 -108
- package/dist/billing/index.js +108 -41
- package/dist/catalog/index.d.cts +74 -45
- package/dist/catalog/index.d.ts +74 -45
- package/dist/catalog/index.js +21 -13
- package/dist/catalog.module-C9O7BxRq.d.ts +180 -0
- package/dist/catalog.module-T-I5Ricz.d.cts +180 -0
- package/dist/checkout-offer/index.d.cts +17 -22
- package/dist/checkout-offer/index.d.ts +17 -22
- package/dist/checkout-offer/index.js +21 -7
- package/dist/checkout-offer.module-DFhGy6JO.d.cts +58 -0
- package/dist/checkout-offer.module-x7SP_dX4.d.ts +58 -0
- package/dist/checkout-offer.service-BeHgYikI.d.ts +242 -0
- package/dist/checkout-offer.service-CCVxG9b2.d.cts +242 -0
- package/dist/chunk-34XVPR6P.js +222 -0
- package/dist/{chunk-AOQJEYOL.js → chunk-3QVGA6JX.js} +3 -1
- package/dist/chunk-3RLWML2S.js +896 -0
- package/dist/{chunk-TXC3LHHB.js → chunk-4SDSYV4A.js} +21 -13
- package/dist/chunk-55QEDAMX.js +946 -0
- package/dist/chunk-5CKC7O52.js +140 -0
- package/dist/chunk-6PJE7EWO.js +185 -0
- package/dist/chunk-7NJKVC5X.js +353 -0
- package/dist/{chunk-RUNZ3X4C.js → chunk-AOS2JPDD.js} +1263 -185
- package/dist/{chunk-WOVQPXV4.js → chunk-CQ2ZZMTD.js} +702 -725
- package/dist/{chunk-NA6O63B7.js → chunk-F5X66HF5.js} +5 -5
- package/dist/chunk-FEQZCWUL.js +174 -0
- package/dist/{chunk-VIJ5NJWC.js → chunk-G6EZWECL.js} +107 -31
- package/dist/chunk-G7RQO2XO.js +0 -0
- package/dist/{chunk-N3L3AICU.js → chunk-GPQWGA6B.js} +5 -6
- package/dist/chunk-GZTL64QK.js +7 -0
- package/dist/{chunk-SABTXESR.js → chunk-J7NJRK3K.js} +17 -1
- package/dist/{chunk-SEPN52AD.js → chunk-JBCC6C3M.js} +566 -307
- package/dist/{chunk-I7GTA3RX.js → chunk-JQRA724W.js} +1 -1
- package/dist/{chunk-WXYJHZCN.js → chunk-JVAEKTJ4.js} +2 -14
- package/dist/chunk-KPEMTBOP.js +26 -0
- package/dist/chunk-LF6J4YYN.js +301 -0
- package/dist/{chunk-LTT736P3.js → chunk-LLYVYRGJ.js} +228 -387
- package/dist/{chunk-6Z7JR4EW.js → chunk-M47BNEY2.js} +1897 -633
- package/dist/{chunk-KFT5AIIH.js → chunk-NDLC5GYK.js} +206 -24
- package/dist/{chunk-O2J2HDXA.js → chunk-OOTEXP47.js} +3 -8
- package/dist/chunk-QPVDCKYS.js +702 -0
- package/dist/{chunk-NHVDCYK5.js → chunk-R5YCJBHY.js} +71 -106
- package/dist/{chunk-XBYAFEOR.js → chunk-S33SO5XX.js} +1 -1
- package/dist/chunk-SZ7RFPXA.js +10 -0
- package/dist/chunk-TBBZWZQT.js +98 -0
- package/dist/chunk-TOEFN7DN.js +295 -0
- package/dist/{chunk-7ZEGFL42.js → chunk-WHJSYKNC.js} +6 -6
- package/dist/chunk-WOEJ6K7M.js +55 -0
- package/dist/{chunk-AU3OOREM.js → chunk-XSSWYPP5.js} +16 -3
- package/dist/contract-line-item-money-B0Z3Ogur.d.cts +43 -0
- package/dist/contract-line-item-money-B0Z3Ogur.d.ts +43 -0
- package/dist/discovery/index.d.cts +2 -2
- package/dist/discovery/index.d.ts +2 -2
- package/dist/discovery/index.js +6 -6
- package/dist/{discovery.scanner-9cMqF95o.d.cts → discovery.scanner-DXKc6JkV.d.cts} +1 -1
- package/dist/{discovery.scanner-9cMqF95o.d.ts → discovery.scanner-DXKc6JkV.d.ts} +1 -1
- package/dist/{enforce-quota.interceptor-Df0e3OWe.d.cts → enforce-quota.interceptor-BFyVdWe8.d.cts} +6 -6
- package/dist/{enforce-quota.interceptor-DW3etVyI.d.ts → enforce-quota.interceptor-GaBXboGx.d.ts} +6 -6
- package/dist/entitlement/index.d.cts +27 -4
- package/dist/entitlement/index.d.ts +27 -4
- package/dist/entitlement/index.js +13 -8
- package/dist/index.d.cts +209 -28
- package/dist/index.d.ts +209 -28
- package/dist/index.js +285 -163
- package/dist/issuer-identity.check-CBO0vcsq.d.cts +197 -0
- package/dist/issuer-identity.check-Cz9CEMkZ.d.ts +197 -0
- package/dist/{module-options-COSEb0VW.d.ts → module-options-DmQ3G4sZ.d.ts} +135 -41
- package/dist/{module-options-BG3MzQva.d.cts → module-options-qAOwinHR.d.cts} +135 -41
- package/dist/payment-callback.service-CR84Xw1Z.d.cts +125 -0
- package/dist/payment-callback.service-CR84Xw1Z.d.ts +125 -0
- package/dist/payments/index.cjs +4 -0
- package/dist/payments/index.d.cts +187 -0
- package/dist/payments/index.d.ts +187 -0
- package/dist/payments/index.js +141 -0
- package/dist/payments.module-BdUq9ot_.d.cts +57 -0
- package/dist/payments.module-CQGv_VG7.d.ts +57 -0
- package/dist/plan-catalog-source-DWe-BGY1.d.cts +21 -0
- package/dist/plan-catalog-source-DWe-BGY1.d.ts +21 -0
- package/dist/{plan-resolution-Cgo_TR1H.d.cts → plan-resolution-CwUy_OqC.d.cts} +14 -0
- package/dist/{plan-resolution-Cgo_TR1H.d.ts → plan-resolution-CwUy_OqC.d.ts} +14 -0
- package/dist/{plan-versions.service-DChhbK6h.d.ts → plan-versions.service-C0N-Em2W.d.ts} +38 -64
- package/dist/{plan-versions.service-TPEtbN0r.d.cts → plan-versions.service-DARLh-qI.d.cts} +38 -64
- package/dist/platform/index.d.cts +52 -23
- package/dist/platform/index.d.ts +52 -23
- package/dist/platform/index.js +57 -36
- package/dist/promo/index.d.cts +23 -8
- package/dist/promo/index.d.ts +23 -8
- package/dist/promo/index.js +15 -7
- package/dist/{promo.module-DtvIycpt.d.cts → promo.module-CKkECmU8.d.cts} +9 -3
- package/dist/{promo.module-Z8h1PNX-.d.ts → promo.module-DuIJdK3U.d.ts} +9 -3
- package/dist/promo.service-BYBu6dy3.d.cts +215 -0
- package/dist/promo.service-CFjfF0Xm.d.ts +215 -0
- package/dist/registration/index.d.cts +104 -47
- package/dist/registration/index.d.ts +104 -47
- package/dist/registration/index.js +15 -9
- package/dist/subscriber/index.cjs +4 -0
- package/dist/subscriber/index.d.cts +28 -0
- package/dist/subscriber/index.d.ts +28 -0
- package/dist/subscriber/index.js +16 -0
- package/dist/subscriber.service-5W6Gi-1B.d.cts +68 -0
- package/dist/subscriber.service-5W6Gi-1B.d.ts +68 -0
- package/dist/subscription-contract/index.d.cts +4 -2
- package/dist/subscription-contract/index.d.ts +4 -2
- package/dist/subscription-contract/index.js +13 -5
- package/dist/{subscription-contract.module-fDFdQ0jm.d.ts → subscription-contract.module-BfIRpc1o.d.ts} +8 -2
- package/dist/{subscription-contract.module-CEK8HS5-.d.cts → subscription-contract.module-DTMuUJYd.d.cts} +8 -2
- package/dist/{subscription-contract.service-hD87MKyg.d.cts → subscription-contract.service-CPZrTM9C.d.ts} +32 -3
- package/dist/{subscription-contract.service-hD87MKyg.d.ts → subscription-contract.service-CfNApLuP.d.cts} +32 -3
- package/dist/tenant-billing.controller-BLqbsONe.d.ts +812 -0
- package/dist/tenant-billing.controller-DKbAqTbM.d.cts +812 -0
- package/dist/tenant-billing.module-ClbjzocC.d.cts +364 -0
- package/dist/tenant-billing.module-DohgJJ2m.d.ts +364 -0
- package/dist/tenant-billing.tokens-G-1xOlMr.d.cts +129 -0
- package/dist/tenant-billing.tokens-G-1xOlMr.d.ts +129 -0
- package/dist/testing/index.d.cts +44 -17
- package/dist/testing/index.d.ts +44 -17
- package/dist/testing/index.js +189 -40
- package/package.json +24 -4
- package/dist/catalog.module-BDDO6iwq.d.cts +0 -103
- package/dist/catalog.module-BTnMsE6u.d.ts +0 -103
- package/dist/checkout-offer.module-CMtiQJTT.d.ts +0 -39
- package/dist/checkout-offer.module-CVpWrzbe.d.cts +0 -39
- package/dist/checkout-offer.service-BLOv2HOo.d.cts +0 -49
- package/dist/checkout-offer.service-BLOv2HOo.d.ts +0 -49
- package/dist/chunk-57V6ZTI6.js +0 -22
- package/dist/chunk-BSK6YBLI.js +0 -87
- package/dist/chunk-DQKCK7DX.js +0 -217
- package/dist/chunk-VXEYLNIB.js +0 -632
- package/dist/chunk-WUDYIPYH.js +0 -715
- package/dist/define-saasicat-DROBe-b1.d.ts +0 -69
- package/dist/define-saasicat-vGBCkFys.d.cts +0 -69
- package/dist/promo.service-BJbKAmw3.d.cts +0 -112
- package/dist/promo.service-BJbKAmw3.d.ts +0 -112
- package/dist/tenant-billing.controller-BQ67mlmI.d.ts +0 -438
- package/dist/tenant-billing.controller-DS8kB8a7.d.cts +0 -438
- package/dist/tenant-billing.module-BznC3XtW.d.cts +0 -299
- package/dist/tenant-billing.module-wUbsQSFG.d.ts +0 -299
- /package/dist/{chunk-2FR6ZL7R.js → chunk-BHZOH2DY.js} +0 -0
- /package/dist/{chunk-2SLTRKXC.js → chunk-DFOW3JVO.js} +0 -0
- /package/dist/{chunk-DBZGV3HC.js → chunk-DU56UKEH.js} +0 -0
- /package/dist/{chunk-OYNHOY45.js → chunk-GAUEVIAE.js} +0 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChangeBillingDetailsDto,
|
|
3
|
+
PaymentWebhookController,
|
|
4
|
+
PaymentsModule,
|
|
5
|
+
StartPaymentMethodSetupDto,
|
|
6
|
+
SubscriberPaymentMethodService,
|
|
7
|
+
TenantBillingDetailsController,
|
|
8
|
+
TenantPaymentMethodController
|
|
9
|
+
} from "../chunk-QPVDCKYS.js";
|
|
10
|
+
import {
|
|
11
|
+
PAYMENT_EVENT_LOG_TOKEN,
|
|
12
|
+
PAYMENT_GATEWAYS_TOKEN,
|
|
13
|
+
PAYMENT_TRANSACTION_RUNNER_TOKEN,
|
|
14
|
+
PaymentCallbackService,
|
|
15
|
+
PaymentGatewayRegistry,
|
|
16
|
+
SUBSCRIBER_PAYMENT_METHOD_REPOSITORY_TOKEN
|
|
17
|
+
} from "../chunk-7NJKVC5X.js";
|
|
18
|
+
import "../chunk-5CKC7O52.js";
|
|
19
|
+
import "../chunk-GPQWGA6B.js";
|
|
20
|
+
import "../chunk-SZ7RFPXA.js";
|
|
21
|
+
import "../chunk-WOEJ6K7M.js";
|
|
22
|
+
import "../chunk-LF6J4YYN.js";
|
|
23
|
+
import "../chunk-YSC46BGM.js";
|
|
24
|
+
import "../chunk-FEQZCWUL.js";
|
|
25
|
+
import "../chunk-J7NJRK3K.js";
|
|
26
|
+
import {
|
|
27
|
+
__name
|
|
28
|
+
} from "../chunk-SHUYVCID.js";
|
|
29
|
+
|
|
30
|
+
// src/payments/dev-payment-gateway.ts
|
|
31
|
+
import { createHmac, randomBytes, randomUUID, timingSafeEqual } from "crypto";
|
|
32
|
+
import { PaymentCallbackRejectedError } from "@saasicat/core";
|
|
33
|
+
var SIGNATURE_HEADER = "x-saasicat-dev-signature";
|
|
34
|
+
var DEV_PAYMENT_PROVIDER = "dev";
|
|
35
|
+
var DevPaymentGateway = class {
|
|
36
|
+
static {
|
|
37
|
+
__name(this, "DevPaymentGateway");
|
|
38
|
+
}
|
|
39
|
+
provider = DEV_PAYMENT_PROVIDER;
|
|
40
|
+
key = randomBytes(32);
|
|
41
|
+
constructor() {
|
|
42
|
+
if (process.env.NODE_ENV === "production") {
|
|
43
|
+
throw new Error("DevPaymentGateway confirms every payment method without taking one, and refuses to run with NODE_ENV=production. Bind a gateway adapter such as @saasicat/payment-stripe.");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async startPaymentMethodSetup(input) {
|
|
47
|
+
const sessionRef = `dev_cs_${randomUUID()}`;
|
|
48
|
+
const customerRef = input.holder.customerRef ?? `dev_cus_${randomUUID()}`;
|
|
49
|
+
const confirmation = {
|
|
50
|
+
eventId: `dev_evt_${randomUUID()}`,
|
|
51
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
52
|
+
sessionRef,
|
|
53
|
+
subject: input.subject,
|
|
54
|
+
paymentMethod: {
|
|
55
|
+
...maskedDetailsOf(input.methods[0] ?? "card"),
|
|
56
|
+
customerRef,
|
|
57
|
+
paymentMethodRef: `dev_pm_${randomUUID()}`
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const body = JSON.stringify(confirmation);
|
|
61
|
+
return {
|
|
62
|
+
sessionRef,
|
|
63
|
+
redirectUrl: input.successUrl,
|
|
64
|
+
customerRef,
|
|
65
|
+
// Confirmed on the spot: there is no form left to complete later.
|
|
66
|
+
confirmableUntil: null,
|
|
67
|
+
immediateCallback: {
|
|
68
|
+
body,
|
|
69
|
+
headers: {
|
|
70
|
+
[SIGNATURE_HEADER]: this.sign(body)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async readCallback(callback) {
|
|
76
|
+
const body = typeof callback.body === "string" ? callback.body : Buffer.from(callback.body).toString("utf8");
|
|
77
|
+
const signature = callback.headers[SIGNATURE_HEADER];
|
|
78
|
+
if (typeof signature !== "string" || !this.verify(body, signature)) {
|
|
79
|
+
throw new PaymentCallbackRejectedError("the development signature does not verify");
|
|
80
|
+
}
|
|
81
|
+
const confirmation = JSON.parse(body);
|
|
82
|
+
return {
|
|
83
|
+
kind: "payment-method-confirmed",
|
|
84
|
+
eventId: confirmation.eventId,
|
|
85
|
+
occurredAt: new Date(confirmation.occurredAt),
|
|
86
|
+
sessionRef: confirmation.sessionRef,
|
|
87
|
+
subject: confirmation.subject,
|
|
88
|
+
paymentMethod: confirmation.paymentMethod
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
sign(body) {
|
|
92
|
+
return createHmac("sha256", this.key).update(body).digest("hex");
|
|
93
|
+
}
|
|
94
|
+
verify(body, signature) {
|
|
95
|
+
const expected = Buffer.from(this.sign(body), "hex");
|
|
96
|
+
const given = Buffer.from(signature, "hex");
|
|
97
|
+
return given.length === expected.length && timingSafeEqual(given, expected);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
function maskedDetailsOf(type) {
|
|
101
|
+
if (type === "sepa_debit") {
|
|
102
|
+
return {
|
|
103
|
+
type,
|
|
104
|
+
brand: null,
|
|
105
|
+
last4: "3000",
|
|
106
|
+
expiryMonth: null,
|
|
107
|
+
expiryYear: null,
|
|
108
|
+
country: "DE",
|
|
109
|
+
bankCode: "37040044",
|
|
110
|
+
mandateReference: `DEV-${randomUUID().slice(0, 8).toUpperCase()}`
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
type,
|
|
115
|
+
brand: "visa",
|
|
116
|
+
last4: "4242",
|
|
117
|
+
expiryMonth: 12,
|
|
118
|
+
expiryYear: (/* @__PURE__ */ new Date()).getUTCFullYear() + 3,
|
|
119
|
+
country: null,
|
|
120
|
+
bankCode: null,
|
|
121
|
+
mandateReference: null
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
__name(maskedDetailsOf, "maskedDetailsOf");
|
|
125
|
+
export {
|
|
126
|
+
ChangeBillingDetailsDto,
|
|
127
|
+
DEV_PAYMENT_PROVIDER,
|
|
128
|
+
DevPaymentGateway,
|
|
129
|
+
PAYMENT_EVENT_LOG_TOKEN,
|
|
130
|
+
PAYMENT_GATEWAYS_TOKEN,
|
|
131
|
+
PAYMENT_TRANSACTION_RUNNER_TOKEN,
|
|
132
|
+
PaymentCallbackService,
|
|
133
|
+
PaymentGatewayRegistry,
|
|
134
|
+
PaymentWebhookController,
|
|
135
|
+
PaymentsModule,
|
|
136
|
+
SUBSCRIBER_PAYMENT_METHOD_REPOSITORY_TOKEN,
|
|
137
|
+
StartPaymentMethodSetupDto,
|
|
138
|
+
SubscriberPaymentMethodService,
|
|
139
|
+
TenantBillingDetailsController,
|
|
140
|
+
TenantPaymentMethodController
|
|
141
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { CanActivate, Type, DynamicModule, ForwardReference, Provider } from '@nestjs/common';
|
|
2
|
+
import { PaymentGateway, PaymentEventLog, SubscriberPaymentMethodRepository, SubscriberRepository, TransactionRunner } from '@saasicat/core';
|
|
3
|
+
import { T as TenantIdResolver, U as UserEmailResolver } from './tenant-billing.tokens-G-1xOlMr.cjs';
|
|
4
|
+
import { P as ProviderSpec } from './di-CcNeq9v-.cjs';
|
|
5
|
+
|
|
6
|
+
/** The tenant's routes for its payment method. */
|
|
7
|
+
interface PaymentsTenantRoutesOptions {
|
|
8
|
+
/** The application's authentication guards, as `TenantBillingModule.forRoot.authGuards`. */
|
|
9
|
+
authGuards: ProviderSpec<ReadonlyArray<CanActivate>>;
|
|
10
|
+
/**
|
|
11
|
+
* Who holds the billing permission. Without guards, the tenant's
|
|
12
|
+
* administrator (`TENANT_ADMIN`, `SUPER_ADMIN`) does.
|
|
13
|
+
*/
|
|
14
|
+
billingPermissionGuards?: ProviderSpec<ReadonlyArray<CanActivate>>;
|
|
15
|
+
/** Default: `req.user.tenantId`. */
|
|
16
|
+
tenantIdResolver?: TenantIdResolver;
|
|
17
|
+
/** Default: `req.user.email` — handed to the gateway when the subscriber has no invoice email. */
|
|
18
|
+
userEmailResolver?: UserEmailResolver;
|
|
19
|
+
}
|
|
20
|
+
interface PaymentsModuleOptions {
|
|
21
|
+
/**
|
|
22
|
+
* One adapter per account in `config/saas.yaml#payments.accounts`, by that
|
|
23
|
+
* account's name — each built with its keys from the environment.
|
|
24
|
+
*/
|
|
25
|
+
gateways: ProviderSpec<Readonly<Record<string, PaymentGateway>>>;
|
|
26
|
+
paymentEventLog: ProviderSpec<PaymentEventLog>;
|
|
27
|
+
subscriberPaymentMethodRepository: ProviderSpec<SubscriberPaymentMethodRepository>;
|
|
28
|
+
subscriberRepository: ProviderSpec<SubscriberRepository>;
|
|
29
|
+
/** The runner of the same persistence adapter the two stores above come from. */
|
|
30
|
+
transactionRunner: ProviderSpec<TransactionRunner>;
|
|
31
|
+
/**
|
|
32
|
+
* Mounts the tenant's billing area: `GET /billing/payment-method`,
|
|
33
|
+
* `POST /billing/payment-method/setup`, and `GET` and `PATCH /billing/details`.
|
|
34
|
+
*/
|
|
35
|
+
tenantRoutes?: PaymentsTenantRoutesOptions;
|
|
36
|
+
imports?: Array<Type<unknown> | DynamicModule | Promise<DynamicModule> | ForwardReference>;
|
|
37
|
+
extraProviders?: Provider[];
|
|
38
|
+
/**
|
|
39
|
+
* Default `true`: self-registration, wired in a module of its own, starts
|
|
40
|
+
* its setups through the registry and hands its confirmations to the
|
|
41
|
+
* callback service.
|
|
42
|
+
*/
|
|
43
|
+
global?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Payment methods through a payment gateway: the webhook route every account's
|
|
47
|
+
* callbacks arrive at, the registry of configured accounts, and — with
|
|
48
|
+
* `tenantRoutes` — the tenant's view of its payment method.
|
|
49
|
+
*
|
|
50
|
+
* Reads `config/saas.yaml#payments` from `PLAN_CATALOG_SETTINGS_TOKEN`, which
|
|
51
|
+
* `SaaSiCatModule.forRoot` provides globally.
|
|
52
|
+
*/
|
|
53
|
+
declare class PaymentsModule {
|
|
54
|
+
static forRoot(options: PaymentsModuleOptions): DynamicModule;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { type PaymentsModuleOptions as P, type PaymentsTenantRoutesOptions as a, PaymentsModule as b };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { CanActivate, Type, DynamicModule, ForwardReference, Provider } from '@nestjs/common';
|
|
2
|
+
import { PaymentGateway, PaymentEventLog, SubscriberPaymentMethodRepository, SubscriberRepository, TransactionRunner } from '@saasicat/core';
|
|
3
|
+
import { T as TenantIdResolver, U as UserEmailResolver } from './tenant-billing.tokens-G-1xOlMr.js';
|
|
4
|
+
import { P as ProviderSpec } from './di-CcNeq9v-.js';
|
|
5
|
+
|
|
6
|
+
/** The tenant's routes for its payment method. */
|
|
7
|
+
interface PaymentsTenantRoutesOptions {
|
|
8
|
+
/** The application's authentication guards, as `TenantBillingModule.forRoot.authGuards`. */
|
|
9
|
+
authGuards: ProviderSpec<ReadonlyArray<CanActivate>>;
|
|
10
|
+
/**
|
|
11
|
+
* Who holds the billing permission. Without guards, the tenant's
|
|
12
|
+
* administrator (`TENANT_ADMIN`, `SUPER_ADMIN`) does.
|
|
13
|
+
*/
|
|
14
|
+
billingPermissionGuards?: ProviderSpec<ReadonlyArray<CanActivate>>;
|
|
15
|
+
/** Default: `req.user.tenantId`. */
|
|
16
|
+
tenantIdResolver?: TenantIdResolver;
|
|
17
|
+
/** Default: `req.user.email` — handed to the gateway when the subscriber has no invoice email. */
|
|
18
|
+
userEmailResolver?: UserEmailResolver;
|
|
19
|
+
}
|
|
20
|
+
interface PaymentsModuleOptions {
|
|
21
|
+
/**
|
|
22
|
+
* One adapter per account in `config/saas.yaml#payments.accounts`, by that
|
|
23
|
+
* account's name — each built with its keys from the environment.
|
|
24
|
+
*/
|
|
25
|
+
gateways: ProviderSpec<Readonly<Record<string, PaymentGateway>>>;
|
|
26
|
+
paymentEventLog: ProviderSpec<PaymentEventLog>;
|
|
27
|
+
subscriberPaymentMethodRepository: ProviderSpec<SubscriberPaymentMethodRepository>;
|
|
28
|
+
subscriberRepository: ProviderSpec<SubscriberRepository>;
|
|
29
|
+
/** The runner of the same persistence adapter the two stores above come from. */
|
|
30
|
+
transactionRunner: ProviderSpec<TransactionRunner>;
|
|
31
|
+
/**
|
|
32
|
+
* Mounts the tenant's billing area: `GET /billing/payment-method`,
|
|
33
|
+
* `POST /billing/payment-method/setup`, and `GET` and `PATCH /billing/details`.
|
|
34
|
+
*/
|
|
35
|
+
tenantRoutes?: PaymentsTenantRoutesOptions;
|
|
36
|
+
imports?: Array<Type<unknown> | DynamicModule | Promise<DynamicModule> | ForwardReference>;
|
|
37
|
+
extraProviders?: Provider[];
|
|
38
|
+
/**
|
|
39
|
+
* Default `true`: self-registration, wired in a module of its own, starts
|
|
40
|
+
* its setups through the registry and hands its confirmations to the
|
|
41
|
+
* callback service.
|
|
42
|
+
*/
|
|
43
|
+
global?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Payment methods through a payment gateway: the webhook route every account's
|
|
47
|
+
* callbacks arrive at, the registry of configured accounts, and — with
|
|
48
|
+
* `tenantRoutes` — the tenant's view of its payment method.
|
|
49
|
+
*
|
|
50
|
+
* Reads `config/saas.yaml#payments` from `PLAN_CATALOG_SETTINGS_TOKEN`, which
|
|
51
|
+
* `SaaSiCatModule.forRoot` provides globally.
|
|
52
|
+
*/
|
|
53
|
+
declare class PaymentsModule {
|
|
54
|
+
static forRoot(options: PaymentsModuleOptions): DynamicModule;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { type PaymentsModuleOptions as P, type PaymentsTenantRoutesOptions as a, PaymentsModule as b };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { PlanCatalog } from '@saasicat/core';
|
|
2
|
+
|
|
3
|
+
/** Where the plans and features of a `PlanCatalogSource` come from. */
|
|
4
|
+
type PlanCatalogOrigin = 'database' | 'given';
|
|
5
|
+
/**
|
|
6
|
+
* The plan catalogue as it stands now.
|
|
7
|
+
*
|
|
8
|
+
* Read it once per operation and hand that value on. Every check the operation
|
|
9
|
+
* makes then sees the same plans, so a version published halfway through
|
|
10
|
+
* cannot price one step at the old figure and the next at the new one.
|
|
11
|
+
*/
|
|
12
|
+
interface PlanCatalogSource {
|
|
13
|
+
/** `database` for the plans an operator publishes, `given` for a fixed catalogue. */
|
|
14
|
+
readonly origin: PlanCatalogOrigin;
|
|
15
|
+
/** The settings of the file, with the plans and features as they stand at this moment. */
|
|
16
|
+
current(): Promise<PlanCatalog>;
|
|
17
|
+
}
|
|
18
|
+
/** A catalogue that does not move: the one given at start, for tests and in-memory setups. */
|
|
19
|
+
declare function givenPlanCatalogSource(catalog: PlanCatalog): PlanCatalogSource;
|
|
20
|
+
|
|
21
|
+
export { type PlanCatalogSource as P, type PlanCatalogOrigin as a, givenPlanCatalogSource as g };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { PlanCatalog } from '@saasicat/core';
|
|
2
|
+
|
|
3
|
+
/** Where the plans and features of a `PlanCatalogSource` come from. */
|
|
4
|
+
type PlanCatalogOrigin = 'database' | 'given';
|
|
5
|
+
/**
|
|
6
|
+
* The plan catalogue as it stands now.
|
|
7
|
+
*
|
|
8
|
+
* Read it once per operation and hand that value on. Every check the operation
|
|
9
|
+
* makes then sees the same plans, so a version published halfway through
|
|
10
|
+
* cannot price one step at the old figure and the next at the new one.
|
|
11
|
+
*/
|
|
12
|
+
interface PlanCatalogSource {
|
|
13
|
+
/** `database` for the plans an operator publishes, `given` for a fixed catalogue. */
|
|
14
|
+
readonly origin: PlanCatalogOrigin;
|
|
15
|
+
/** The settings of the file, with the plans and features as they stand at this moment. */
|
|
16
|
+
current(): Promise<PlanCatalog>;
|
|
17
|
+
}
|
|
18
|
+
/** A catalogue that does not move: the one given at start, for tests and in-memory setups. */
|
|
19
|
+
declare function givenPlanCatalogSource(catalog: PlanCatalog): PlanCatalogSource;
|
|
20
|
+
|
|
21
|
+
export { type PlanCatalogSource as P, type PlanCatalogOrigin as a, givenPlanCatalogSource as g };
|
|
@@ -23,6 +23,20 @@ interface EntitlementResolutionConfig {
|
|
|
23
23
|
* If the subscription sets a `trialEntitlementPlan`, that one wins.
|
|
24
24
|
*/
|
|
25
25
|
defaultTrialEntitlementPlan?: PlanId;
|
|
26
|
+
/**
|
|
27
|
+
* What a subscription keeps once its cancellation has taken effect.
|
|
28
|
+
*
|
|
29
|
+
* Undefined — the default — means nothing: no features, no quotas. That is
|
|
30
|
+
* what ending a contract means, and it is the answer an installation should
|
|
31
|
+
* leave in place unless it has decided otherwise.
|
|
32
|
+
*
|
|
33
|
+
* Set it to keep a floor after the end: a read-only tier that lets a former
|
|
34
|
+
* customer export their data, or a free plan they fall back to. The plan is
|
|
35
|
+
* resolved through the catalog like any other, so it needs an active
|
|
36
|
+
* version; bundle bookings and custom limits are NOT added to it, because
|
|
37
|
+
* those belonged to the subscription that ended.
|
|
38
|
+
*/
|
|
39
|
+
canceledEntitlementPlan?: PlanId;
|
|
26
40
|
}
|
|
27
41
|
/**
|
|
28
42
|
* Resolves the effective plan of a subscription for limit aggregation.
|
|
@@ -23,6 +23,20 @@ interface EntitlementResolutionConfig {
|
|
|
23
23
|
* If the subscription sets a `trialEntitlementPlan`, that one wins.
|
|
24
24
|
*/
|
|
25
25
|
defaultTrialEntitlementPlan?: PlanId;
|
|
26
|
+
/**
|
|
27
|
+
* What a subscription keeps once its cancellation has taken effect.
|
|
28
|
+
*
|
|
29
|
+
* Undefined — the default — means nothing: no features, no quotas. That is
|
|
30
|
+
* what ending a contract means, and it is the answer an installation should
|
|
31
|
+
* leave in place unless it has decided otherwise.
|
|
32
|
+
*
|
|
33
|
+
* Set it to keep a floor after the end: a read-only tier that lets a former
|
|
34
|
+
* customer export their data, or a free plan they fall back to. The plan is
|
|
35
|
+
* resolved through the catalog like any other, so it needs an active
|
|
36
|
+
* version; bundle bookings and custom limits are NOT added to it, because
|
|
37
|
+
* those belonged to the subscription that ended.
|
|
38
|
+
*/
|
|
39
|
+
canceledEntitlementPlan?: PlanId;
|
|
26
40
|
}
|
|
27
41
|
/**
|
|
28
42
|
* Resolves the effective plan of a subscription for limit aggregation.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { BundleRepository, DiscoverySnapshot, SubscriptionRepository, PlanRepository, CatalogEntryRepository, BundleRow, BundleVersionRow, CreateBundleData, UpdateBundleData, CreateBundleVersionDraftData, BundleVersionMutationResult, UpdateBundleVersionDraftData, PublishBundleVersionData, MarketingProjectionRepository, MarketingProjectionFilter, MarketingProjectionRow, CreateMarketingProjectionData, UpdateMarketingProjectionData, FeatureUiRegistry, CapabilityCatalogEntryRow, FeatureCatalogEntryRow, QuotaCatalogEntryRow, ReviewCatalogEntryData, CatalogEntryI18n, UpdateCatalogEntryBaseData, SyncDiscoveryResult, PromotionRepository, PromotionRow, CreatePromotionData, UpdatePromotionData, MarketingSettingsRepository, MarketingSettingsRow, UpdateMarketingSettingsData,
|
|
1
|
+
import { BundleRepository, DiscoverySnapshot, SubscriptionRepository, PlanRepository, CatalogEntryRepository, BundleRow, BundleVersionRow, CreateBundleData, UpdateBundleData, CreateBundleVersionDraftData, BundleVersionMutationResult, UpdateBundleVersionDraftData, PublishBundleVersionData, MarketingProjectionRepository, MarketingProjectionFilter, MarketingProjectionRow, CreateMarketingProjectionData, UpdateMarketingProjectionData, FeatureUiRegistry, CapabilityCatalogEntryRow, FeatureCatalogEntryRow, QuotaCatalogEntryRow, ReviewCatalogEntryData, CatalogEntryI18n, UpdateCatalogEntryBaseData, SyncDiscoveryResult, PromotionRepository, PromotionRow, CreatePromotionData, UpdatePromotionData, MarketingSettingsRepository, MarketingSettingsRow, UpdateMarketingSettingsData, PlanRow, CreatePlanData, UpdatePlanData, PlanVersionRow, CreatePlanVersionDraftData, PlanVersionMutationResult, UpdatePlanVersionDraftData, PublishPlanVersionData } from '@saasicat/core';
|
|
2
2
|
import { OnApplicationBootstrap } from '@nestjs/common';
|
|
3
|
-
import {
|
|
3
|
+
import { a as DiscoveryScanner } from './discovery.scanner-DXKc6JkV.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Configuration for the mutating operations.
|
|
@@ -36,7 +36,7 @@ declare class BundlesService {
|
|
|
36
36
|
private mode;
|
|
37
37
|
private readonly marketedOnly;
|
|
38
38
|
constructor(repo: BundleRepository, snapshot?: DiscoverySnapshot | null, config?: CatalogServiceConfig, subscriptions?: SubscriptionRepository | null, planRepo?: PlanRepository | null, scanner?: DiscoveryScanner | null, catalogEntries?: CatalogEntryRepository | null);
|
|
39
|
-
listBundles(
|
|
39
|
+
listBundles(): Promise<BundleRow[]>;
|
|
40
40
|
getBundle(bundleId: string): Promise<{
|
|
41
41
|
bundle: BundleRow;
|
|
42
42
|
versions: BundleVersionRow[];
|
|
@@ -90,6 +90,26 @@ declare class BundlesService {
|
|
|
90
90
|
*/
|
|
91
91
|
publishBundleVersion(versionId: string, publishMeta: PublishBundleVersionData): Promise<BundleVersionMutationResult>;
|
|
92
92
|
private runStrictCheck;
|
|
93
|
+
/**
|
|
94
|
+
* Refuses a version that a plan it is offered to could not actually buy.
|
|
95
|
+
*
|
|
96
|
+
* "Some price resolves" is not the same as "this plan, in the rhythm it is
|
|
97
|
+
* sold in, resolves one". A bundle priced monthly only, offered to a plan
|
|
98
|
+
* that is sold yearly, passes the first test and fails the second — and the
|
|
99
|
+
* failure surfaces at a tenant's checkout rather than at the operator's
|
|
100
|
+
* desk, which is the wrong end of the mistake.
|
|
101
|
+
*
|
|
102
|
+
* The expectation comes from the catalog, not from a list: the plans are
|
|
103
|
+
* whichever the compatibility declares (empty = every plan), and the cycles
|
|
104
|
+
* are whichever each plan version carries a price for. Add a third cycle to
|
|
105
|
+
* the catalog one day and this asks about it without being told.
|
|
106
|
+
*
|
|
107
|
+
* Skipped where no plan repository is bound — a consumer without one has no
|
|
108
|
+
* catalog to derive from, and guessing would refuse valid bundles.
|
|
109
|
+
*/
|
|
110
|
+
private refuseUnsellableToACompatiblePlan;
|
|
111
|
+
/** The live version of a plan, or null where the port cannot answer. */
|
|
112
|
+
private liveVersionOf;
|
|
93
113
|
private gateOrPass;
|
|
94
114
|
private assertBundleVersionWindowUpdate;
|
|
95
115
|
private findPreviousBundleVersion;
|
|
@@ -123,25 +143,25 @@ declare class CatalogEntriesService implements OnApplicationBootstrap {
|
|
|
123
143
|
* `POST /admin/catalog/discovery/sync` remains as a fallback.
|
|
124
144
|
*/
|
|
125
145
|
onApplicationBootstrap(): Promise<void>;
|
|
126
|
-
listCapabilities(
|
|
127
|
-
listFeatures(
|
|
128
|
-
listQuotas(
|
|
146
|
+
listCapabilities(codeStatus?: string): Promise<CapabilityCatalogEntryRow[]>;
|
|
147
|
+
listFeatures(discoveryStatus?: string): Promise<FeatureCatalogEntryRow[]>;
|
|
148
|
+
listQuotas(discoveryStatus?: string): Promise<QuotaCatalogEntryRow[]>;
|
|
129
149
|
/**
|
|
130
150
|
* Approval transition of a feature (#20). Validates the state machine;
|
|
131
151
|
* `approved` persists the approval signature from the current
|
|
132
152
|
* snapshot, `pending` (revoke/reactivate) deletes it, `outdated`/
|
|
133
153
|
* `obsolete` keep the last approval as history.
|
|
134
154
|
*/
|
|
135
|
-
reviewFeature(
|
|
155
|
+
reviewFeature(featureKey: string, data: ReviewCatalogEntryData, reviewedBy: string | null): Promise<FeatureCatalogEntryRow>;
|
|
136
156
|
/** Approval transition of a quota — same state machine as features (#20). */
|
|
137
|
-
reviewQuota(
|
|
157
|
+
reviewQuota(quotaKey: string, data: ReviewCatalogEntryData, reviewedBy: string | null): Promise<QuotaCatalogEntryRow>;
|
|
138
158
|
private resolveReviewUpdate;
|
|
139
|
-
setFeatureI18n(
|
|
140
|
-
setQuotaI18n(
|
|
159
|
+
setFeatureI18n(featureKey: string, i18n: CatalogEntryI18n): Promise<FeatureCatalogEntryRow>;
|
|
160
|
+
setQuotaI18n(quotaKey: string, i18n: CatalogEntryI18n): Promise<QuotaCatalogEntryRow>;
|
|
141
161
|
/** Sets the editable default-locale label/description of a feature. */
|
|
142
|
-
setFeatureBase(
|
|
162
|
+
setFeatureBase(featureKey: string, data: UpdateCatalogEntryBaseData): Promise<FeatureCatalogEntryRow>;
|
|
143
163
|
/** Sets the editable default-locale label/description of a quota. */
|
|
144
|
-
setQuotaBase(
|
|
164
|
+
setQuotaBase(quotaKey: string, data: UpdateCatalogEntryBaseData): Promise<QuotaCatalogEntryRow>;
|
|
145
165
|
/**
|
|
146
166
|
* Synchronizes the catalog entries from a discovery snapshot.
|
|
147
167
|
* Idempotent — can run against the same snapshot any number of times.
|
|
@@ -161,7 +181,7 @@ declare class CatalogEntriesService implements OnApplicationBootstrap {
|
|
|
161
181
|
declare class PromotionsService {
|
|
162
182
|
private readonly repo;
|
|
163
183
|
constructor(repo: PromotionRepository);
|
|
164
|
-
list(
|
|
184
|
+
list(): Promise<PromotionRow[]>;
|
|
165
185
|
getById(id: string): Promise<PromotionRow>;
|
|
166
186
|
create(data: CreatePromotionData): Promise<PromotionRow>;
|
|
167
187
|
update(id: string, data: UpdatePromotionData): Promise<PromotionRow>;
|
|
@@ -172,54 +192,8 @@ declare class MarketingSettingsService {
|
|
|
172
192
|
private readonly repo;
|
|
173
193
|
constructor(repo: MarketingSettingsRepository);
|
|
174
194
|
/** Returns the stored config or `null` (in which case the full pool applies). */
|
|
175
|
-
get(
|
|
176
|
-
upsert(
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
declare class PublicMarketingCatalogService {
|
|
180
|
-
private readonly planRepo;
|
|
181
|
-
private readonly marketingRepo;
|
|
182
|
-
private readonly promotionRepo;
|
|
183
|
-
private readonly catalogEntryRepo;
|
|
184
|
-
private readonly bundleRepo;
|
|
185
|
-
constructor(planRepo: PlanRepository, marketingRepo: MarketingProjectionRepository, promotionRepo: PromotionRepository, catalogEntryRepo?: CatalogEntryRepository | null, bundleRepo?: BundleRepository | null);
|
|
186
|
-
getCatalog(projectKey: string, locale: string, currency: string, vatRate: number, asOf?: Date): Promise<PublicMarketingCatalogResponse>;
|
|
187
|
-
/**
|
|
188
|
-
* Loads all published-and-live bundle versions for a project and
|
|
189
|
-
* filters on `marketed === true`. Without a registered
|
|
190
|
-
* `BundleRepository` (apps without the SuperAdmin bundle editor) the
|
|
191
|
-
* list is empty — the public catalog stays plan-only.
|
|
192
|
-
*/
|
|
193
|
-
private loadMarketedBundles;
|
|
194
|
-
/**
|
|
195
|
-
* Maps BundleVersionRow → PublicMarketingBundle. Locale resolution
|
|
196
|
-
* runs via `MarketingProjectionRepository.findByTarget('BUNDLE',
|
|
197
|
-
* bundleVersionId, locale)` — on a miss it falls back to
|
|
198
|
-
* `DEFAULT_LOCALE`. If the fallback is missing too, label/description
|
|
199
|
-
* come directly from the bundle root (denormalized in `BundleVersionRow`).
|
|
200
|
-
*
|
|
201
|
-
* If the marketing projection is `visible === false`, the bundle is
|
|
202
|
-
* hidden from the public response (preparation phase).
|
|
203
|
-
*/
|
|
204
|
-
private toPublicBundle;
|
|
205
|
-
/**
|
|
206
|
-
* Labels for bundle features ∪ their requires (#48): `comparison.features`
|
|
207
|
-
* only covers the plan-feature union — bundle-only features would
|
|
208
|
-
* otherwise get no label in the public payload. Curated keys only;
|
|
209
|
-
* without a CatalogEntryRepository the map stays empty (frontends fall
|
|
210
|
-
* back to the key).
|
|
211
|
-
*/
|
|
212
|
-
private resolveBundleFeatureLabels;
|
|
213
|
-
/** Loads the translated feature/quota labels (+ sortOrder) for a locale. */
|
|
214
|
-
private loadLabelMeta;
|
|
215
|
-
/**
|
|
216
|
-
* Resolves the displayed label of a top feature: the `label` override
|
|
217
|
-
* wins; otherwise the translated label of the referenced `key`.
|
|
218
|
-
*/
|
|
219
|
-
private resolveTopFeature;
|
|
220
|
-
/** Row definitions of the comparison matrix (feature/quota union + labels). */
|
|
221
|
-
private buildComparison;
|
|
222
|
-
private resolvePromo;
|
|
195
|
+
get(): Promise<MarketingSettingsRow | null>;
|
|
196
|
+
upsert(data: UpdateMarketingSettingsData): Promise<MarketingSettingsRow>;
|
|
223
197
|
}
|
|
224
198
|
|
|
225
199
|
declare class PlansService {
|
|
@@ -233,8 +207,8 @@ declare class PlansService {
|
|
|
233
207
|
* `SubscriptionRepository` is registered or it does not implement
|
|
234
208
|
* `countActiveByPlanKey`.
|
|
235
209
|
*/
|
|
236
|
-
getTenantCounts(
|
|
237
|
-
listPlans(
|
|
210
|
+
getTenantCounts(): Promise<Record<string, number>>;
|
|
211
|
+
listPlans(opts?: {
|
|
238
212
|
onlyPublished?: boolean;
|
|
239
213
|
}): Promise<PlanRow[]>;
|
|
240
214
|
getPlan(planId: string): Promise<PlanRow>;
|
|
@@ -326,4 +300,4 @@ declare class PlanVersionsService {
|
|
|
326
300
|
private gateOrPass;
|
|
327
301
|
}
|
|
328
302
|
|
|
329
|
-
export { BundlesService as B, CatalogEntriesService as C, MarketingProjectionsService as M, PlanVersionsService as P, type CatalogServiceConfig as a, MarketingSettingsService as b, PlansService as c, PromotionsService as d
|
|
303
|
+
export { BundlesService as B, CatalogEntriesService as C, MarketingProjectionsService as M, PlanVersionsService as P, type CatalogServiceConfig as a, MarketingSettingsService as b, PlansService as c, PromotionsService as d };
|