@saasicat/nest 1.0.0-rc.20 → 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/dist/.build-stamp +1 -1
- package/dist/_entries.cjs +3570 -2574
- package/dist/admin/index.d.cts +5 -4
- package/dist/admin/index.d.ts +5 -4
- package/dist/admin/index.js +2 -1
- package/dist/{admin-resources.module-aOJ_0rur.d.cts → admin-resources.module-B4XNvdH-.d.cts} +0 -1
- package/dist/{admin-resources.module-BENMc0Wh.d.ts → admin-resources.module-CWqdI9BV.d.ts} +0 -1
- package/dist/{admin-stats.service-cwLMrkGl.d.ts → admin-stats.service-CsLNt19Z.d.ts} +10 -6
- package/dist/{admin-stats.service-BJdzHCtJ.d.cts → admin-stats.service-DrZrU8IJ.d.cts} +10 -6
- package/dist/{aggregation-CtIjlWHT.d.ts → aggregation-ChpZDh0g.d.ts} +20 -4
- package/dist/{aggregation-BHo7a_In.d.cts → aggregation-OpnhwzF5.d.cts} +20 -4
- package/dist/billing/index.d.cts +23 -16
- package/dist/billing/index.d.ts +23 -16
- package/dist/billing/index.js +20 -15
- package/dist/catalog/index.d.cts +17 -8
- package/dist/catalog/index.d.ts +17 -8
- package/dist/catalog/index.js +5 -3
- package/dist/{catalog.module-B5E8tsl7.d.ts → catalog.module-C9O7BxRq.d.ts} +80 -7
- package/dist/{catalog.module-DKVvsPsl.d.cts → catalog.module-T-I5Ricz.d.cts} +80 -7
- package/dist/checkout-offer/index.d.cts +5 -4
- package/dist/checkout-offer/index.d.ts +5 -4
- package/dist/checkout-offer/index.js +15 -14
- package/dist/{checkout-offer.service-BP4CoZle.d.ts → checkout-offer.service-BeHgYikI.d.ts} +51 -8
- package/dist/{checkout-offer.service-Bk5mHEoR.d.cts → checkout-offer.service-CCVxG9b2.d.cts} +51 -8
- package/dist/{chunk-AOQJEYOL.js → chunk-3QVGA6JX.js} +3 -1
- package/dist/chunk-3RLWML2S.js +896 -0
- package/dist/{chunk-DTIYUJH5.js → chunk-4SDSYV4A.js} +16 -6
- package/dist/{chunk-X4PDUYBX.js → chunk-55QEDAMX.js} +106 -302
- package/dist/chunk-6PJE7EWO.js +185 -0
- package/dist/{chunk-CRCNY3U6.js → chunk-7NJKVC5X.js} +54 -14
- package/dist/{chunk-RSAPMUGB.js → chunk-AOS2JPDD.js} +67 -64
- package/dist/{chunk-NCXGXNQK.js → chunk-CQ2ZZMTD.js} +174 -72
- package/dist/{chunk-NEICZJE4.js → chunk-F5X66HF5.js} +1 -1
- package/dist/{chunk-323FB6TD.js → chunk-FEQZCWUL.js} +67 -19
- package/dist/{chunk-FQS5FPH2.js → chunk-G6EZWECL.js} +42 -21
- package/dist/{chunk-XDRJ4FRG.js → chunk-JBCC6C3M.js} +84 -20
- package/dist/chunk-LF6J4YYN.js +301 -0
- package/dist/{chunk-OAUN2FPJ.js → chunk-LLYVYRGJ.js} +50 -21
- package/dist/{chunk-6ISJMNW7.js → chunk-M47BNEY2.js} +125 -108
- package/dist/{chunk-U465NU23.js → chunk-NDLC5GYK.js} +40 -33
- package/dist/{chunk-YFCTBLM5.js → chunk-QPVDCKYS.js} +237 -60
- package/dist/{chunk-IORLRWJZ.js → chunk-R5YCJBHY.js} +2 -2
- package/dist/{chunk-QBF7KLSA.js → chunk-TBBZWZQT.js} +8 -6
- package/dist/chunk-TOEFN7DN.js +295 -0
- package/dist/{chunk-ZUOCCEWB.js → chunk-WHJSYKNC.js} +2 -2
- package/dist/chunk-WOEJ6K7M.js +55 -0
- package/dist/{chunk-WYXLTSUZ.js → chunk-XSSWYPP5.js} +6 -4
- package/dist/contract-line-item-money-B0Z3Ogur.d.cts +43 -0
- package/dist/contract-line-item-money-B0Z3Ogur.d.ts +43 -0
- package/dist/{enforce-quota.interceptor-CLyCNVIm.d.cts → enforce-quota.interceptor-BFyVdWe8.d.cts} +6 -6
- package/dist/{enforce-quota.interceptor-DQ_BN_jV.d.ts → enforce-quota.interceptor-GaBXboGx.d.ts} +6 -6
- package/dist/entitlement/index.d.cts +2 -1
- package/dist/entitlement/index.d.ts +2 -1
- package/dist/entitlement/index.js +6 -7
- package/dist/index.d.cts +27 -26
- package/dist/index.d.ts +27 -26
- package/dist/index.js +47 -33
- package/dist/{issuer-identity.check-B8Wzugbf.d.cts → issuer-identity.check-CBO0vcsq.d.cts} +5 -5
- package/dist/{issuer-identity.check-q-Uha5XY.d.ts → issuer-identity.check-Cz9CEMkZ.d.ts} +5 -5
- package/dist/{module-options-C5EBOxzm.d.ts → module-options-DmQ3G4sZ.d.ts} +17 -7
- package/dist/{module-options-Cr5FkELF.d.cts → module-options-qAOwinHR.d.cts} +17 -7
- package/dist/{payment-callback.service-DPUfjp-F.d.cts → payment-callback.service-CR84Xw1Z.d.cts} +31 -3
- package/dist/{payment-callback.service-DPUfjp-F.d.ts → payment-callback.service-CR84Xw1Z.d.ts} +31 -3
- package/dist/payments/index.d.cts +59 -5
- package/dist/payments/index.d.ts +59 -5
- package/dist/payments/index.js +11 -5
- package/dist/{payments.module-Da26YttL.d.cts → payments.module-BdUq9ot_.d.cts} +5 -2
- package/dist/{payments.module-C6r6dJdn.d.ts → payments.module-CQGv_VG7.d.ts} +5 -2
- 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-versions.service-BTlw-aEX.d.ts → plan-versions.service-C0N-Em2W.d.ts} +2 -48
- package/dist/{plan-versions.service-BM7lKLr1.d.cts → plan-versions.service-DARLh-qI.d.cts} +2 -48
- package/dist/platform/index.d.cts +20 -19
- package/dist/platform/index.d.ts +20 -19
- package/dist/platform/index.js +22 -21
- package/dist/promo/index.d.cts +9 -6
- package/dist/promo/index.d.ts +9 -6
- package/dist/promo/index.js +7 -6
- package/dist/{promo.module-YfF6ADDf.d.cts → promo.module-CKkECmU8.d.cts} +8 -2
- package/dist/{promo.module-KmIpp1Gb.d.ts → promo.module-DuIJdK3U.d.ts} +8 -2
- package/dist/promo.service-BYBu6dy3.d.cts +215 -0
- package/dist/promo.service-CFjfF0Xm.d.ts +215 -0
- package/dist/registration/index.d.cts +29 -4
- package/dist/registration/index.d.ts +29 -4
- package/dist/registration/index.js +12 -5
- package/dist/subscriber/index.d.cts +2 -2
- package/dist/subscriber/index.d.ts +2 -2
- package/dist/subscriber/index.js +5 -4
- package/dist/{subscriber.service-BZsvHw1V.d.cts → subscriber.service-5W6Gi-1B.d.cts} +14 -3
- package/dist/{subscriber.service-BZsvHw1V.d.ts → subscriber.service-5W6Gi-1B.d.ts} +14 -3
- package/dist/subscription-contract/index.d.cts +3 -3
- package/dist/subscription-contract/index.d.ts +3 -3
- package/dist/subscription-contract/index.js +11 -9
- package/dist/{subscription-contract.service-Duv_Ns1W.d.ts → subscription-contract.service-CPZrTM9C.d.ts} +1 -1
- package/dist/{subscription-contract.service-D7Qb_Ns-.d.cts → subscription-contract.service-CfNApLuP.d.cts} +1 -1
- package/dist/{tenant-billing.controller-0HZ_2DQH.d.ts → tenant-billing.controller-BLqbsONe.d.ts} +7 -6
- package/dist/{tenant-billing.controller-CN9DgasV.d.cts → tenant-billing.controller-DKbAqTbM.d.cts} +7 -6
- package/dist/{tenant-billing.module-CPybDf7c.d.cts → tenant-billing.module-ClbjzocC.d.cts} +24 -16
- package/dist/{tenant-billing.module-C1sLBCW-.d.ts → tenant-billing.module-DohgJJ2m.d.ts} +24 -16
- package/dist/testing/index.d.cts +11 -10
- package/dist/testing/index.d.ts +11 -10
- package/dist/testing/index.js +20 -20
- package/package.json +3 -3
- package/dist/chunk-3HT3REXB.js +0 -110
- package/dist/chunk-5BCYJ3QY.js +0 -44
- package/dist/chunk-QSBMFY4I.js +0 -33
- package/dist/chunk-U4X2YRRO.js +0 -214
- package/dist/chunk-XWAZNKAB.js +0 -638
- package/dist/contract-line-item-money-BWWQhRul.d.cts +0 -11
- package/dist/contract-line-item-money-BWWQhRul.d.ts +0 -11
- package/dist/promo.service-CBMm9RU7.d.cts +0 -113
- package/dist/promo.service-CBMm9RU7.d.ts +0 -113
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__name
|
|
3
|
+
} from "./chunk-SHUYVCID.js";
|
|
4
|
+
|
|
5
|
+
// src/promo/math.ts
|
|
6
|
+
function round2(n) {
|
|
7
|
+
return Math.round(n * 100) / 100;
|
|
8
|
+
}
|
|
9
|
+
__name(round2, "round2");
|
|
10
|
+
function isTaxRatePercent(rate) {
|
|
11
|
+
return Number.isFinite(rate) && rate >= 0 && rate <= 100 && !(rate > 0 && rate < 1);
|
|
12
|
+
}
|
|
13
|
+
__name(isTaxRatePercent, "isTaxRatePercent");
|
|
14
|
+
function grossFromNet(net, vatRate) {
|
|
15
|
+
return round2(net * (1 + vatRate / 100));
|
|
16
|
+
}
|
|
17
|
+
__name(grossFromNet, "grossFromNet");
|
|
18
|
+
function netFromGross(gross, vatRate) {
|
|
19
|
+
return round2(gross * 100 / (100 + vatRate));
|
|
20
|
+
}
|
|
21
|
+
__name(netFromGross, "netFromGross");
|
|
22
|
+
function computeIncludedVat(grossAmount, vatRate) {
|
|
23
|
+
return round2(grossAmount * vatRate / (100 + vatRate));
|
|
24
|
+
}
|
|
25
|
+
__name(computeIncludedVat, "computeIncludedVat");
|
|
26
|
+
|
|
27
|
+
// src/subscription-contract/contract-line-item-money.ts
|
|
28
|
+
var MONTHS_PER_YEAR = 12;
|
|
29
|
+
function recordContractLinesMoney(lines, money) {
|
|
30
|
+
const shares = grossSharesOf(lines, money.taxRate);
|
|
31
|
+
return lines.map((line, index) => {
|
|
32
|
+
const priceGross = shares[index];
|
|
33
|
+
return {
|
|
34
|
+
...line,
|
|
35
|
+
priceGross,
|
|
36
|
+
currency: money.currency,
|
|
37
|
+
taxRate: money.taxRate,
|
|
38
|
+
taxAmount: fromCents(toCents(priceGross) - toCents(line.priceNet))
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
__name(recordContractLinesMoney, "recordContractLinesMoney");
|
|
43
|
+
function grossSharesOf(lines, taxRate) {
|
|
44
|
+
const netSoFar = /* @__PURE__ */ new Map();
|
|
45
|
+
return lines.map((line) => {
|
|
46
|
+
const before = netSoFar.get(line.billingCycle) ?? 0;
|
|
47
|
+
const after = before + toCents(line.priceNet);
|
|
48
|
+
netSoFar.set(line.billingCycle, after);
|
|
49
|
+
return fromCents(grossCents(after, taxRate) - grossCents(before, taxRate));
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
__name(grossSharesOf, "grossSharesOf");
|
|
53
|
+
function contractTotalsOf(lines, contractCycle) {
|
|
54
|
+
let subtotalNet = 0;
|
|
55
|
+
let discountNet = 0;
|
|
56
|
+
let totalGross = 0;
|
|
57
|
+
for (const line of lines) {
|
|
58
|
+
const times = timesDueInOnePeriod(contractCycle, line.billingCycle);
|
|
59
|
+
const net = times * toCents(line.priceNet);
|
|
60
|
+
if (line.kind === "discount") discountNet -= net;
|
|
61
|
+
else subtotalNet += net;
|
|
62
|
+
totalGross += times * toCents(line.priceGross);
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
subtotalNet: fromCents(subtotalNet),
|
|
66
|
+
discountNet: fromCents(discountNet),
|
|
67
|
+
totalNet: fromCents(subtotalNet - discountNet),
|
|
68
|
+
totalGross: fromCents(totalGross)
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
__name(contractTotalsOf, "contractTotalsOf");
|
|
72
|
+
function timesDueInOnePeriod(contractCycle, lineCycle) {
|
|
73
|
+
return contractCycle === "yearly" && lineCycle === "monthly" ? MONTHS_PER_YEAR : 1;
|
|
74
|
+
}
|
|
75
|
+
__name(timesDueInOnePeriod, "timesDueInOnePeriod");
|
|
76
|
+
function grossCents(netCents, taxRate) {
|
|
77
|
+
return toCents(grossFromNet(fromCents(netCents), taxRate));
|
|
78
|
+
}
|
|
79
|
+
__name(grossCents, "grossCents");
|
|
80
|
+
function toCents(amount) {
|
|
81
|
+
return Math.round(amount * 100);
|
|
82
|
+
}
|
|
83
|
+
__name(toCents, "toCents");
|
|
84
|
+
function fromCents(cents) {
|
|
85
|
+
return cents === 0 ? 0 : cents / 100;
|
|
86
|
+
}
|
|
87
|
+
__name(fromCents, "fromCents");
|
|
88
|
+
|
|
89
|
+
// src/subscription-contract/contract-refusals.ts
|
|
90
|
+
import { UnprocessableEntityException } from "@nestjs/common";
|
|
91
|
+
import { CONTRACT_ERROR_CODES } from "@saasicat/core";
|
|
92
|
+
function assertTaxRatePercent(field, taxRate) {
|
|
93
|
+
if (isTaxRatePercent(taxRate)) return;
|
|
94
|
+
throw new UnprocessableEntityException({
|
|
95
|
+
code: CONTRACT_ERROR_CODES.SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT,
|
|
96
|
+
message: `A subscription contract states a tax rate of ${taxRate} at ${field}, which is not a percentage: a rate lies from 0 to 100, and a value between 0 and 1 is refused as a fraction.`,
|
|
97
|
+
params: {
|
|
98
|
+
field,
|
|
99
|
+
taxRate
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
__name(assertTaxRatePercent, "assertTaxRatePercent");
|
|
104
|
+
function assertContractWindow(effectiveFrom, effectiveUntil) {
|
|
105
|
+
if (!effectiveUntil || effectiveUntil > effectiveFrom) return;
|
|
106
|
+
throw new UnprocessableEntityException({
|
|
107
|
+
code: CONTRACT_ERROR_CODES.SUBSCRIPTION_CONTRACT_INVALID_WINDOW,
|
|
108
|
+
message: "effectiveUntil must be after effectiveFrom."
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
__name(assertContractWindow, "assertContractWindow");
|
|
112
|
+
function assertOnePlanLine(lineItems) {
|
|
113
|
+
if (lineItems.filter((item) => item.kind === "plan").length === 1) return;
|
|
114
|
+
throw new UnprocessableEntityException({
|
|
115
|
+
code: CONTRACT_ERROR_CODES.SUBSCRIPTION_CONTRACT_PLAN_LINE_ITEM_REQUIRED,
|
|
116
|
+
message: "A subscription contract requires exactly one plan base item."
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
__name(assertOnePlanLine, "assertOnePlanLine");
|
|
120
|
+
var ADDED_UP_TOTALS = [
|
|
121
|
+
"subtotalNet",
|
|
122
|
+
"discountNet",
|
|
123
|
+
"totalNet",
|
|
124
|
+
"totalGross"
|
|
125
|
+
];
|
|
126
|
+
function assertLinesAddUp(lineItems, priceSnapshot) {
|
|
127
|
+
const lines = contractTotalsOf(lineItems, priceSnapshot.billingCycle);
|
|
128
|
+
const field = ADDED_UP_TOTALS.find((total) => Math.round(lines[total] * 100) !== Math.round(priceSnapshot[total] * 100));
|
|
129
|
+
if (!field) return;
|
|
130
|
+
throw new UnprocessableEntityException({
|
|
131
|
+
code: CONTRACT_ERROR_CODES.SUBSCRIPTION_CONTRACT_LINES_DO_NOT_ADD_UP,
|
|
132
|
+
message: `The line items of a subscription contract add up to ${lines[field]} for priceSnapshot.${field}, but the contract states ${priceSnapshot[field]}.`,
|
|
133
|
+
params: {
|
|
134
|
+
field: `priceSnapshot.${field}`,
|
|
135
|
+
lines: lines[field],
|
|
136
|
+
stated: priceSnapshot[field]
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
__name(assertLinesAddUp, "assertLinesAddUp");
|
|
141
|
+
function assertNoNegativeDiscount(data) {
|
|
142
|
+
const negative = (data.priceSnapshot.discountNet < 0 ? {
|
|
143
|
+
field: "priceSnapshot.discountNet",
|
|
144
|
+
amount: data.priceSnapshot.discountNet
|
|
145
|
+
} : null) ?? negativeResolvedAmount("promotionSnapshots", data.promotionSnapshots) ?? negativeResolvedAmount("promoCodeSnapshots", data.promoCodeSnapshots);
|
|
146
|
+
if (!negative) return;
|
|
147
|
+
throw new UnprocessableEntityException({
|
|
148
|
+
code: CONTRACT_ERROR_CODES.SUBSCRIPTION_CONTRACT_DISCOUNT_NEGATIVE,
|
|
149
|
+
message: `A subscription contract states a discount of ${negative.amount} at ${negative.field}; a discount takes money off and is never negative.`,
|
|
150
|
+
params: {
|
|
151
|
+
field: negative.field,
|
|
152
|
+
amount: negative.amount
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
__name(assertNoNegativeDiscount, "assertNoNegativeDiscount");
|
|
157
|
+
function negativeResolvedAmount(list, snapshots) {
|
|
158
|
+
for (const [index, snapshot] of (snapshots ?? []).entries()) {
|
|
159
|
+
const amount = snapshot?.resolvedAmountNet;
|
|
160
|
+
if (typeof amount === "number" && amount < 0) {
|
|
161
|
+
return {
|
|
162
|
+
field: `${list}[${index}].resolvedAmountNet`,
|
|
163
|
+
amount
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
__name(negativeResolvedAmount, "negativeResolvedAmount");
|
|
170
|
+
|
|
171
|
+
export {
|
|
172
|
+
round2,
|
|
173
|
+
isTaxRatePercent,
|
|
174
|
+
grossFromNet,
|
|
175
|
+
netFromGross,
|
|
176
|
+
computeIncludedVat,
|
|
177
|
+
recordContractLinesMoney,
|
|
178
|
+
grossSharesOf,
|
|
179
|
+
contractTotalsOf,
|
|
180
|
+
assertTaxRatePercent,
|
|
181
|
+
assertContractWindow,
|
|
182
|
+
assertOnePlanLine,
|
|
183
|
+
assertLinesAddUp,
|
|
184
|
+
assertNoNegativeDiscount
|
|
185
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-
|
|
2
|
+
PLAN_CATALOG_SETTINGS_TOKEN
|
|
3
|
+
} from "./chunk-FEQZCWUL.js";
|
|
4
4
|
import {
|
|
5
5
|
codedError
|
|
6
6
|
} from "./chunk-J7NJRK3K.js";
|
|
@@ -78,24 +78,24 @@ var PaymentGatewayRegistry = class {
|
|
|
78
78
|
static {
|
|
79
79
|
__name(this, "PaymentGatewayRegistry");
|
|
80
80
|
}
|
|
81
|
-
|
|
81
|
+
settings;
|
|
82
82
|
accounts = /* @__PURE__ */ new Map();
|
|
83
83
|
takesNew;
|
|
84
|
-
constructor(
|
|
85
|
-
this.
|
|
86
|
-
const problems = paymentAccountProblems(
|
|
84
|
+
constructor(settings, gateways) {
|
|
85
|
+
this.settings = settings;
|
|
86
|
+
const problems = paymentAccountProblems(settings.payments, gateways);
|
|
87
87
|
if (problems.length > 0) {
|
|
88
88
|
throw new Error(`Payments cannot start:
|
|
89
89
|
- ${problems.join("\n- ")}`);
|
|
90
90
|
}
|
|
91
|
-
for (const [name, account] of Object.entries(
|
|
91
|
+
for (const [name, account] of Object.entries(settings.payments.accounts)) {
|
|
92
92
|
this.accounts.set(name, {
|
|
93
93
|
name,
|
|
94
94
|
provider: account.provider,
|
|
95
95
|
gateway: gateways[name]
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
|
-
this.takesNew =
|
|
98
|
+
this.takesNew = settings.payments.newPaymentMethods;
|
|
99
99
|
}
|
|
100
100
|
/** The account a callback names, or `null` when none is configured under it. */
|
|
101
101
|
account(name) {
|
|
@@ -109,7 +109,7 @@ var PaymentGatewayRegistry = class {
|
|
|
109
109
|
}
|
|
110
110
|
/** The origins a gateway's form may send a person back to. */
|
|
111
111
|
returnUrlOrigins() {
|
|
112
|
-
return this.
|
|
112
|
+
return this.settings.payments.returnUrlOrigins;
|
|
113
113
|
}
|
|
114
114
|
/** The account a new payment method is taken at, or `null` when none takes new ones. */
|
|
115
115
|
forNewPaymentMethods() {
|
|
@@ -117,24 +117,24 @@ var PaymentGatewayRegistry = class {
|
|
|
117
117
|
const account = this.accounts.get(this.takesNew);
|
|
118
118
|
return {
|
|
119
119
|
...account,
|
|
120
|
-
methods: this.
|
|
120
|
+
methods: this.settings.payments.accounts[this.takesNew].methods
|
|
121
121
|
};
|
|
122
122
|
}
|
|
123
123
|
};
|
|
124
124
|
PaymentGatewayRegistry = _ts_decorate([
|
|
125
125
|
Injectable(),
|
|
126
|
-
_ts_param(0, Inject(
|
|
126
|
+
_ts_param(0, Inject(PLAN_CATALOG_SETTINGS_TOKEN)),
|
|
127
127
|
_ts_param(1, Inject(PAYMENT_GATEWAYS_TOKEN)),
|
|
128
128
|
_ts_metadata("design:type", Function),
|
|
129
129
|
_ts_metadata("design:paramtypes", [
|
|
130
|
-
typeof
|
|
130
|
+
typeof PlanCatalogSettings === "undefined" ? Object : PlanCatalogSettings,
|
|
131
131
|
typeof Readonly === "undefined" ? Object : Readonly
|
|
132
132
|
])
|
|
133
133
|
], PaymentGatewayRegistry);
|
|
134
134
|
|
|
135
135
|
// src/payments/payment-callback.service.ts
|
|
136
136
|
import { BadRequestException, Inject as Inject2, Injectable as Injectable2, Logger, NotFoundException } from "@nestjs/common";
|
|
137
|
-
import { PAYMENT_ERROR_CODES, isPaymentCallbackRejectedError } from "@saasicat/core";
|
|
137
|
+
import { PAYMENT_ERROR_CODES, isForeignPaymentMethodReferenceError, isPaymentCallbackRejectedError } from "@saasicat/core";
|
|
138
138
|
function _ts_decorate2(decorators, target, key, desc) {
|
|
139
139
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
140
140
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -207,7 +207,7 @@ var PaymentCallbackService = class _PaymentCallbackService {
|
|
|
207
207
|
afterCommit: /* @__PURE__ */ __name((step) => afterCommit.push(step), "afterCommit")
|
|
208
208
|
};
|
|
209
209
|
if (event.kind === "payment-method-confirmed") {
|
|
210
|
-
const effect = await
|
|
210
|
+
const effect = await this.confirm(handler, event, context);
|
|
211
211
|
if (effect === "nothing-to-do") {
|
|
212
212
|
await this.log.releaseSession(account, event.eventId, tx);
|
|
213
213
|
}
|
|
@@ -230,6 +230,42 @@ var PaymentCallbackService = class _PaymentCallbackService {
|
|
|
230
230
|
}
|
|
231
231
|
return "handled";
|
|
232
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Hands the confirmation to its handler, and names the one refusal that
|
|
235
|
+
* will never come out differently.
|
|
236
|
+
*
|
|
237
|
+
* A reference belongs to one subscriber for good (`SC-SEC-014`), so a
|
|
238
|
+
* confirmation refused for that reason is refused on every delivery. It
|
|
239
|
+
* still leaves by throwing, which rolls this transaction back: by the time
|
|
240
|
+
* it is raised a handler has written something that must not stand on its
|
|
241
|
+
* own — the setup marked complete on the tenant's path, the whole
|
|
242
|
+
* activation on the sign-up's. Answering the gateway anything else would
|
|
243
|
+
* commit one of those without the payment method that justifies it.
|
|
244
|
+
*
|
|
245
|
+
* The gateway therefore retries until it gives up, and the claim rolls back
|
|
246
|
+
* with everything else, so nothing durable is left to say why. This line is
|
|
247
|
+
* what says it.
|
|
248
|
+
*
|
|
249
|
+
* There is no terminal state for it, and that is worth knowing before an
|
|
250
|
+
* operator waits for one: the webhook route has no filter for this, so every
|
|
251
|
+
* redelivery is answered `500`, for as long as the provider keeps trying. A
|
|
252
|
+
* provider that disables an endpoint after sustained failures would take the
|
|
253
|
+
* account's other callbacks with it. Giving the event an end would mean
|
|
254
|
+
* keeping the claim while undoing what the handler wrote, which needs a
|
|
255
|
+
* savepoint the port does not have — so it is named here rather than
|
|
256
|
+
* implied. It names the account, the reference and the subject the
|
|
257
|
+
* event was about — never the subscriber the reference belongs to, which is
|
|
258
|
+
* on the other side of the boundary being refused.
|
|
259
|
+
*/
|
|
260
|
+
async confirm(handler, event, context) {
|
|
261
|
+
try {
|
|
262
|
+
return await handler.confirmed(event, context);
|
|
263
|
+
} catch (error) {
|
|
264
|
+
if (!isForeignPaymentMethodReferenceError(error)) throw error;
|
|
265
|
+
this.logger.error(`Payment event ${event.eventId} at '${context.gatewayAccount}' confirms payment method '${error.paymentMethodRef}' for ${subjectOf(event)}, and that reference belongs to another subscriber; nothing was recorded, and every delivery of this event is refused the same way.`);
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
233
269
|
async read(readCallback, callback, account) {
|
|
234
270
|
try {
|
|
235
271
|
return await readCallback(callback);
|
|
@@ -256,6 +292,10 @@ PaymentCallbackService = _ts_decorate2([
|
|
|
256
292
|
typeof TransactionRunner === "undefined" ? Object : TransactionRunner
|
|
257
293
|
])
|
|
258
294
|
], PaymentCallbackService);
|
|
295
|
+
function subjectOf(event) {
|
|
296
|
+
return event.subject.kind === "registration" ? `sign-up ${event.subject.pendingRegistrationId}` : `subscriber ${event.subject.subscriberId}`;
|
|
297
|
+
}
|
|
298
|
+
__name(subjectOf, "subjectOf");
|
|
259
299
|
function summaryOf(event) {
|
|
260
300
|
const subject = event.subject.kind === "registration" ? {
|
|
261
301
|
registration: event.subject.pendingRegistrationId
|