@openlup/platform 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +222 -99
- package/README.md +59 -589
- package/dist/fulfillment/adapterConformance.js +1 -1
- package/dist/fulfillment/bindings.js +1 -1
- package/dist/fulfillment/contracts/conformance.d.ts +3 -1
- package/dist/fulfillment/contracts/dispatch.d.ts +41 -3
- package/dist/fulfillment/contracts/index.d.ts +3 -3
- package/dist/fulfillment/contracts/observation.d.ts +21 -1
- package/dist/fulfillment/contracts/readback.d.ts +21 -0
- package/dist/fulfillment/contracts/store.d.ts +3 -0
- package/dist/fulfillment/contracts/sync.d.ts +58 -2
- package/dist/fulfillment/dispatchProjection.d.ts +20 -1
- package/dist/fulfillment/dispatchProjection.js +95 -6
- package/dist/fulfillment/index.d.ts +3 -2
- package/dist/fulfillment/index.js +2 -2
- package/dist/fulfillment/migrations.d.ts +5 -0
- package/dist/fulfillment/migrations.js +1 -0
- package/dist/fulfillment/observationProjection.d.ts +24 -3
- package/dist/fulfillment/observationProjection.js +65 -12
- package/dist/fulfillment/omnipack/client.d.ts +3 -1
- package/dist/fulfillment/omnipack/client.js +17 -7
- package/dist/fulfillment/omnipack/index.d.ts +4 -1
- package/dist/fulfillment/omnipack/index.js +104 -45
- package/dist/fulfillment/omnipack/mappers.d.ts +7 -1
- package/dist/fulfillment/omnipack/mappers.js +24 -2
- package/dist/fulfillment/omnipack/observation.d.ts +11 -4
- package/dist/fulfillment/omnipack/observation.js +44 -29
- package/dist/fulfillment/omnipack/reconciliation.d.ts +15 -0
- package/dist/fulfillment/omnipack/reconciliation.js +55 -0
- package/dist/fulfillment/postgresDispatchStore.js +1 -1
- package/dist/fulfillment/postgresShipmentSpine.d.ts +8 -1
- package/dist/fulfillment/postgresShipmentSpine.js +17 -3
- package/dist/fulfillment/postgresStockSyncPort.js +6 -3
- package/dist/fulfillment/providerDispatchAcceptance.d.ts +1 -0
- package/dist/fulfillment/providerDispatchAcceptance.js +8 -2
- package/dist/fulfillment/providerDispatchWorker.d.ts +13 -0
- package/dist/fulfillment/providerDispatchWorker.js +47 -20
- package/dist/fulfillment/providerReconciliationWorker.d.ts +27 -19
- package/dist/fulfillment/providerReconciliationWorker.js +75 -19
- package/dist/fulfillment/providerStockSyncWorker.d.ts +5 -1
- package/dist/fulfillment/providerStockSyncWorker.js +229 -120
- package/dist/fulfillment/providerTrackingReferences.d.ts +3 -2
- package/dist/fulfillment/verifiedIngress.d.ts +38 -11
- package/dist/fulfillment/verifiedIngress.js +36 -8
- package/dist/invoicing/capabilities.d.ts +11 -0
- package/dist/invoicing/capabilities.js +10 -0
- package/dist/invoicing/contracts/capabilities.d.ts +40 -0
- package/dist/invoicing/contracts/capabilities.js +1 -0
- package/dist/invoicing/contracts/companyLookup.d.ts +49 -0
- package/dist/invoicing/contracts/companyLookup.js +1 -0
- package/dist/invoicing/contracts/delivery.d.ts +74 -0
- package/dist/invoicing/contracts/delivery.js +1 -0
- package/dist/invoicing/contracts/facts.d.ts +100 -0
- package/dist/invoicing/contracts/facts.js +1 -0
- package/dist/invoicing/contracts/host.d.ts +246 -0
- package/dist/invoicing/contracts/host.js +1 -0
- package/dist/invoicing/contracts/index.d.ts +16 -0
- package/dist/invoicing/contracts/index.js +1 -0
- package/dist/invoicing/contracts/provider.d.ts +126 -0
- package/dist/invoicing/contracts/provider.js +1 -0
- package/dist/invoicing/contracts/settlement.d.ts +66 -0
- package/dist/invoicing/contracts/settlement.js +1 -0
- package/dist/invoicing/contracts/store.d.ts +267 -0
- package/dist/invoicing/contracts/store.js +1 -0
- package/dist/invoicing/index.d.ts +1 -0
- package/dist/invoicing/index.js +1 -0
- package/dist/subscriptions/automaticRenewal.d.ts +44 -0
- package/dist/subscriptions/automaticRenewal.js +81 -0
- package/dist/subscriptions/automaticRenewalExecution.d.ts +21 -0
- package/dist/subscriptions/automaticRenewalExecution.js +44 -0
- package/dist/subscriptions/contracts/commands.d.ts +115 -0
- package/dist/subscriptions/contracts/commands.js +1 -0
- package/dist/subscriptions/contracts/dunning.d.ts +133 -0
- package/dist/subscriptions/contracts/dunning.js +1 -0
- package/dist/subscriptions/contracts/index.d.ts +6 -0
- package/dist/subscriptions/contracts/index.js +1 -0
- package/dist/subscriptions/contracts/messages.d.ts +43 -0
- package/dist/subscriptions/contracts/messages.js +1 -0
- package/dist/subscriptions/contracts/renewal.d.ts +533 -0
- package/dist/subscriptions/contracts/renewal.js +1 -0
- package/dist/subscriptions/contracts/stockWait.d.ts +73 -0
- package/dist/subscriptions/contracts/stockWait.js +1 -0
- package/dist/subscriptions/index.d.ts +39 -0
- package/dist/subscriptions/index.js +17 -0
- package/dist/subscriptions/paymentOutcomes.d.ts +8 -0
- package/dist/subscriptions/paymentOutcomes.js +27 -0
- package/dist/subscriptions/postgresRenewalStore.d.ts +20 -0
- package/dist/subscriptions/postgresRenewalStore.js +61 -0
- package/dist/subscriptions/postgresRetryStore.d.ts +78 -0
- package/dist/subscriptions/postgresRetryStore.js +142 -0
- package/dist/subscriptions/renewalCycleFailureMechanism.d.ts +34 -0
- package/dist/subscriptions/renewalCycleFailureMechanism.js +79 -0
- package/dist/subscriptions/renewalCycleMechanism.d.ts +46 -0
- package/dist/subscriptions/renewalCycleMechanism.js +157 -0
- package/dist/subscriptions/renewalCycleRefusalMechanism.d.ts +41 -0
- package/dist/subscriptions/renewalCycleRefusalMechanism.js +160 -0
- package/dist/subscriptions/renewalIdentity.d.ts +3 -0
- package/dist/subscriptions/renewalIdentity.js +4 -0
- package/package.json +22 -2
- package/release-gates.json +56 -3
- package/sql/20261022160000_subscription_retry_lifecycle.sql +328 -0
- package/sql/20261022165000_fulfillment_stock_evidence_replay_reopen.sql +30 -0
- package/src/fulfillment/adapterConformance.ts +1 -1
- package/src/fulfillment/bindings.ts +1 -1
- package/src/fulfillment/contracts/conformance.ts +3 -1
- package/src/fulfillment/contracts/dispatch.ts +20 -5
- package/src/fulfillment/contracts/index.ts +3 -3
- package/src/fulfillment/contracts/observation.ts +11 -3
- package/src/fulfillment/contracts/readback.ts +13 -0
- package/src/fulfillment/contracts/store.ts +1 -0
- package/src/fulfillment/contracts/sync.ts +13 -2
- package/src/fulfillment/dispatchProjection.ts +92 -7
- package/src/fulfillment/index.ts +3 -2
- package/src/fulfillment/migrations.ts +1 -0
- package/src/fulfillment/observationProjection.ts +64 -13
- package/src/fulfillment/omnipack/client.ts +19 -7
- package/src/fulfillment/omnipack/index.ts +96 -39
- package/src/fulfillment/omnipack/mappers.ts +23 -2
- package/src/fulfillment/omnipack/observation.ts +45 -27
- package/src/fulfillment/omnipack/reconciliation.ts +56 -0
- package/src/fulfillment/postgresDispatchStore.ts +1 -1
- package/src/fulfillment/postgresShipmentSpine.ts +21 -4
- package/src/fulfillment/postgresStockSyncPort.ts +6 -3
- package/src/fulfillment/providerDispatchAcceptance.ts +9 -3
- package/src/fulfillment/providerDispatchWorker.ts +48 -20
- package/src/fulfillment/providerReconciliationWorker.ts +81 -25
- package/src/fulfillment/providerStockSyncWorker.ts +222 -126
- package/src/fulfillment/providerTrackingReferences.ts +3 -2
- package/src/fulfillment/verifiedIngress.ts +44 -11
- package/src/invoicing/capabilities.ts +9 -0
- package/src/invoicing/contracts/capabilities.ts +15 -0
- package/src/invoicing/contracts/companyLookup.ts +13 -0
- package/src/invoicing/contracts/delivery.ts +24 -0
- package/src/invoicing/contracts/facts.ts +60 -0
- package/src/invoicing/contracts/host.ts +85 -0
- package/src/invoicing/contracts/index.ts +16 -0
- package/src/invoicing/contracts/provider.ts +78 -0
- package/src/invoicing/contracts/settlement.ts +39 -0
- package/src/invoicing/contracts/store.ts +98 -0
- package/src/invoicing/index.ts +1 -0
- package/src/subscriptions/automaticRenewal.ts +87 -0
- package/src/subscriptions/automaticRenewalExecution.ts +39 -0
- package/src/subscriptions/contracts/commands.ts +46 -0
- package/src/subscriptions/contracts/dunning.ts +48 -0
- package/src/subscriptions/contracts/index.ts +6 -0
- package/src/subscriptions/contracts/messages.ts +18 -0
- package/src/subscriptions/contracts/renewal.ts +325 -0
- package/src/subscriptions/contracts/stockWait.ts +34 -0
- package/src/subscriptions/index.ts +24 -0
- package/src/subscriptions/paymentOutcomes.ts +23 -0
- package/src/subscriptions/postgresRenewalStore.ts +76 -0
- package/src/subscriptions/postgresRetryStore.ts +174 -0
- package/src/subscriptions/renewalCycleFailureMechanism.ts +119 -0
- package/src/subscriptions/renewalCycleMechanism.ts +225 -0
- package/src/subscriptions/renewalCycleRefusalMechanism.ts +215 -0
- package/src/subscriptions/renewalIdentity.ts +5 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import type { InvoiceHostPorts } from "./host.js";
|
|
2
|
+
import type { InvoiceFacts } from "./facts.js";
|
|
3
|
+
import type { InvoiceIdentity, InvoiceDocumentIdentity, InvoiceProof, InvoiceUnsupported, InvoiceWriteResult, InvoiceReadbackResult } from "./provider.js";
|
|
4
|
+
import type { InvoiceClearanceObservation, InvoiceArtifactResult, InvoiceDeliveryPort } from "./delivery.js";
|
|
5
|
+
type InvoiceDutyStatus = "owed" | "blocked" | "ready" | "processing" | "unknown" | "issued" | "voided" | "unsupported" | "faulted";
|
|
6
|
+
/** Native duty/lease fields map to the pinned invoicing_* fixtures. Managed hosts
|
|
7
|
+
* translate their outboxId/attemptCount fences; never claim transaction equality.
|
|
8
|
+
*/
|
|
9
|
+
export interface InvoiceClaim {
|
|
10
|
+
readonly dutyId: string;
|
|
11
|
+
readonly documentId: string;
|
|
12
|
+
readonly requestId: string;
|
|
13
|
+
readonly token: string;
|
|
14
|
+
readonly generation: number;
|
|
15
|
+
readonly leaseUntil: string;
|
|
16
|
+
readonly identity: InvoiceIdentity;
|
|
17
|
+
readonly facts: InvoiceFacts;
|
|
18
|
+
readonly action: "create" | "readback";
|
|
19
|
+
readonly correction: {
|
|
20
|
+
readonly before: InvoiceFacts;
|
|
21
|
+
readonly reason: string;
|
|
22
|
+
readonly after: {
|
|
23
|
+
readonly totalNetMinor: 0;
|
|
24
|
+
readonly totalTaxMinor: 0;
|
|
25
|
+
readonly totalGrossMinor: 0;
|
|
26
|
+
};
|
|
27
|
+
} | null;
|
|
28
|
+
}
|
|
29
|
+
/** Exact camel-case shape returned by invoicing_duty_readback. This is not
|
|
30
|
+
* payment truth's accountingRequested; empty duties never assert invoice absence.
|
|
31
|
+
* @consumer Operator owed-duty readback.
|
|
32
|
+
*/
|
|
33
|
+
export interface InvoiceDutyReadback {
|
|
34
|
+
readonly orderId: string;
|
|
35
|
+
readonly captureFaults: readonly {
|
|
36
|
+
readonly sourceId: string;
|
|
37
|
+
readonly state: "retryable" | "faulted" | "resolved";
|
|
38
|
+
readonly attempts: number;
|
|
39
|
+
readonly availableAt: string;
|
|
40
|
+
}[];
|
|
41
|
+
readonly duties: readonly {
|
|
42
|
+
readonly dutyId: string;
|
|
43
|
+
readonly parentDutyId: string | null;
|
|
44
|
+
readonly documentId: string | null;
|
|
45
|
+
readonly state: "owed" | "blocked" | "claimed" | "unknown" | "issued" | "voided" | "unsupported" | "faulted";
|
|
46
|
+
readonly attempts: number;
|
|
47
|
+
readonly factAttempts: number;
|
|
48
|
+
readonly diagnosticCode: "faulted" | "missing_facts" | "legacy_facts_unproved" | "retryable" | "blocked" | "unsupported" | "unknown" | "absent" | null;
|
|
49
|
+
readonly createdAt: string;
|
|
50
|
+
readonly availableAt: string;
|
|
51
|
+
readonly leaseUntil: string | null;
|
|
52
|
+
}[];
|
|
53
|
+
}
|
|
54
|
+
/** Every operation is one local transaction; commit before external I/O. Native
|
|
55
|
+
* success atomically saves late identity and compensation; stale catch writes nothing.
|
|
56
|
+
* Managed and native stores are bound to the selected provider at construction.
|
|
57
|
+
* No implementation, executor or migration is installed by these declarations.
|
|
58
|
+
*/
|
|
59
|
+
export interface InvoiceStorePort {
|
|
60
|
+
/** Never call inside a renewal money, dunning or dispatch transaction without
|
|
61
|
+
* isolation: capture failure must not propagate; the sweep recovers.
|
|
62
|
+
*/
|
|
63
|
+
captureDuty(input: {
|
|
64
|
+
readonly requestKey: string;
|
|
65
|
+
readonly sourceKind: "paid" | "handoff";
|
|
66
|
+
readonly sourceId: string;
|
|
67
|
+
readonly orderId: string;
|
|
68
|
+
readonly occurredAt: string;
|
|
69
|
+
readonly evidence: {
|
|
70
|
+
readonly settlementIntentId: string;
|
|
71
|
+
readonly amountMinor: number;
|
|
72
|
+
readonly currency: string;
|
|
73
|
+
} | {
|
|
74
|
+
readonly shipmentId: string;
|
|
75
|
+
readonly handedOverAt: string;
|
|
76
|
+
};
|
|
77
|
+
}): Promise<{
|
|
78
|
+
readonly dutyId: string;
|
|
79
|
+
readonly status: InvoiceDutyStatus;
|
|
80
|
+
} | InvoiceUnsupported>;
|
|
81
|
+
installFacts(input: {
|
|
82
|
+
readonly dutyId: string;
|
|
83
|
+
readonly facts: InvoiceFacts;
|
|
84
|
+
}): Promise<{
|
|
85
|
+
readonly dutyId: string;
|
|
86
|
+
readonly status: InvoiceDutyStatus;
|
|
87
|
+
readonly replayed?: boolean;
|
|
88
|
+
} | InvoiceUnsupported>;
|
|
89
|
+
/** Pinned split facts lease: sweep and prepare commit separately; finishing
|
|
90
|
+
* requires the same token and authoritative document/fact match. No host I/O
|
|
91
|
+
* under the duty lock, and unsupported never silently drops an owed duty.
|
|
92
|
+
*/
|
|
93
|
+
sweep(limit: number): Promise<{
|
|
94
|
+
readonly collected: number;
|
|
95
|
+
readonly claims: readonly {
|
|
96
|
+
readonly dutyId: string;
|
|
97
|
+
readonly token: string;
|
|
98
|
+
}[];
|
|
99
|
+
} | InvoiceUnsupported>;
|
|
100
|
+
prepareFacts(input: {
|
|
101
|
+
readonly dutyId: string;
|
|
102
|
+
readonly token: string;
|
|
103
|
+
}): Promise<{
|
|
104
|
+
readonly status: "owed" | "ready" | "issued" | "voided" | "unsupported" | "stale";
|
|
105
|
+
readonly documentId?: string;
|
|
106
|
+
readonly requestId?: string;
|
|
107
|
+
readonly code?: "retryable" | "legacy_facts_unproved";
|
|
108
|
+
} | InvoiceUnsupported>;
|
|
109
|
+
finishFacts(input: {
|
|
110
|
+
readonly dutyId: string;
|
|
111
|
+
readonly token: string;
|
|
112
|
+
readonly result: Omit<Exclude<Awaited<ReturnType<InvoiceStorePort["prepareFacts"]>>, InvoiceUnsupported>, "status"> & {
|
|
113
|
+
readonly status: "owed" | "ready" | "issued" | "voided" | "unsupported";
|
|
114
|
+
};
|
|
115
|
+
}): Promise<{
|
|
116
|
+
readonly dutyId: string;
|
|
117
|
+
readonly status: "owed" | "ready" | "issued" | "voided" | "unsupported" | "faulted" | "stale";
|
|
118
|
+
} | InvoiceUnsupported>;
|
|
119
|
+
readDuties(orderId: string): Promise<InvoiceDutyReadback | InvoiceUnsupported>;
|
|
120
|
+
/** Filter order before locking/limit; never lease unrelated orders then filter.
|
|
121
|
+
* P8 reads the injected host seller before calling the first-claim SQL wrapper;
|
|
122
|
+
* its revisionRef supplies p_revision, without requiring a duty from the caller.
|
|
123
|
+
*/
|
|
124
|
+
claim(input: {
|
|
125
|
+
readonly limit: number;
|
|
126
|
+
readonly visibilitySeconds: number;
|
|
127
|
+
readonly providerRef: string;
|
|
128
|
+
readonly orderId?: string | null;
|
|
129
|
+
}): Promise<readonly InvoiceClaim[] | InvoiceUnsupported>;
|
|
130
|
+
preflight(input: {
|
|
131
|
+
readonly claim: InvoiceClaim;
|
|
132
|
+
readonly amountMinor: number | null;
|
|
133
|
+
readonly currency: string | null;
|
|
134
|
+
} & ({
|
|
135
|
+
readonly status: "succeeded" | "failed" | "pending" | "unknown" | "unavailable";
|
|
136
|
+
readonly proof: InvoiceProof | null;
|
|
137
|
+
} | {
|
|
138
|
+
readonly status: "absent";
|
|
139
|
+
readonly evidence: Extract<Awaited<ReturnType<InvoiceHostPorts["money"]["read"]>>, {
|
|
140
|
+
kind: "absent";
|
|
141
|
+
}>["evidence"];
|
|
142
|
+
})): Promise<{
|
|
143
|
+
readonly kind: "ready" | "blocked" | "mismatch" | "unavailable" | "stale";
|
|
144
|
+
} | InvoiceUnsupported>;
|
|
145
|
+
finish(input: {
|
|
146
|
+
readonly claim: InvoiceClaim;
|
|
147
|
+
readonly result: Extract<InvoiceWriteResult, {
|
|
148
|
+
kind: "issued";
|
|
149
|
+
}> | Extract<InvoiceReadbackResult, {
|
|
150
|
+
kind: "found";
|
|
151
|
+
}>;
|
|
152
|
+
}): Promise<{
|
|
153
|
+
readonly kind: "issued";
|
|
154
|
+
readonly replayed: boolean;
|
|
155
|
+
} | {
|
|
156
|
+
readonly kind: "stale";
|
|
157
|
+
} | InvoiceUnsupported>;
|
|
158
|
+
/** Retryable requires a definite refusal (effectMayHaveSucceeded:false) or a
|
|
159
|
+
* pre-I/O failure and phase before_send. Every other failure maps to unknown.
|
|
160
|
+
* Native result kinds other than stale/voided map to recorded.
|
|
161
|
+
*/
|
|
162
|
+
fail(input: {
|
|
163
|
+
readonly claim: InvoiceClaim;
|
|
164
|
+
readonly retrySeconds: number;
|
|
165
|
+
} & ({
|
|
166
|
+
readonly kind: "absent";
|
|
167
|
+
readonly evidence: Extract<InvoiceReadbackResult, {
|
|
168
|
+
kind: "absent";
|
|
169
|
+
}>;
|
|
170
|
+
} | {
|
|
171
|
+
readonly kind: "retryable";
|
|
172
|
+
readonly phase: "before_send";
|
|
173
|
+
readonly proof: InvoiceProof | null;
|
|
174
|
+
} | {
|
|
175
|
+
readonly kind: "blocked" | "unsupported" | "unknown";
|
|
176
|
+
readonly proof: InvoiceProof | null;
|
|
177
|
+
})): Promise<{
|
|
178
|
+
readonly kind: "recorded" | "voided" | "stale";
|
|
179
|
+
} | InvoiceUnsupported>;
|
|
180
|
+
/** Native post-write unsupported maps to unsupported_recorded, retaining duty/replay.
|
|
181
|
+
* Missing means no M09 duty, never invoice absence: retain/retry the reversal
|
|
182
|
+
* intent and route legacy documents to the incumbent path.
|
|
183
|
+
*/
|
|
184
|
+
requestReversal(input: {
|
|
185
|
+
readonly requestKey: string;
|
|
186
|
+
readonly orderId: string;
|
|
187
|
+
readonly reason: "order_canceled" | "order_refunded";
|
|
188
|
+
readonly proof: InvoiceProof;
|
|
189
|
+
}): Promise<{
|
|
190
|
+
readonly kind: "missing" | "voided" | "pending_readback" | "correction_requested";
|
|
191
|
+
readonly dutyId?: string;
|
|
192
|
+
readonly replayed?: boolean;
|
|
193
|
+
} | {
|
|
194
|
+
readonly kind: "unsupported_recorded";
|
|
195
|
+
readonly dutyId?: string;
|
|
196
|
+
readonly replayed?: boolean;
|
|
197
|
+
} | InvoiceUnsupported>;
|
|
198
|
+
/** Existing local-only request remains distinct from fiscal issuance. */
|
|
199
|
+
requestLocalDocument(input: {
|
|
200
|
+
readonly requestKey: string;
|
|
201
|
+
readonly orderId: string;
|
|
202
|
+
readonly providerRef: string;
|
|
203
|
+
}): Promise<{
|
|
204
|
+
readonly kind: "requested";
|
|
205
|
+
readonly documentId: string;
|
|
206
|
+
readonly invoiceRef: string;
|
|
207
|
+
readonly status: string;
|
|
208
|
+
readonly replayed: boolean;
|
|
209
|
+
} | InvoiceUnsupported>;
|
|
210
|
+
listClearanceTargets(limit: number): Promise<readonly {
|
|
211
|
+
readonly documentId: string;
|
|
212
|
+
readonly identity: InvoiceDocumentIdentity;
|
|
213
|
+
readonly providerDocumentRef: string;
|
|
214
|
+
readonly providerDocumentNumber: string | null;
|
|
215
|
+
readonly state: InvoiceClearanceObservation["state"];
|
|
216
|
+
}[] | InvoiceUnsupported>;
|
|
217
|
+
/** Native maps clearance state unsupported to observation kind unsupported;
|
|
218
|
+
* provider_mismatch returns InvoiceUnsupported before recording anything.
|
|
219
|
+
*/
|
|
220
|
+
recordObservation(input: {
|
|
221
|
+
readonly requestKey: string;
|
|
222
|
+
readonly documentId: string;
|
|
223
|
+
readonly eventRef: string;
|
|
224
|
+
readonly eventKind: "clearance" | "artifacts" | "unsupported" | "provider_mismatch";
|
|
225
|
+
readonly identity: InvoiceDocumentIdentity;
|
|
226
|
+
readonly providerDocumentRef: string;
|
|
227
|
+
readonly providerDocumentNumber: string | null;
|
|
228
|
+
readonly clearance: InvoiceClearanceObservation | null;
|
|
229
|
+
readonly artifacts: readonly Extract<InvoiceArtifactResult, {
|
|
230
|
+
kind: "found";
|
|
231
|
+
}>["artifact"][];
|
|
232
|
+
readonly observedAt: string;
|
|
233
|
+
readonly payloadHash: string;
|
|
234
|
+
readonly source: "provider_api" | "provider_webhook" | "manual_reconciliation";
|
|
235
|
+
}): Promise<{
|
|
236
|
+
readonly replayed: boolean;
|
|
237
|
+
} | InvoiceUnsupported>;
|
|
238
|
+
/** Verify M04 binding before claiming; document rows need no M09 issue duty. */
|
|
239
|
+
claimDeliveries(input: {
|
|
240
|
+
readonly limit: number;
|
|
241
|
+
readonly visibilitySeconds: number;
|
|
242
|
+
readonly requireClearance: boolean;
|
|
243
|
+
readonly orderId?: string | null;
|
|
244
|
+
}): Promise<readonly {
|
|
245
|
+
readonly token: string;
|
|
246
|
+
readonly generation: number;
|
|
247
|
+
readonly identity: InvoiceDocumentIdentity;
|
|
248
|
+
readonly providerDocumentRef: string;
|
|
249
|
+
readonly delivery: Omit<Parameters<InvoiceDeliveryPort["send"]>[0], "filename" | "content" | "contentType">;
|
|
250
|
+
}[] | InvoiceUnsupported>;
|
|
251
|
+
/** Native uncertain settlement passes p_receipt NULL and retains a non-null
|
|
252
|
+
* receiptRef hint in p_evidence; managed uses it as the provider message id.
|
|
253
|
+
* Managed failed_before_send uses retrySeconds for backoff; native ignores it.
|
|
254
|
+
*/
|
|
255
|
+
settleDelivery(input: {
|
|
256
|
+
readonly deliveryRef: string;
|
|
257
|
+
readonly token: string;
|
|
258
|
+
readonly generation: number;
|
|
259
|
+
readonly providerDocumentRef: string;
|
|
260
|
+
readonly retrySeconds: number;
|
|
261
|
+
readonly result: Exclude<Awaited<ReturnType<InvoiceDeliveryPort["send"]>>, InvoiceUnsupported>;
|
|
262
|
+
}): Promise<{
|
|
263
|
+
readonly kind: "recorded" | "stale";
|
|
264
|
+
readonly replayed: boolean;
|
|
265
|
+
} | InvoiceUnsupported>;
|
|
266
|
+
}
|
|
267
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { readInvoiceCapabilities } from "./capabilities.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { readInvoiceCapabilities } from "./capabilities.js";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { RenewalDue, RenewalExecutionRequest, RenewalFrozenSnapshot, RenewalPaymentRail, RenewalPreparedSettlement, RenewalReadback, RenewalReservationResult, RenewalSettlementPort, RenewalStorePort } from "./contracts/renewal.js";
|
|
2
|
+
import type { RenewalFailureHandoffPort } from "./contracts/dunning.js";
|
|
3
|
+
export type AutomaticRenewalRunStatus = "succeeded" | "refused" | "deferred" | "awaiting_provider" | "error" | "lease_active" | "terminal_replay";
|
|
4
|
+
export interface AutomaticRenewalRunResult {
|
|
5
|
+
subscriptionId: string;
|
|
6
|
+
identityKey: string;
|
|
7
|
+
operationId: string;
|
|
8
|
+
status: AutomaticRenewalRunStatus;
|
|
9
|
+
replayed: boolean;
|
|
10
|
+
reason: string | null;
|
|
11
|
+
caseRef?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface AutomaticRenewalPolicy {
|
|
14
|
+
executionInput(due: RenewalDue, operation: RenewalReadback, prepared: Pick<RenewalPreparedSettlement, "orderId" | "settlementIntentId">): RenewalExecutionRequest;
|
|
15
|
+
validateInput(input: RenewalExecutionRequest): void;
|
|
16
|
+
classifyFailure(reason: string): string;
|
|
17
|
+
identityKey(due: RenewalDue): string;
|
|
18
|
+
}
|
|
19
|
+
export interface AutomaticRenewalRunDeps {
|
|
20
|
+
store: RenewalStorePort;
|
|
21
|
+
inventory: {
|
|
22
|
+
expireReservations(input: {
|
|
23
|
+
now: string;
|
|
24
|
+
limit?: number;
|
|
25
|
+
}): Promise<number>;
|
|
26
|
+
reserve(input: {
|
|
27
|
+
operationId: string;
|
|
28
|
+
claimToken: string;
|
|
29
|
+
now: string;
|
|
30
|
+
}): Promise<RenewalReservationResult>;
|
|
31
|
+
};
|
|
32
|
+
settlement: RenewalSettlementPort;
|
|
33
|
+
paymentRails: {
|
|
34
|
+
resolve(channelKey: string): RenewalPaymentRail | null;
|
|
35
|
+
};
|
|
36
|
+
failureHandoff: RenewalFailureHandoffPort;
|
|
37
|
+
policy: AutomaticRenewalPolicy;
|
|
38
|
+
workerId: string;
|
|
39
|
+
now?: () => string;
|
|
40
|
+
}
|
|
41
|
+
export declare function runAutomaticSubscriptionRenewals(deps: AutomaticRenewalRunDeps, limit?: number): Promise<AutomaticRenewalRunResult[]>;
|
|
42
|
+
export declare function runOneAutomaticSubscriptionRenewal(deps: AutomaticRenewalRunDeps, due: RenewalDue, occurredAt?: string): Promise<AutomaticRenewalRunResult>;
|
|
43
|
+
/** No implicit +1: ambiguous history must be resolved by the host before opening a repair case. */
|
|
44
|
+
export declare function correlatedLogicalCycle(snapshot: RenewalFrozenSnapshot): number;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { automaticRenewalResult, handoffRefusal, recoverPreparedExternalAttempt, refuseRenewalBeforePayment, runRenewalBatchRows } from "./automaticRenewalExecution.js";
|
|
2
|
+
import { readAndApplyProviderOutcome } from "./paymentOutcomes.js";
|
|
3
|
+
export async function runAutomaticSubscriptionRenewals(deps, limit = 50) {
|
|
4
|
+
const now = (deps.now ?? defaultNow)();
|
|
5
|
+
await deps.inventory.expireReservations({ now, limit: Math.max(limit, 100) });
|
|
6
|
+
const due = await deps.store.listDue(limit, now);
|
|
7
|
+
return runRenewalBatchRows(due, row => runOneAutomaticSubscriptionRenewal(deps, row, now), deps.policy);
|
|
8
|
+
}
|
|
9
|
+
export async function runOneAutomaticSubscriptionRenewal(deps, due, occurredAt = (deps.now ?? defaultNow)()) {
|
|
10
|
+
const schedule = { subscriptionId: due.subscriptionId, scheduledAt: due.scheduledAt, scheduledInstant: due.scheduledInstant };
|
|
11
|
+
const snapshot = await deps.store.buildCycleSnapshots(schedule);
|
|
12
|
+
const claim = await deps.store.claimDue({ snapshot, workerId: deps.workerId, now: occurredAt });
|
|
13
|
+
if (claim.state === "succeeded" || claim.state === "refused")
|
|
14
|
+
return automaticRenewalResult(claim, "terminal_replay", claim.terminalReason);
|
|
15
|
+
if (!claim.acquired)
|
|
16
|
+
return automaticRenewalResult(claim, "lease_active", claim.reason);
|
|
17
|
+
const rail = deps.paymentRails.resolve(snapshot.settlementChannelKey);
|
|
18
|
+
if (!rail)
|
|
19
|
+
return refuseRenewalBeforePayment({ store: deps.store, operation: claim, reason: "settlement_channel_not_configured", occurredAt });
|
|
20
|
+
if (claim.state === "attempt_prepared")
|
|
21
|
+
return resumePreparedAttempt(deps, claim, rail, occurredAt);
|
|
22
|
+
const mandate = await deps.store.readMandateEvidence(schedule);
|
|
23
|
+
if (!mandate.authorized || mandate.authorizationRef !== snapshot.authorizationRef)
|
|
24
|
+
return refuseRenewalBeforePayment({ store: deps.store, operation: claim, reason: "authorization_evidence_mismatch", occurredAt });
|
|
25
|
+
const delivery = await deps.store.admitDeliveryAlignment({ ...schedule, occurredAt });
|
|
26
|
+
if (!delivery.allowed) {
|
|
27
|
+
if (delivery.deferred)
|
|
28
|
+
return { ...automaticRenewalResult(claim, "deferred", delivery.reason), ...(delivery.caseRef ? { caseRef: delivery.caseRef } : {}) };
|
|
29
|
+
return refuseRenewalBeforePayment({ store: deps.store, operation: claim, reason: delivery.reason, occurredAt });
|
|
30
|
+
}
|
|
31
|
+
const reservation = await deps.inventory.reserve({ operationId: claim.operationId, claimToken: claim.claimToken, now: occurredAt });
|
|
32
|
+
if (!reservation.reserved)
|
|
33
|
+
return automaticRenewalResult(claim, "refused", reservation.reason ?? "insufficient_atp");
|
|
34
|
+
const prepared = await deps.settlement.prepareRenewal({ operationId: claim.operationId, claimToken: claim.claimToken, now: occurredAt });
|
|
35
|
+
if (prepared.replayed) {
|
|
36
|
+
const readback = await deps.store.readRenewal(claim.identityKey);
|
|
37
|
+
if (!readback)
|
|
38
|
+
throw new Error("subscription_renewal_prepared_readback_missing");
|
|
39
|
+
return resumePreparedAttempt(deps, readback, rail, occurredAt);
|
|
40
|
+
}
|
|
41
|
+
const executionInput = deps.policy.executionInput(due, claim, prepared);
|
|
42
|
+
try {
|
|
43
|
+
deps.policy.validateInput(executionInput);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
await deps.settlement.recordTerminalOutcome({ operationId: claim.operationId, eventKey: `${claim.identityKey}:provider-preflight`, operationFingerprint: claim.operationFingerprint, outcome: "refused", reason: "provider_input_invalid", occurredAt });
|
|
47
|
+
return automaticRenewalResult(claim, "refused", "provider_input_invalid");
|
|
48
|
+
}
|
|
49
|
+
let execution;
|
|
50
|
+
try {
|
|
51
|
+
execution = await rail.execute(executionInput);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return automaticRenewalResult(claim, "awaiting_provider", "provider_execution_indeterminate");
|
|
55
|
+
}
|
|
56
|
+
const externalAttemptRef = execution.externalAttemptRef?.trim() || null;
|
|
57
|
+
if (externalAttemptRef)
|
|
58
|
+
await deps.settlement.acknowledgeExternalAttempt({ operationId: claim.operationId, operationFingerprint: claim.operationFingerprint, externalAttemptRef, acknowledgedAt: occurredAt });
|
|
59
|
+
if (execution.failure || execution.status === "requires_action") {
|
|
60
|
+
const reason = execution.failure?.reason ?? (execution.status === "requires_action" ? "off_session_sca_required" : "provider_failed");
|
|
61
|
+
return handoffRefusal(deps, claim, prepared, externalAttemptRef, reason, execution.failure?.failureClass ?? deps.policy.classifyFailure(reason), occurredAt);
|
|
62
|
+
}
|
|
63
|
+
if (!externalAttemptRef)
|
|
64
|
+
return automaticRenewalResult(claim, "awaiting_provider", "provider_attempt_ack_missing");
|
|
65
|
+
return readAndApplyProviderOutcome(deps, claim, prepared, rail, externalAttemptRef, occurredAt);
|
|
66
|
+
}
|
|
67
|
+
async function resumePreparedAttempt(deps, operation, rail, occurredAt) {
|
|
68
|
+
if (!operation.cycleId || !operation.orderId || !operation.settlementIntentId)
|
|
69
|
+
throw new Error("subscription_renewal_prepared_artifacts_missing");
|
|
70
|
+
const externalAttemptRef = await recoverPreparedExternalAttempt({ operation, rail, settlement: deps.settlement, occurredAt });
|
|
71
|
+
if (!externalAttemptRef)
|
|
72
|
+
return automaticRenewalResult(operation, "awaiting_provider", "prepared_attempt_ack_missing");
|
|
73
|
+
return readAndApplyProviderOutcome(deps, operation, { cycleId: operation.cycleId, orderId: operation.orderId, settlementIntentId: operation.settlementIntentId }, rail, externalAttemptRef, occurredAt);
|
|
74
|
+
}
|
|
75
|
+
function defaultNow() { return new Date().toISOString(); }
|
|
76
|
+
/** No implicit +1: ambiguous history must be resolved by the host before opening a repair case. */
|
|
77
|
+
export function correlatedLogicalCycle(snapshot) {
|
|
78
|
+
if (snapshot.cycle.status !== "correlated")
|
|
79
|
+
throw new Error("subscription_renewal_cycle_correlation_required");
|
|
80
|
+
return snapshot.cycle.logicalCycleNumber;
|
|
81
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { RenewalDue, RenewalPaymentRail, RenewalReadback, RenewalSettlementPort, RenewalStorePort } from "./contracts/renewal.js";
|
|
2
|
+
import type { AutomaticRenewalPolicy, AutomaticRenewalRunDeps, AutomaticRenewalRunResult, AutomaticRenewalRunStatus } from "./automaticRenewal.js";
|
|
3
|
+
export declare function automaticRenewalResult(operation: RenewalReadback, status: AutomaticRenewalRunStatus, reason: string | null): AutomaticRenewalRunResult;
|
|
4
|
+
export declare function recoverPreparedExternalAttempt(input: {
|
|
5
|
+
operation: RenewalReadback;
|
|
6
|
+
rail: RenewalPaymentRail;
|
|
7
|
+
settlement: RenewalSettlementPort;
|
|
8
|
+
occurredAt: string;
|
|
9
|
+
}): Promise<string | null>;
|
|
10
|
+
export declare function runRenewalBatchRows(due: RenewalDue[], run: (row: RenewalDue) => Promise<AutomaticRenewalRunResult>, policy: Pick<AutomaticRenewalPolicy, "identityKey">): Promise<AutomaticRenewalRunResult[]>;
|
|
11
|
+
export declare function refuseRenewalBeforePayment(input: {
|
|
12
|
+
store: RenewalStorePort;
|
|
13
|
+
operation: RenewalReadback;
|
|
14
|
+
reason: string;
|
|
15
|
+
occurredAt: string;
|
|
16
|
+
}): Promise<AutomaticRenewalRunResult>;
|
|
17
|
+
export declare function handoffRefusal(deps: AutomaticRenewalRunDeps, operation: RenewalReadback, prepared: {
|
|
18
|
+
cycleId: string;
|
|
19
|
+
orderId: string;
|
|
20
|
+
settlementIntentId: string;
|
|
21
|
+
}, externalAttemptRef: string | null, reason: string, failureClass: string, occurredAt: string): Promise<AutomaticRenewalRunResult>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { nextRetryAttemptAt } from "@openlup/core/subscription";
|
|
2
|
+
export function automaticRenewalResult(operation, status, reason) {
|
|
3
|
+
return { subscriptionId: operation.subscriptionId, identityKey: operation.identityKey, operationId: operation.operationId, status, replayed: operation.replayed, reason };
|
|
4
|
+
}
|
|
5
|
+
export async function recoverPreparedExternalAttempt(input) {
|
|
6
|
+
if (input.operation.externalAttemptRef)
|
|
7
|
+
return input.operation.externalAttemptRef;
|
|
8
|
+
if (!input.operation.settlementIntentId)
|
|
9
|
+
return null;
|
|
10
|
+
const externalAttemptRef = await input.rail.findExternalAttempt({ settlementIntentId: input.operation.settlementIntentId });
|
|
11
|
+
if (!externalAttemptRef)
|
|
12
|
+
return null;
|
|
13
|
+
await input.settlement.acknowledgeExternalAttempt({ operationId: input.operation.operationId, operationFingerprint: input.operation.operationFingerprint, externalAttemptRef, acknowledgedAt: input.occurredAt });
|
|
14
|
+
return externalAttemptRef;
|
|
15
|
+
}
|
|
16
|
+
export async function runRenewalBatchRows(due, run, policy) {
|
|
17
|
+
const results = [];
|
|
18
|
+
for (const row of due) {
|
|
19
|
+
try {
|
|
20
|
+
results.push(await run(row));
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
results.push({ subscriptionId: row.subscriptionId, identityKey: policy.identityKey(row), operationId: "", status: "error", replayed: false, reason: "renewal_processing_failed" });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return results;
|
|
27
|
+
}
|
|
28
|
+
export async function refuseRenewalBeforePayment(input) {
|
|
29
|
+
const refused = await input.store.refuseBeforePayment({ operationId: input.operation.operationId, claimToken: input.operation.claimToken, reason: input.reason, occurredAt: input.occurredAt });
|
|
30
|
+
return automaticRenewalResult(refused, "refused", input.reason);
|
|
31
|
+
}
|
|
32
|
+
const HANDOFF_OPENS_AT_RETRY_ATTEMPT = 1;
|
|
33
|
+
export async function handoffRefusal(deps, operation, prepared, externalAttemptRef, reason, failureClass, occurredAt) {
|
|
34
|
+
await deps.failureHandoff.handoffFailure({
|
|
35
|
+
idempotencyKey: `${operation.identityKey}:failure-handoff`, subscriptionId: operation.subscriptionId,
|
|
36
|
+
clientId: operation.cycleSnapshot.clientId, cycleId: prepared.cycleId, orderId: prepared.orderId,
|
|
37
|
+
paymentIntentId: prepared.settlementIntentId, retryAttempt: HANDOFF_OPENS_AT_RETRY_ATTEMPT,
|
|
38
|
+
failureClass, failureReason: reason,
|
|
39
|
+
nextRetryAt: nextRetryAttemptAt(occurredAt, HANDOFF_OPENS_AT_RETRY_ATTEMPT, undefined, failureClass),
|
|
40
|
+
occurredAt, amountMinor: operation.cycleSnapshot.pricingSnapshot.totalAmountMinor, currency: operation.cycleSnapshot.pricingSnapshot.currency,
|
|
41
|
+
});
|
|
42
|
+
await deps.settlement.recordTerminalOutcome({ operationId: operation.operationId, eventKey: `${operation.identityKey}:provider-refusal`, operationFingerprint: operation.operationFingerprint, outcome: "refused", externalRef: externalAttemptRef, reason, occurredAt });
|
|
43
|
+
return automaticRenewalResult(operation, "refused", reason);
|
|
44
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export interface SubscriptionCommandContext {
|
|
2
|
+
actor: {
|
|
3
|
+
kind: "customer" | "operator";
|
|
4
|
+
actorId: string;
|
|
5
|
+
clientId: string;
|
|
6
|
+
};
|
|
7
|
+
subscriptionId: string;
|
|
8
|
+
idempotencyKey: string;
|
|
9
|
+
now: string;
|
|
10
|
+
}
|
|
11
|
+
export type SubscriptionCommand = SubscriptionCommandContext & ({
|
|
12
|
+
action: "pause";
|
|
13
|
+
pausePreset: string;
|
|
14
|
+
reason: string | null;
|
|
15
|
+
survey: string | null;
|
|
16
|
+
saveOffer: string | null;
|
|
17
|
+
} | {
|
|
18
|
+
action: "resume" | "skip" | "cancel";
|
|
19
|
+
reason: string | null;
|
|
20
|
+
confirmedChargeTiming: boolean;
|
|
21
|
+
} | {
|
|
22
|
+
action: "slide";
|
|
23
|
+
newNextCycleAt: string;
|
|
24
|
+
confirmedChargeTiming: boolean;
|
|
25
|
+
} | {
|
|
26
|
+
action: "edit_future_terms";
|
|
27
|
+
shippingAddressId: string;
|
|
28
|
+
planDays: number;
|
|
29
|
+
cadenceDays: number;
|
|
30
|
+
expectedTemplateVersion: number;
|
|
31
|
+
acceptedQuoteHash: string;
|
|
32
|
+
confirmedChargeTiming: boolean;
|
|
33
|
+
coreLines: readonly SubscriptionBundleLine[];
|
|
34
|
+
addonLines: readonly SubscriptionBundleLine[];
|
|
35
|
+
compositionConstraint: string;
|
|
36
|
+
});
|
|
37
|
+
export interface SubscriptionBundleLine {
|
|
38
|
+
sku: string;
|
|
39
|
+
quantity: number;
|
|
40
|
+
}
|
|
41
|
+
export interface SubscriptionCommandReceipt {
|
|
42
|
+
actorId: string;
|
|
43
|
+
idempotencyKey: string;
|
|
44
|
+
commandFingerprint: string;
|
|
45
|
+
outcome: "applied" | "replayed" | "noop";
|
|
46
|
+
subscriptionId: string;
|
|
47
|
+
originalState: string;
|
|
48
|
+
nextCycleAt: string | null;
|
|
49
|
+
revision: string;
|
|
50
|
+
eventId: string | null;
|
|
51
|
+
}
|
|
52
|
+
export type SubscriptionUnsupportedCode = "multi_cycle_prepaid_unsupported" | "native_generic_bundle_commands_unsupported" | "native_command_bound_receipts_unsupported" | "native_prepared_attempt_store_unsupported";
|
|
53
|
+
export interface SubscriptionUnsupported {
|
|
54
|
+
supported: false;
|
|
55
|
+
refusalCode: SubscriptionUnsupportedCode;
|
|
56
|
+
}
|
|
57
|
+
/** Readiness must be selected explicitly; a port declaration does not install a store. */
|
|
58
|
+
export interface SubscriptionCapabilities {
|
|
59
|
+
graph: "managed" | "native";
|
|
60
|
+
multiCyclePrepaid: {
|
|
61
|
+
supported: false;
|
|
62
|
+
refusalCode: "multi_cycle_prepaid_unsupported";
|
|
63
|
+
};
|
|
64
|
+
genericBundleCommands: {
|
|
65
|
+
supported: true;
|
|
66
|
+
} | {
|
|
67
|
+
supported: false;
|
|
68
|
+
refusalCode: "native_generic_bundle_commands_unsupported";
|
|
69
|
+
};
|
|
70
|
+
commandBoundReceipts: {
|
|
71
|
+
supported: true;
|
|
72
|
+
} | {
|
|
73
|
+
supported: false;
|
|
74
|
+
refusalCode: "native_command_bound_receipts_unsupported";
|
|
75
|
+
};
|
|
76
|
+
preparedAttemptStore: {
|
|
77
|
+
supported: true;
|
|
78
|
+
} | {
|
|
79
|
+
supported: false;
|
|
80
|
+
refusalCode: "native_prepared_attempt_store_unsupported";
|
|
81
|
+
};
|
|
82
|
+
retryRecovery: "unavailable" | "selected";
|
|
83
|
+
stockRecovery: "unavailable" | "selected";
|
|
84
|
+
deliveryAdmission: "required";
|
|
85
|
+
executorCount: 1;
|
|
86
|
+
defaultEnabled: false;
|
|
87
|
+
}
|
|
88
|
+
export interface SubscriptionCommandPort {
|
|
89
|
+
readonly capabilities: SubscriptionCapabilities;
|
|
90
|
+
/** Future term writes, actor/command receipt and events must commit atomically. */
|
|
91
|
+
apply(command: SubscriptionCommand): Promise<SubscriptionCommandReceipt | SubscriptionUnsupported>;
|
|
92
|
+
}
|
|
93
|
+
export interface SubscriptionActivationPort {
|
|
94
|
+
activate(input: {
|
|
95
|
+
idempotencyKey: string;
|
|
96
|
+
orderId: string;
|
|
97
|
+
paymentIntentId: string;
|
|
98
|
+
paidEvidenceRef: string;
|
|
99
|
+
paidAt: string;
|
|
100
|
+
paymentMethodRef: string;
|
|
101
|
+
paymentMethodKind: string;
|
|
102
|
+
checkoutKind: string;
|
|
103
|
+
}): Promise<{
|
|
104
|
+
outcome: "noop" | "blocked";
|
|
105
|
+
reason: string;
|
|
106
|
+
} | {
|
|
107
|
+
outcome: "active";
|
|
108
|
+
subscriptionId: string;
|
|
109
|
+
orderId: string;
|
|
110
|
+
paymentIntentId: string;
|
|
111
|
+
nextCycleAt: string;
|
|
112
|
+
cadenceDays: number;
|
|
113
|
+
replayed: boolean;
|
|
114
|
+
}>;
|
|
115
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|