@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
package/dist/promo/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { PromoCodeValueType, PromoCodeDurationType, BillingCycle, PromoCodeRepository, PromoCodeRedemptionRepository } from '@saasicat/core';
|
|
2
|
-
import {
|
|
3
|
-
export { C as CODE_MAX_LENGTH,
|
|
1
|
+
import { PromoCodeValueType, PromoCodeDurationType, BillingCycle, PromoCodeRepository, PromoCodeRedemptionRepository, PromoCodeHoldRepository } from '@saasicat/core';
|
|
2
|
+
import { a as PromoCodesService, i as PreviewResult } from '../promo.service-CFjfF0Xm.js';
|
|
3
|
+
export { C as CODE_MAX_LENGTH, b as CODE_MIN_LENGTH, c as CODE_PATTERN, d as CheckoutHoldInput, e as PreviewInput, f as PreviewInvalid, g as PreviewOptions, h as PreviewReason, j as PreviewValid, k as PromoCodeStats, P as PromoServiceConfig, R as RedeemInput } from '../promo.service-CFjfF0Xm.js';
|
|
4
4
|
export { P as PromoCodeRateLimitGuard, h as hashIp, i as ipFingerprint } from '../rate-limit.guard-dntqAlfZ.js';
|
|
5
|
-
export {
|
|
5
|
+
export { a as PromoCodesModule, P as PromoCodesModuleOptions } from '../promo.module-DuIJdK3U.js';
|
|
6
6
|
import { Type, CanActivate } from '@nestjs/common';
|
|
7
|
+
import '../plan-catalog-source-DWe-BGY1.js';
|
|
7
8
|
import '../di-CcNeq9v-.js';
|
|
8
9
|
|
|
9
10
|
/** Structural view of a PromoCode for the calculator functions.
|
|
@@ -80,20 +81,34 @@ interface PromoLabelOptions {
|
|
|
80
81
|
declare function buildLabel(code: Pick<PromoCodeForCalc, 'valueType' | 'value' | 'durationType' | 'durationValue'>, cycle: BillingCycle, options?: PromoLabelOptions): string;
|
|
81
82
|
|
|
82
83
|
declare function round2(n: number): number;
|
|
83
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* Whether a tax rate is one SaaSiCat accepts. Every tax rate in SaaSiCat is a
|
|
86
|
+
* percentage — 19 means 19 % — wherever it is stated: the settings, the
|
|
87
|
+
* catalogue, an offer, a contract, a contract line.
|
|
88
|
+
*
|
|
89
|
+
* A percentage lies from 0 to 100, and none lies strictly between 0 and 1:
|
|
90
|
+
* that is the shape of a fraction such as 0.19, and a fraction is refused
|
|
91
|
+
* rather than taken for a fraction of a per cent.
|
|
92
|
+
*/
|
|
93
|
+
declare function isTaxRatePercent(rate: number): boolean;
|
|
94
|
+
/** Gross = net * (1 + vatRate/100), with `vatRate` a percentage. */
|
|
84
95
|
declare function grossFromNet(net: number, vatRate: number): number;
|
|
85
|
-
/**
|
|
96
|
+
/** Net = gross * 100 / (100 + vatRate), with `vatRate` a percentage. */
|
|
97
|
+
declare function netFromGross(gross: number, vatRate: number): number;
|
|
98
|
+
/** VAT included in the gross amount: gross * vatRate / (100 + vatRate). */
|
|
86
99
|
declare function computeIncludedVat(grossAmount: number, vatRate: number): number;
|
|
87
100
|
|
|
88
101
|
declare class PromoCodeExpirer {
|
|
89
102
|
private readonly promoRepo;
|
|
90
103
|
private readonly redemptionRepo;
|
|
104
|
+
private readonly holds;
|
|
91
105
|
private readonly logger;
|
|
92
|
-
constructor(promoRepo: PromoCodeRepository, redemptionRepo: PromoCodeRedemptionRepository);
|
|
106
|
+
constructor(promoRepo: PromoCodeRepository, redemptionRepo: PromoCodeRedemptionRepository, holds?: PromoCodeHoldRepository | null);
|
|
93
107
|
expirePromoCodes(): Promise<void>;
|
|
94
108
|
}
|
|
95
109
|
|
|
96
110
|
declare const PROMO_CODE_REPOSITORY_TOKEN: unique symbol;
|
|
111
|
+
declare const PROMO_CODE_HOLD_REPOSITORY_TOKEN: unique symbol;
|
|
97
112
|
declare const PROMO_CODE_REDEMPTION_REPOSITORY_TOKEN: unique symbol;
|
|
98
113
|
declare const PROMO_CODE_VALIDATION_LOG_REPOSITORY_TOKEN: unique symbol;
|
|
99
114
|
declare const PROMO_FIRST_TIME_CUSTOMER_CHECK_TOKEN: unique symbol;
|
|
@@ -166,4 +181,4 @@ declare class UpdatePromoCodeDto {
|
|
|
166
181
|
revenueDeductionAccount?: string | null;
|
|
167
182
|
}
|
|
168
183
|
|
|
169
|
-
export { CreatePromoCodeDto, PROMO_CODE_REDEMPTION_REPOSITORY_TOKEN, PROMO_CODE_REPOSITORY_TOKEN, PROMO_CODE_VALIDATION_LOG_REPOSITORY_TOKEN, PROMO_FIRST_TIME_CUSTOMER_CHECK_TOKEN, PROMO_REVENUE_DEDUCTION_AGGREGATOR_TOKEN, PROMO_SERVICE_CONFIG_TOKEN, PROMO_SUBSCRIPTION_LOOKUP_TOKEN, PROMO_TRANSACTION_RUNNER_TOKEN, PreviewPromoCodeDto, PreviewResult, PromoCodeExpirer, type PromoCodeForCalc, PromoCodePublicController, PromoCodesService, type PromoLabelOptions, UpdatePromoCodeDto, addCycles, buildLabel, buildPromoCodeAdminController, computeDiscountGross, computeDiscountedGross, computeIncludedVat, computeRegularStartsAt, grossFromNet, round2 };
|
|
184
|
+
export { CreatePromoCodeDto, PROMO_CODE_HOLD_REPOSITORY_TOKEN, PROMO_CODE_REDEMPTION_REPOSITORY_TOKEN, PROMO_CODE_REPOSITORY_TOKEN, PROMO_CODE_VALIDATION_LOG_REPOSITORY_TOKEN, PROMO_FIRST_TIME_CUSTOMER_CHECK_TOKEN, PROMO_REVENUE_DEDUCTION_AGGREGATOR_TOKEN, PROMO_SERVICE_CONFIG_TOKEN, PROMO_SUBSCRIPTION_LOOKUP_TOKEN, PROMO_TRANSACTION_RUNNER_TOKEN, PreviewPromoCodeDto, PreviewResult, PromoCodeExpirer, type PromoCodeForCalc, PromoCodePublicController, PromoCodesService, type PromoLabelOptions, UpdatePromoCodeDto, addCycles, buildLabel, buildPromoCodeAdminController, computeDiscountGross, computeDiscountedGross, computeIncludedVat, computeRegularStartsAt, grossFromNet, isTaxRatePercent, netFromGross, round2 };
|
package/dist/promo/index.js
CHANGED
|
@@ -10,11 +10,13 @@ import {
|
|
|
10
10
|
buildPromoCodeAdminController,
|
|
11
11
|
hashIp,
|
|
12
12
|
ipFingerprint
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-4SDSYV4A.js";
|
|
14
|
+
import "../chunk-OOTEXP47.js";
|
|
14
15
|
import {
|
|
15
16
|
CODE_MAX_LENGTH,
|
|
16
17
|
CODE_MIN_LENGTH,
|
|
17
18
|
CODE_PATTERN,
|
|
19
|
+
PROMO_CODE_HOLD_REPOSITORY_TOKEN,
|
|
18
20
|
PROMO_CODE_REDEMPTION_REPOSITORY_TOKEN,
|
|
19
21
|
PROMO_CODE_REPOSITORY_TOKEN,
|
|
20
22
|
PROMO_CODE_VALIDATION_LOG_REPOSITORY_TOKEN,
|
|
@@ -29,22 +31,26 @@ import {
|
|
|
29
31
|
computeDiscountGross,
|
|
30
32
|
computeDiscountedGross,
|
|
31
33
|
computeRegularStartsAt
|
|
32
|
-
} from "../chunk-
|
|
33
|
-
import "../chunk-
|
|
34
|
-
import "../chunk-YSC46BGM.js";
|
|
34
|
+
} from "../chunk-3RLWML2S.js";
|
|
35
|
+
import "../chunk-TBBZWZQT.js";
|
|
35
36
|
import {
|
|
36
37
|
computeIncludedVat,
|
|
37
38
|
grossFromNet,
|
|
39
|
+
isTaxRatePercent,
|
|
40
|
+
netFromGross,
|
|
38
41
|
round2
|
|
39
|
-
} from "../chunk-
|
|
40
|
-
import "../chunk-
|
|
41
|
-
import "../chunk-
|
|
42
|
+
} from "../chunk-6PJE7EWO.js";
|
|
43
|
+
import "../chunk-SZ7RFPXA.js";
|
|
44
|
+
import "../chunk-YSC46BGM.js";
|
|
45
|
+
import "../chunk-FEQZCWUL.js";
|
|
46
|
+
import "../chunk-J7NJRK3K.js";
|
|
42
47
|
import "../chunk-SHUYVCID.js";
|
|
43
48
|
export {
|
|
44
49
|
CODE_MAX_LENGTH,
|
|
45
50
|
CODE_MIN_LENGTH,
|
|
46
51
|
CODE_PATTERN,
|
|
47
52
|
CreatePromoCodeDto,
|
|
53
|
+
PROMO_CODE_HOLD_REPOSITORY_TOKEN,
|
|
48
54
|
PROMO_CODE_REDEMPTION_REPOSITORY_TOKEN,
|
|
49
55
|
PROMO_CODE_REPOSITORY_TOKEN,
|
|
50
56
|
PROMO_CODE_VALIDATION_LOG_REPOSITORY_TOKEN,
|
|
@@ -70,5 +76,7 @@ export {
|
|
|
70
76
|
grossFromNet,
|
|
71
77
|
hashIp,
|
|
72
78
|
ipFingerprint,
|
|
79
|
+
isTaxRatePercent,
|
|
80
|
+
netFromGross,
|
|
73
81
|
round2
|
|
74
82
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Type, CanActivate, DynamicModule, ForwardReference } from '@nestjs/common';
|
|
2
|
-
import { PromoCodeRepository, PromoCodeRedemptionRepository, PromoCodeValidationLogRepository, FirstTimeCustomerCheck, PromoSubscriptionLookup, PromoRevenueDeductionAggregator, TransactionRunner } from '@saasicat/core';
|
|
2
|
+
import { PromoCodeRepository, PromoCodeRedemptionRepository, PromoCodeValidationLogRepository, FirstTimeCustomerCheck, PromoSubscriptionLookup, PromoRevenueDeductionAggregator, TransactionRunner, PromoCodeHoldRepository } from '@saasicat/core';
|
|
3
3
|
import { P as ProviderSpec } from './di-CcNeq9v-.cjs';
|
|
4
|
-
import { P as PromoServiceConfig } from './promo.service-
|
|
4
|
+
import { P as PromoServiceConfig } from './promo.service-BYBu6dy3.cjs';
|
|
5
5
|
|
|
6
6
|
interface PromoCodesModuleOptions {
|
|
7
7
|
promoCodeRepository: ProviderSpec<PromoCodeRepository>;
|
|
@@ -11,6 +11,12 @@ interface PromoCodesModuleOptions {
|
|
|
11
11
|
subscriptionLookup: ProviderSpec<PromoSubscriptionLookup>;
|
|
12
12
|
revenueAggregator: ProviderSpec<PromoRevenueDeductionAggregator>;
|
|
13
13
|
transactionRunner: ProviderSpec<TransactionRunner>;
|
|
14
|
+
/**
|
|
15
|
+
* The slots a code keeps for checkouts that have started. Left out, no
|
|
16
|
+
* checkout holds a slot, and a sign-up that asks for one is refused at
|
|
17
|
+
* start-up of the hold rather than holding nothing.
|
|
18
|
+
*/
|
|
19
|
+
holdRepository?: ProviderSpec<PromoCodeHoldRepository>;
|
|
14
20
|
config?: PromoServiceConfig;
|
|
15
21
|
/**
|
|
16
22
|
* Default `true`. Set to `false` if the consumer wants to disable the cron
|
|
@@ -36,4 +42,4 @@ declare class PromoCodesModule {
|
|
|
36
42
|
static forRoot(options: PromoCodesModuleOptions): DynamicModule;
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
export {
|
|
45
|
+
export { type PromoCodesModuleOptions as P, PromoCodesModule as a };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Type, CanActivate, DynamicModule, ForwardReference } from '@nestjs/common';
|
|
2
|
-
import { PromoCodeRepository, PromoCodeRedemptionRepository, PromoCodeValidationLogRepository, FirstTimeCustomerCheck, PromoSubscriptionLookup, PromoRevenueDeductionAggregator, TransactionRunner } from '@saasicat/core';
|
|
2
|
+
import { PromoCodeRepository, PromoCodeRedemptionRepository, PromoCodeValidationLogRepository, FirstTimeCustomerCheck, PromoSubscriptionLookup, PromoRevenueDeductionAggregator, TransactionRunner, PromoCodeHoldRepository } from '@saasicat/core';
|
|
3
3
|
import { P as ProviderSpec } from './di-CcNeq9v-.js';
|
|
4
|
-
import { P as PromoServiceConfig } from './promo.service-
|
|
4
|
+
import { P as PromoServiceConfig } from './promo.service-CFjfF0Xm.js';
|
|
5
5
|
|
|
6
6
|
interface PromoCodesModuleOptions {
|
|
7
7
|
promoCodeRepository: ProviderSpec<PromoCodeRepository>;
|
|
@@ -11,6 +11,12 @@ interface PromoCodesModuleOptions {
|
|
|
11
11
|
subscriptionLookup: ProviderSpec<PromoSubscriptionLookup>;
|
|
12
12
|
revenueAggregator: ProviderSpec<PromoRevenueDeductionAggregator>;
|
|
13
13
|
transactionRunner: ProviderSpec<TransactionRunner>;
|
|
14
|
+
/**
|
|
15
|
+
* The slots a code keeps for checkouts that have started. Left out, no
|
|
16
|
+
* checkout holds a slot, and a sign-up that asks for one is refused at
|
|
17
|
+
* start-up of the hold rather than holding nothing.
|
|
18
|
+
*/
|
|
19
|
+
holdRepository?: ProviderSpec<PromoCodeHoldRepository>;
|
|
14
20
|
config?: PromoServiceConfig;
|
|
15
21
|
/**
|
|
16
22
|
* Default `true`. Set to `false` if the consumer wants to disable the cron
|
|
@@ -36,4 +42,4 @@ declare class PromoCodesModule {
|
|
|
36
42
|
static forRoot(options: PromoCodesModuleOptions): DynamicModule;
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
export {
|
|
45
|
+
export { type PromoCodesModuleOptions as P, PromoCodesModule as a };
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { PromoCodeRepository, PromoCodeRedemptionRepository, PromoCodeValidationLogRepository, FirstTimeCustomerCheck, PromoSubscriptionLookup, PromoRevenueDeductionAggregator, TransactionRunner, PromoCodeHoldRepository, CreatePromoCodeData, PromoCodeRecord, UpdatePromoCodeData, PromoCodeFilter, PromoCodeRedemptionListItem, BillingCycle, PromoPreviewInvalidReason, TransactionContext, PromoCodeRedemptionRecord } from '@saasicat/core';
|
|
2
|
+
import { P as PlanCatalogSource } from './plan-catalog-source-DWe-BGY1.cjs';
|
|
3
|
+
|
|
4
|
+
declare const CODE_MIN_LENGTH = 4;
|
|
5
|
+
declare const CODE_MAX_LENGTH = 32;
|
|
6
|
+
declare const CODE_PATTERN: RegExp;
|
|
7
|
+
type PreviewReason = PromoPreviewInvalidReason;
|
|
8
|
+
type PreviewInvalid = {
|
|
9
|
+
valid: false;
|
|
10
|
+
reason: PreviewReason;
|
|
11
|
+
};
|
|
12
|
+
interface PreviewValid {
|
|
13
|
+
valid: true;
|
|
14
|
+
code: string;
|
|
15
|
+
label: string;
|
|
16
|
+
discount: {
|
|
17
|
+
valueType: PromoCodeRecord['valueType'];
|
|
18
|
+
value: string;
|
|
19
|
+
durationType: PromoCodeRecord['durationType'];
|
|
20
|
+
durationValue: number | null;
|
|
21
|
+
};
|
|
22
|
+
price: {
|
|
23
|
+
originalGross: string;
|
|
24
|
+
discountGross: string;
|
|
25
|
+
discountNet: string;
|
|
26
|
+
discountedGross: string;
|
|
27
|
+
includedVat: string;
|
|
28
|
+
nextRegularAmountGross: string;
|
|
29
|
+
regularStartsAt: string | null;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
type PreviewResult = PreviewValid | PreviewInvalid;
|
|
33
|
+
interface PreviewInput {
|
|
34
|
+
code: string;
|
|
35
|
+
planId: string;
|
|
36
|
+
billingCycle: BillingCycle;
|
|
37
|
+
/** For the firstTimeCustomersOnly check. */
|
|
38
|
+
email?: string;
|
|
39
|
+
ipHash?: string;
|
|
40
|
+
sessionId?: string;
|
|
41
|
+
}
|
|
42
|
+
interface PreviewOptions {
|
|
43
|
+
/**
|
|
44
|
+
* The checkout offer the code is priced for. A live slot that offer holds
|
|
45
|
+
* is its own and is not counted against it.
|
|
46
|
+
*/
|
|
47
|
+
checkoutOfferId?: string;
|
|
48
|
+
/**
|
|
49
|
+
* The offer is priced as it is concluded: its live slot also keeps the
|
|
50
|
+
* code's state as it was when the slot was taken, so a code paused or past
|
|
51
|
+
* its validity since is still accepted for the checkout that holds it.
|
|
52
|
+
*/
|
|
53
|
+
concluding?: boolean;
|
|
54
|
+
}
|
|
55
|
+
interface RedeemInput {
|
|
56
|
+
code: string;
|
|
57
|
+
subscriptionId: string;
|
|
58
|
+
tenantId: string;
|
|
59
|
+
/** For the firstTimeCustomersOnly check in the final redeem path. */
|
|
60
|
+
email?: string;
|
|
61
|
+
}
|
|
62
|
+
/** A slot of a code to hold for a checkout. */
|
|
63
|
+
interface CheckoutHoldInput {
|
|
64
|
+
/** The checkout offer the slot is held for; it holds one at most. */
|
|
65
|
+
checkoutOfferId: string;
|
|
66
|
+
code: string;
|
|
67
|
+
planId: string;
|
|
68
|
+
billingCycle: BillingCycle;
|
|
69
|
+
/** For the firstTimeCustomersOnly check. */
|
|
70
|
+
email?: string;
|
|
71
|
+
/** When the checkout expires, and with it the hold. */
|
|
72
|
+
until: Date;
|
|
73
|
+
}
|
|
74
|
+
interface PromoServiceConfig {
|
|
75
|
+
/** Plans that are not discountable (e.g. 'ENTERPRISE'). */
|
|
76
|
+
nonRedeemablePlans?: readonly string[];
|
|
77
|
+
}
|
|
78
|
+
interface PromoCodeStats {
|
|
79
|
+
code: PromoCodeRecord;
|
|
80
|
+
validations: number;
|
|
81
|
+
redemptions: {
|
|
82
|
+
active: number;
|
|
83
|
+
total: number;
|
|
84
|
+
reversed: number;
|
|
85
|
+
expired: number;
|
|
86
|
+
};
|
|
87
|
+
/** Slots held for checkouts that have not concluded, which no one else can take. */
|
|
88
|
+
held: number;
|
|
89
|
+
/** Decimal-as-string ('0.00' if no aggregator is available). */
|
|
90
|
+
revenueDeductionGross: string;
|
|
91
|
+
}
|
|
92
|
+
declare class PromoCodesService {
|
|
93
|
+
private readonly promoRepo;
|
|
94
|
+
private readonly redemptionRepo;
|
|
95
|
+
private readonly validationLogRepo;
|
|
96
|
+
private readonly firstTimeCheck;
|
|
97
|
+
private readonly subscriptionLookup;
|
|
98
|
+
private readonly revenueAggregator;
|
|
99
|
+
private readonly transactionRunner;
|
|
100
|
+
private readonly planCatalogs;
|
|
101
|
+
private readonly config;
|
|
102
|
+
private readonly holds;
|
|
103
|
+
constructor(promoRepo: PromoCodeRepository, redemptionRepo: PromoCodeRedemptionRepository, validationLogRepo: PromoCodeValidationLogRepository, firstTimeCheck: FirstTimeCustomerCheck, subscriptionLookup: PromoSubscriptionLookup, revenueAggregator: PromoRevenueDeductionAggregator, transactionRunner: TransactionRunner, planCatalogs: PlanCatalogSource, config: PromoServiceConfig, holds?: PromoCodeHoldRepository | null);
|
|
104
|
+
create(input: CreatePromoCodeData): Promise<PromoCodeRecord>;
|
|
105
|
+
/**
|
|
106
|
+
* Changes a code under the rules a new one is held to, asking the rules the
|
|
107
|
+
* named fields bear on: the terms as they stand after the change have to be
|
|
108
|
+
* ones `create` would accept. Pausing alone is always possible.
|
|
109
|
+
*/
|
|
110
|
+
update(id: string, input: UpdatePromoCodeData): Promise<PromoCodeRecord>;
|
|
111
|
+
softDelete(id: string): Promise<void>;
|
|
112
|
+
findAll(filter?: PromoCodeFilter): Promise<PromoCodeRecord[]>;
|
|
113
|
+
findOne(id: string): Promise<PromoCodeRecord>;
|
|
114
|
+
stats(id: string): Promise<PromoCodeStats>;
|
|
115
|
+
listRedemptions(promoCodeId: string): Promise<PromoCodeRedemptionListItem[]>;
|
|
116
|
+
preview(input: PreviewInput, options?: PreviewOptions): Promise<PreviewResult>;
|
|
117
|
+
/**
|
|
118
|
+
* Holds a slot of the code for a checkout offer until `until`, after every
|
|
119
|
+
* check a preview makes — so a customer who reaches the payment form keeps
|
|
120
|
+
* the code however many others redeem it meanwhile. Starting the same
|
|
121
|
+
* checkout again moves the expiry of the slot it holds rather than taking a
|
|
122
|
+
* second — later, never earlier (`PromoCodeHoldRepository.extend`); a
|
|
123
|
+
* checkout that held another code gives that one back.
|
|
124
|
+
*
|
|
125
|
+
* Refused with `PROMO_CODE_NOT_REDEEMABLE` and the reason a preview would
|
|
126
|
+
* give, `EXHAUSTED` when every slot is redeemed or held — also when the
|
|
127
|
+
* code changed between that check and taking the slot. Nothing is held
|
|
128
|
+
* then, and a slot the offer held before stays with it until it expires.
|
|
129
|
+
*/
|
|
130
|
+
holdForCheckout(input: CheckoutHoldInput): Promise<void>;
|
|
131
|
+
/** Gives back the slot a checkout offer holds, if it holds one. */
|
|
132
|
+
releaseCheckoutHold(checkoutOfferId: string, tx?: TransactionContext): Promise<void>;
|
|
133
|
+
/**
|
|
134
|
+
* Gives back the slot a checkout offer holds only while it still expires at
|
|
135
|
+
* `expiresAt` — the hold as the caller wrote it. A slot another start of the
|
|
136
|
+
* checkout moved since stays with the form that start opened.
|
|
137
|
+
*/
|
|
138
|
+
releaseCheckoutHoldIfUnmoved(checkoutOfferId: string, expiresAt: Date): Promise<void>;
|
|
139
|
+
/**
|
|
140
|
+
* Hands the offer's live hold to the redemption that runs on `tx`: redeeming
|
|
141
|
+
* the code there takes the held slot instead of a free one, whatever
|
|
142
|
+
* happened to the code since the hold was taken. The caller releases it
|
|
143
|
+
* with `releaseCheckoutHold` on the same transaction once the redemption
|
|
144
|
+
* had its chance — a hold the redemption did not take is not held any more.
|
|
145
|
+
* False when the offer holds no live slot.
|
|
146
|
+
*/
|
|
147
|
+
handOverCheckoutHold(checkoutOfferId: string, tx: TransactionContext): Promise<boolean>;
|
|
148
|
+
/**
|
|
149
|
+
* Wraps `redeemInTransaction` in its own transaction-runner call —
|
|
150
|
+
* the default path when the caller has no external tx context
|
|
151
|
+
* (e.g. POST /billing/promo/redeem as a stand-alone endpoint).
|
|
152
|
+
*/
|
|
153
|
+
redeem(input: RedeemInput): Promise<PromoCodeRedemptionRecord>;
|
|
154
|
+
/**
|
|
155
|
+
* Redeems a promo code within an EXTERNAL transaction — the caller
|
|
156
|
+
* (typically `TenantSubscriptionWritePort.applyOnboardingSelection`)
|
|
157
|
+
* already has `prisma.$transaction(...)` open and passes the `tx`
|
|
158
|
+
* context through. This lands plan change, add-on insert, and redemption
|
|
159
|
+
* insert in a single DB transaction (P10.1.1).
|
|
160
|
+
*
|
|
161
|
+
* A checkout that held a slot of the code, and whose conclusion handed it
|
|
162
|
+
* over on this transaction, is redeemed on that slot: its state is not
|
|
163
|
+
* asked again, and no free slot is taken. Otherwise a free one is claimed.
|
|
164
|
+
* Either way the redemption is refused where a preview would refuse on the
|
|
165
|
+
* price — an invoice of zero the code does not allow — and it records at
|
|
166
|
+
* most the price it is redeemed against.
|
|
167
|
+
*/
|
|
168
|
+
redeemInTransaction(input: RedeemInput, tx: TransactionContext): Promise<PromoCodeRedemptionRecord>;
|
|
169
|
+
reverse(subscriptionId: string): Promise<PromoCodeRedemptionRecord | null>;
|
|
170
|
+
/**
|
|
171
|
+
* Why a code does not apply to a plan, or what it takes off it. The plans
|
|
172
|
+
* are read only once the code itself has passed, so somebody trying codes
|
|
173
|
+
* that do not exist costs no read of the plan tables.
|
|
174
|
+
*/
|
|
175
|
+
private judge;
|
|
176
|
+
/** Everything about a code that needs no plan price: its state, its slots, and the plans it fits. */
|
|
177
|
+
private checkCode;
|
|
178
|
+
/** Whether the code can be used now at all: its status and its validity window. */
|
|
179
|
+
private checkState;
|
|
180
|
+
/** Whether the plan is sold at a price in this rhythm, and one the code's minimum allows. */
|
|
181
|
+
private checkPlanPrice;
|
|
182
|
+
private checkFirstTimeCustomer;
|
|
183
|
+
/**
|
|
184
|
+
* Lazy expiry: sets codes whose validUntil has passed to EXPIRED.
|
|
185
|
+
* Called before every find/preview — defense-in-depth alongside the cron.
|
|
186
|
+
*/
|
|
187
|
+
private lazyExpire;
|
|
188
|
+
/**
|
|
189
|
+
* The code with the slots of its expired holds given back, so that what
|
|
190
|
+
* it counts as held is what is held now. One code at a time: this runs on
|
|
191
|
+
* every preview, and a sweep of one code locks one row.
|
|
192
|
+
*/
|
|
193
|
+
private findWithLiveHolds;
|
|
194
|
+
/**
|
|
195
|
+
* Why a code the checks passed a moment ago had no slot to take: it may
|
|
196
|
+
* have been paused, deleted or run past its validity in between, and a
|
|
197
|
+
* refusal that says "exhausted" for any of them misleads the customer and
|
|
198
|
+
* the log a support answer is built on.
|
|
199
|
+
*/
|
|
200
|
+
private whyNoSlot;
|
|
201
|
+
/** Whether the checkout offer holds a slot of this code that has not expired. */
|
|
202
|
+
private holdsLiveSlot;
|
|
203
|
+
/** The slots of a code held right now, its expired holds given back first. */
|
|
204
|
+
private liveHeldCount;
|
|
205
|
+
private requireHolds;
|
|
206
|
+
private ruleContext;
|
|
207
|
+
/**
|
|
208
|
+
* Lowest applicable plan price. With a whitelist it takes the minimum
|
|
209
|
+
* from the whitelist, otherwise across all marketed plans of the catalog
|
|
210
|
+
* (except non-redeemable).
|
|
211
|
+
*/
|
|
212
|
+
private lowestApplicablePlanGross;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export { CODE_MAX_LENGTH as C, type PromoServiceConfig as P, type RedeemInput as R, PromoCodesService as a, CODE_MIN_LENGTH as b, CODE_PATTERN as c, type CheckoutHoldInput as d, type PreviewInput as e, type PreviewInvalid as f, type PreviewOptions as g, type PreviewReason as h, type PreviewResult as i, type PreviewValid as j, type PromoCodeStats as k };
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { PromoCodeRepository, PromoCodeRedemptionRepository, PromoCodeValidationLogRepository, FirstTimeCustomerCheck, PromoSubscriptionLookup, PromoRevenueDeductionAggregator, TransactionRunner, PromoCodeHoldRepository, CreatePromoCodeData, PromoCodeRecord, UpdatePromoCodeData, PromoCodeFilter, PromoCodeRedemptionListItem, BillingCycle, PromoPreviewInvalidReason, TransactionContext, PromoCodeRedemptionRecord } from '@saasicat/core';
|
|
2
|
+
import { P as PlanCatalogSource } from './plan-catalog-source-DWe-BGY1.js';
|
|
3
|
+
|
|
4
|
+
declare const CODE_MIN_LENGTH = 4;
|
|
5
|
+
declare const CODE_MAX_LENGTH = 32;
|
|
6
|
+
declare const CODE_PATTERN: RegExp;
|
|
7
|
+
type PreviewReason = PromoPreviewInvalidReason;
|
|
8
|
+
type PreviewInvalid = {
|
|
9
|
+
valid: false;
|
|
10
|
+
reason: PreviewReason;
|
|
11
|
+
};
|
|
12
|
+
interface PreviewValid {
|
|
13
|
+
valid: true;
|
|
14
|
+
code: string;
|
|
15
|
+
label: string;
|
|
16
|
+
discount: {
|
|
17
|
+
valueType: PromoCodeRecord['valueType'];
|
|
18
|
+
value: string;
|
|
19
|
+
durationType: PromoCodeRecord['durationType'];
|
|
20
|
+
durationValue: number | null;
|
|
21
|
+
};
|
|
22
|
+
price: {
|
|
23
|
+
originalGross: string;
|
|
24
|
+
discountGross: string;
|
|
25
|
+
discountNet: string;
|
|
26
|
+
discountedGross: string;
|
|
27
|
+
includedVat: string;
|
|
28
|
+
nextRegularAmountGross: string;
|
|
29
|
+
regularStartsAt: string | null;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
type PreviewResult = PreviewValid | PreviewInvalid;
|
|
33
|
+
interface PreviewInput {
|
|
34
|
+
code: string;
|
|
35
|
+
planId: string;
|
|
36
|
+
billingCycle: BillingCycle;
|
|
37
|
+
/** For the firstTimeCustomersOnly check. */
|
|
38
|
+
email?: string;
|
|
39
|
+
ipHash?: string;
|
|
40
|
+
sessionId?: string;
|
|
41
|
+
}
|
|
42
|
+
interface PreviewOptions {
|
|
43
|
+
/**
|
|
44
|
+
* The checkout offer the code is priced for. A live slot that offer holds
|
|
45
|
+
* is its own and is not counted against it.
|
|
46
|
+
*/
|
|
47
|
+
checkoutOfferId?: string;
|
|
48
|
+
/**
|
|
49
|
+
* The offer is priced as it is concluded: its live slot also keeps the
|
|
50
|
+
* code's state as it was when the slot was taken, so a code paused or past
|
|
51
|
+
* its validity since is still accepted for the checkout that holds it.
|
|
52
|
+
*/
|
|
53
|
+
concluding?: boolean;
|
|
54
|
+
}
|
|
55
|
+
interface RedeemInput {
|
|
56
|
+
code: string;
|
|
57
|
+
subscriptionId: string;
|
|
58
|
+
tenantId: string;
|
|
59
|
+
/** For the firstTimeCustomersOnly check in the final redeem path. */
|
|
60
|
+
email?: string;
|
|
61
|
+
}
|
|
62
|
+
/** A slot of a code to hold for a checkout. */
|
|
63
|
+
interface CheckoutHoldInput {
|
|
64
|
+
/** The checkout offer the slot is held for; it holds one at most. */
|
|
65
|
+
checkoutOfferId: string;
|
|
66
|
+
code: string;
|
|
67
|
+
planId: string;
|
|
68
|
+
billingCycle: BillingCycle;
|
|
69
|
+
/** For the firstTimeCustomersOnly check. */
|
|
70
|
+
email?: string;
|
|
71
|
+
/** When the checkout expires, and with it the hold. */
|
|
72
|
+
until: Date;
|
|
73
|
+
}
|
|
74
|
+
interface PromoServiceConfig {
|
|
75
|
+
/** Plans that are not discountable (e.g. 'ENTERPRISE'). */
|
|
76
|
+
nonRedeemablePlans?: readonly string[];
|
|
77
|
+
}
|
|
78
|
+
interface PromoCodeStats {
|
|
79
|
+
code: PromoCodeRecord;
|
|
80
|
+
validations: number;
|
|
81
|
+
redemptions: {
|
|
82
|
+
active: number;
|
|
83
|
+
total: number;
|
|
84
|
+
reversed: number;
|
|
85
|
+
expired: number;
|
|
86
|
+
};
|
|
87
|
+
/** Slots held for checkouts that have not concluded, which no one else can take. */
|
|
88
|
+
held: number;
|
|
89
|
+
/** Decimal-as-string ('0.00' if no aggregator is available). */
|
|
90
|
+
revenueDeductionGross: string;
|
|
91
|
+
}
|
|
92
|
+
declare class PromoCodesService {
|
|
93
|
+
private readonly promoRepo;
|
|
94
|
+
private readonly redemptionRepo;
|
|
95
|
+
private readonly validationLogRepo;
|
|
96
|
+
private readonly firstTimeCheck;
|
|
97
|
+
private readonly subscriptionLookup;
|
|
98
|
+
private readonly revenueAggregator;
|
|
99
|
+
private readonly transactionRunner;
|
|
100
|
+
private readonly planCatalogs;
|
|
101
|
+
private readonly config;
|
|
102
|
+
private readonly holds;
|
|
103
|
+
constructor(promoRepo: PromoCodeRepository, redemptionRepo: PromoCodeRedemptionRepository, validationLogRepo: PromoCodeValidationLogRepository, firstTimeCheck: FirstTimeCustomerCheck, subscriptionLookup: PromoSubscriptionLookup, revenueAggregator: PromoRevenueDeductionAggregator, transactionRunner: TransactionRunner, planCatalogs: PlanCatalogSource, config: PromoServiceConfig, holds?: PromoCodeHoldRepository | null);
|
|
104
|
+
create(input: CreatePromoCodeData): Promise<PromoCodeRecord>;
|
|
105
|
+
/**
|
|
106
|
+
* Changes a code under the rules a new one is held to, asking the rules the
|
|
107
|
+
* named fields bear on: the terms as they stand after the change have to be
|
|
108
|
+
* ones `create` would accept. Pausing alone is always possible.
|
|
109
|
+
*/
|
|
110
|
+
update(id: string, input: UpdatePromoCodeData): Promise<PromoCodeRecord>;
|
|
111
|
+
softDelete(id: string): Promise<void>;
|
|
112
|
+
findAll(filter?: PromoCodeFilter): Promise<PromoCodeRecord[]>;
|
|
113
|
+
findOne(id: string): Promise<PromoCodeRecord>;
|
|
114
|
+
stats(id: string): Promise<PromoCodeStats>;
|
|
115
|
+
listRedemptions(promoCodeId: string): Promise<PromoCodeRedemptionListItem[]>;
|
|
116
|
+
preview(input: PreviewInput, options?: PreviewOptions): Promise<PreviewResult>;
|
|
117
|
+
/**
|
|
118
|
+
* Holds a slot of the code for a checkout offer until `until`, after every
|
|
119
|
+
* check a preview makes — so a customer who reaches the payment form keeps
|
|
120
|
+
* the code however many others redeem it meanwhile. Starting the same
|
|
121
|
+
* checkout again moves the expiry of the slot it holds rather than taking a
|
|
122
|
+
* second — later, never earlier (`PromoCodeHoldRepository.extend`); a
|
|
123
|
+
* checkout that held another code gives that one back.
|
|
124
|
+
*
|
|
125
|
+
* Refused with `PROMO_CODE_NOT_REDEEMABLE` and the reason a preview would
|
|
126
|
+
* give, `EXHAUSTED` when every slot is redeemed or held — also when the
|
|
127
|
+
* code changed between that check and taking the slot. Nothing is held
|
|
128
|
+
* then, and a slot the offer held before stays with it until it expires.
|
|
129
|
+
*/
|
|
130
|
+
holdForCheckout(input: CheckoutHoldInput): Promise<void>;
|
|
131
|
+
/** Gives back the slot a checkout offer holds, if it holds one. */
|
|
132
|
+
releaseCheckoutHold(checkoutOfferId: string, tx?: TransactionContext): Promise<void>;
|
|
133
|
+
/**
|
|
134
|
+
* Gives back the slot a checkout offer holds only while it still expires at
|
|
135
|
+
* `expiresAt` — the hold as the caller wrote it. A slot another start of the
|
|
136
|
+
* checkout moved since stays with the form that start opened.
|
|
137
|
+
*/
|
|
138
|
+
releaseCheckoutHoldIfUnmoved(checkoutOfferId: string, expiresAt: Date): Promise<void>;
|
|
139
|
+
/**
|
|
140
|
+
* Hands the offer's live hold to the redemption that runs on `tx`: redeeming
|
|
141
|
+
* the code there takes the held slot instead of a free one, whatever
|
|
142
|
+
* happened to the code since the hold was taken. The caller releases it
|
|
143
|
+
* with `releaseCheckoutHold` on the same transaction once the redemption
|
|
144
|
+
* had its chance — a hold the redemption did not take is not held any more.
|
|
145
|
+
* False when the offer holds no live slot.
|
|
146
|
+
*/
|
|
147
|
+
handOverCheckoutHold(checkoutOfferId: string, tx: TransactionContext): Promise<boolean>;
|
|
148
|
+
/**
|
|
149
|
+
* Wraps `redeemInTransaction` in its own transaction-runner call —
|
|
150
|
+
* the default path when the caller has no external tx context
|
|
151
|
+
* (e.g. POST /billing/promo/redeem as a stand-alone endpoint).
|
|
152
|
+
*/
|
|
153
|
+
redeem(input: RedeemInput): Promise<PromoCodeRedemptionRecord>;
|
|
154
|
+
/**
|
|
155
|
+
* Redeems a promo code within an EXTERNAL transaction — the caller
|
|
156
|
+
* (typically `TenantSubscriptionWritePort.applyOnboardingSelection`)
|
|
157
|
+
* already has `prisma.$transaction(...)` open and passes the `tx`
|
|
158
|
+
* context through. This lands plan change, add-on insert, and redemption
|
|
159
|
+
* insert in a single DB transaction (P10.1.1).
|
|
160
|
+
*
|
|
161
|
+
* A checkout that held a slot of the code, and whose conclusion handed it
|
|
162
|
+
* over on this transaction, is redeemed on that slot: its state is not
|
|
163
|
+
* asked again, and no free slot is taken. Otherwise a free one is claimed.
|
|
164
|
+
* Either way the redemption is refused where a preview would refuse on the
|
|
165
|
+
* price — an invoice of zero the code does not allow — and it records at
|
|
166
|
+
* most the price it is redeemed against.
|
|
167
|
+
*/
|
|
168
|
+
redeemInTransaction(input: RedeemInput, tx: TransactionContext): Promise<PromoCodeRedemptionRecord>;
|
|
169
|
+
reverse(subscriptionId: string): Promise<PromoCodeRedemptionRecord | null>;
|
|
170
|
+
/**
|
|
171
|
+
* Why a code does not apply to a plan, or what it takes off it. The plans
|
|
172
|
+
* are read only once the code itself has passed, so somebody trying codes
|
|
173
|
+
* that do not exist costs no read of the plan tables.
|
|
174
|
+
*/
|
|
175
|
+
private judge;
|
|
176
|
+
/** Everything about a code that needs no plan price: its state, its slots, and the plans it fits. */
|
|
177
|
+
private checkCode;
|
|
178
|
+
/** Whether the code can be used now at all: its status and its validity window. */
|
|
179
|
+
private checkState;
|
|
180
|
+
/** Whether the plan is sold at a price in this rhythm, and one the code's minimum allows. */
|
|
181
|
+
private checkPlanPrice;
|
|
182
|
+
private checkFirstTimeCustomer;
|
|
183
|
+
/**
|
|
184
|
+
* Lazy expiry: sets codes whose validUntil has passed to EXPIRED.
|
|
185
|
+
* Called before every find/preview — defense-in-depth alongside the cron.
|
|
186
|
+
*/
|
|
187
|
+
private lazyExpire;
|
|
188
|
+
/**
|
|
189
|
+
* The code with the slots of its expired holds given back, so that what
|
|
190
|
+
* it counts as held is what is held now. One code at a time: this runs on
|
|
191
|
+
* every preview, and a sweep of one code locks one row.
|
|
192
|
+
*/
|
|
193
|
+
private findWithLiveHolds;
|
|
194
|
+
/**
|
|
195
|
+
* Why a code the checks passed a moment ago had no slot to take: it may
|
|
196
|
+
* have been paused, deleted or run past its validity in between, and a
|
|
197
|
+
* refusal that says "exhausted" for any of them misleads the customer and
|
|
198
|
+
* the log a support answer is built on.
|
|
199
|
+
*/
|
|
200
|
+
private whyNoSlot;
|
|
201
|
+
/** Whether the checkout offer holds a slot of this code that has not expired. */
|
|
202
|
+
private holdsLiveSlot;
|
|
203
|
+
/** The slots of a code held right now, its expired holds given back first. */
|
|
204
|
+
private liveHeldCount;
|
|
205
|
+
private requireHolds;
|
|
206
|
+
private ruleContext;
|
|
207
|
+
/**
|
|
208
|
+
* Lowest applicable plan price. With a whitelist it takes the minimum
|
|
209
|
+
* from the whitelist, otherwise across all marketed plans of the catalog
|
|
210
|
+
* (except non-redeemable).
|
|
211
|
+
*/
|
|
212
|
+
private lowestApplicablePlanGross;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export { CODE_MAX_LENGTH as C, type PromoServiceConfig as P, type RedeemInput as R, PromoCodesService as a, CODE_MIN_LENGTH as b, CODE_PATTERN as c, type CheckoutHoldInput as d, type PreviewInput as e, type PreviewInvalid as f, type PreviewOptions as g, type PreviewReason as h, type PreviewResult as i, type PreviewValid as j, type PromoCodeStats as k };
|