@openlup/platform 0.27.0 → 0.28.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 +167 -109
- package/README.md +111 -8
- package/dist/account-support/accountReadFacade.d.ts +8 -0
- package/dist/account-support/accountReadFacade.js +10 -0
- package/dist/account-support/accountReadModels.d.ts +69 -0
- package/dist/account-support/accountReadModels.js +272 -0
- package/dist/account-support/bindings.d.ts +23 -0
- package/dist/account-support/bindings.js +16 -0
- package/dist/account-support/contracts/account.d.ts +281 -0
- package/dist/account-support/contracts/account.js +1 -0
- package/dist/account-support/contracts/capabilities.d.ts +44 -0
- package/dist/account-support/contracts/capabilities.js +1 -0
- package/dist/account-support/contracts/commands.d.ts +307 -0
- package/dist/account-support/contracts/commands.js +1 -0
- package/dist/account-support/contracts/customer360.d.ts +154 -0
- package/dist/account-support/contracts/customer360.js +1 -0
- package/dist/account-support/contracts/index.d.ts +10 -0
- package/dist/account-support/contracts/index.js +1 -0
- package/dist/account-support/contracts/invoice.d.ts +79 -0
- package/dist/account-support/contracts/invoice.js +1 -0
- package/dist/account-support/contracts/operator.d.ts +289 -0
- package/dist/account-support/contracts/operator.js +1 -0
- package/dist/account-support/contribution.d.ts +24 -0
- package/dist/account-support/contribution.js +22 -0
- package/dist/account-support/customer360.d.ts +106 -0
- package/dist/account-support/customer360.js +163 -0
- package/dist/account-support/customerAccountReadDiagnostics.d.ts +11 -0
- package/dist/account-support/customerAccountReadDiagnostics.js +71 -0
- package/dist/account-support/index.d.ts +26 -0
- package/dist/account-support/index.js +15 -0
- package/dist/account-support/invoiceAccess.d.ts +57 -0
- package/dist/account-support/invoiceAccess.js +40 -0
- package/dist/account-support/migrations.d.ts +14 -0
- package/dist/account-support/migrations.js +7 -0
- package/dist/account-support/operatorAuthority.d.ts +67 -0
- package/dist/account-support/operatorAuthority.js +379 -0
- package/dist/account-support/operatorRecovery.d.ts +66 -0
- package/dist/account-support/operatorRecovery.js +129 -0
- package/dist/account-support/orderHistory.d.ts +50 -0
- package/dist/account-support/orderHistory.js +188 -0
- package/dist/account-support/paymentMethodComposition.d.ts +5 -0
- package/dist/account-support/paymentMethodComposition.js +4 -0
- package/dist/account-support/postgresOperatorAuthority.d.ts +16 -0
- package/dist/account-support/postgresOperatorAuthority.js +93 -0
- package/dist/account-support/selfService.d.ts +6 -0
- package/dist/account-support/selfService.js +81 -0
- package/dist/account-support/subscriptionCommandCodec.d.ts +10 -0
- package/dist/account-support/subscriptionCommandCodec.js +38 -0
- package/dist/catalog/draftSchemas.d.ts +1 -1
- package/dist/catalog/foundationSchemas.d.ts +3 -3
- package/dist/catalog/resolvedSkuProjection.d.ts +2 -2
- package/dist/catalog/sellableSchemas.d.ts +2 -2
- package/dist/commerce/checkoutCommandSchemas.d.ts +2 -2
- package/dist/commerce/checkoutResumeSchemas.d.ts +6 -6
- package/dist/commerce/checkoutSchemas.d.ts +12 -12
- package/dist/commerce/commerceReturnsPort.d.ts +2 -2
- package/dist/commerce/configuratorIntentSchemas.d.ts +2 -2
- package/dist/commerce/contractPrimitives.d.ts +8 -8
- package/dist/commerce/customerDefaultsSnapshotSchemas.d.ts +4 -4
- package/dist/commerce/model/omsReadModelHelpers.d.ts +4 -4
- package/dist/commerce/offerAvailabilitySchemas.d.ts +1 -1
- package/dist/commerce/oms/omsContractBase.d.ts +15 -15
- package/dist/commerce/oms/omsContractReadAuxSchemas.d.ts +11 -11
- package/dist/commerce/oms/omsContractReadSchemas.d.ts +121 -121
- package/dist/commerce/oms/omsContractRequests.d.ts +13 -13
- package/dist/commerce/oms/omsControlPlane.d.ts +22 -22
- package/dist/commerce/oms/omsFulfillmentContractSchemas.d.ts +12 -12
- package/dist/commerce/oms/omsFulfillmentDebugSchemas.d.ts +12 -12
- package/dist/commerce/orderReviewContracts.d.ts +33 -33
- package/dist/commerce/outboxEventSchemas.d.ts +4 -4
- package/dist/commerce/schemas.d.ts +21 -21
- package/dist/commerce/server/orderReviewHandlers.d.ts +10 -10
- package/dist/communications/consentPurposeRegistry.d.ts +1 -1
- package/dist/communications/newsletterIntegrationContracts.d.ts +8 -8
- package/dist/composition/accountSupportContribution.d.ts +18 -0
- package/dist/composition/accountSupportContribution.js +21 -0
- package/dist/composition/index.d.ts +1 -0
- package/dist/composition/index.js +1 -0
- package/dist/fulfillment/handlerSchemas.d.ts +40 -40
- package/dist/identity/customerContracts.d.ts +2 -2
- package/dist/invoicing/snapshot.d.ts +1 -1
- package/dist/pricing/acquisitionQuoteBinding.d.ts +1 -1
- package/dist/pricing/catalogFactsSchemas.d.ts +1 -1
- package/dist/pricing/quotePromotionEvidence.d.ts +1 -1
- package/dist/pricing/quoteSchemas.d.ts +56 -56
- package/dist/promotions/adminPromotionCodesContracts.d.ts +30 -30
- package/dist/promotions/adminPromotionsContracts.d.ts +14 -14
- package/package.json +12 -2
- package/release-gates.json +30 -3
- package/sql/20261026120000_account_support_operator_authority.sql +693 -0
- package/src/account-support/accountReadFacade.ts +15 -0
- package/src/account-support/accountReadModels.ts +371 -0
- package/src/account-support/bindings.ts +28 -0
- package/src/account-support/contracts/account.ts +306 -0
- package/src/account-support/contracts/capabilities.ts +30 -0
- package/src/account-support/contracts/commands.ts +243 -0
- package/src/account-support/contracts/customer360.ts +150 -0
- package/src/account-support/contracts/index.ts +141 -0
- package/src/account-support/contracts/invoice.ts +44 -0
- package/src/account-support/contracts/operator.ts +123 -0
- package/src/account-support/contribution.ts +35 -0
- package/src/account-support/customer360.ts +268 -0
- package/src/account-support/customerAccountReadDiagnostics.ts +83 -0
- package/src/account-support/index.ts +72 -0
- package/src/account-support/invoiceAccess.ts +63 -0
- package/src/account-support/migrations.ts +7 -0
- package/src/account-support/operatorAuthority.ts +393 -0
- package/src/account-support/operatorRecovery.ts +136 -0
- package/src/account-support/orderHistory.ts +309 -0
- package/src/account-support/paymentMethodComposition.ts +8 -0
- package/src/account-support/postgresOperatorAuthority.ts +92 -0
- package/src/account-support/selfService.ts +83 -0
- package/src/account-support/subscriptionCommandCodec.ts +48 -0
- package/src/composition/accountSupportContribution.ts +28 -0
- package/src/composition/index.ts +1 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/** Host-verified selection; never an authorization claim from a request body. */
|
|
2
|
+
export interface CustomerContext { principalId: string; clientId: string; graph: "managed" | "native" }
|
|
3
|
+
export type AccountReadRefusal = "FORBIDDEN" | "NOT_FOUND" | "UPSTREAM_UNAVAILABLE" | "INVALID_RESPONSE";
|
|
4
|
+
/** Owner DTOs stay host supplied, including private profile and market fields.
|
|
5
|
+
* Null and thrown failures retain their existing host mappings. No cursor is promised.
|
|
6
|
+
*/
|
|
7
|
+
export interface AccountReadPort<Account, OrderList, OrderDetail> {
|
|
8
|
+
getAccount(userId: string): Promise<Account | null>;
|
|
9
|
+
listOrders(userId: string, input: { limit: number }): Promise<OrderList | null>;
|
|
10
|
+
getOrderDetail(userId: string, orderId: string): Promise<OrderDetail | null>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
import type { AccountSupportMoney, SubscriptionPaymentMethodStatus } from "./commands.js";
|
|
14
|
+
import type { CustomerDocumentDeliveryStatus } from "./invoice.js";
|
|
15
|
+
|
|
16
|
+
export interface AccountProfile {
|
|
17
|
+
clientId: string;
|
|
18
|
+
email: string;
|
|
19
|
+
firstName: string | null;
|
|
20
|
+
lastName: string | null;
|
|
21
|
+
phone: string | null;
|
|
22
|
+
lifecycleStage: "lead" | "tester" | "customer" | "inactive"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface AccountAddress {
|
|
26
|
+
addressId: string;
|
|
27
|
+
kind: "shipping" | "billing" | "both";
|
|
28
|
+
label: string | null;
|
|
29
|
+
recipientName: string | null;
|
|
30
|
+
contactPhone: string | null;
|
|
31
|
+
companyName: string | null;
|
|
32
|
+
taxId: string | null;
|
|
33
|
+
line1: string;
|
|
34
|
+
line2: string | null;
|
|
35
|
+
city: string;
|
|
36
|
+
postalCode: string;
|
|
37
|
+
country: string;
|
|
38
|
+
isDefault: boolean;
|
|
39
|
+
deliveryNotes: string | null;
|
|
40
|
+
courierInstructions: string | null;
|
|
41
|
+
lastUsedAt: string | null;
|
|
42
|
+
createdAt: string;
|
|
43
|
+
updatedAt: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface AccountBillingProfile {
|
|
47
|
+
profileId: string;
|
|
48
|
+
label: string | null;
|
|
49
|
+
fullName: string;
|
|
50
|
+
email: string;
|
|
51
|
+
phone: string | null;
|
|
52
|
+
companyName: string | null;
|
|
53
|
+
taxId: string | null;
|
|
54
|
+
companyVerificationLevel?: "registry_verified" | "provider_verified" | "manual_unverified" | "invalid" | null;
|
|
55
|
+
companyIdentitySource?: string | null;
|
|
56
|
+
companyIdentityEvidenceHash?: string | null;
|
|
57
|
+
isDefault: boolean;
|
|
58
|
+
createdAt: string;
|
|
59
|
+
updatedAt: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Method/provider vocabulary is supplied by the selected host, never a platform provider list. */
|
|
63
|
+
export interface AccountPaymentPreference<Method extends string = string> {
|
|
64
|
+
scope: "one_time" | "subscription" | "any";
|
|
65
|
+
methodKind: Method;
|
|
66
|
+
lastSelectedAt: string
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface AccountPickupPoint {
|
|
70
|
+
id: string;
|
|
71
|
+
name: string;
|
|
72
|
+
address: { line1: string; postalCode: string; city: string; country: string } | null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface AccountDeliverySelection {
|
|
76
|
+
deliveryKind: "courier" | "parcel-locker";
|
|
77
|
+
providerKind: string | null;
|
|
78
|
+
carrierKind: string | null;
|
|
79
|
+
carrierCode: string | null;
|
|
80
|
+
serviceCode: string | null;
|
|
81
|
+
pickupPoint: AccountPickupPoint | null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface AccountDeliveryPreference<Provider extends string = string> extends AccountDeliverySelection {
|
|
85
|
+
scope: "one_time" | "subscription" | "any";
|
|
86
|
+
providerKind: Provider;
|
|
87
|
+
carrierKind: string;
|
|
88
|
+
carrierCode: string;
|
|
89
|
+
serviceCode: string;
|
|
90
|
+
lastSelectedAt: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface AccountEvent {
|
|
94
|
+
eventId: string;
|
|
95
|
+
eventType: string;
|
|
96
|
+
entityType: string;
|
|
97
|
+
entityId: string | null;
|
|
98
|
+
occurredAt: string
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface AccountSubscriptionLine {
|
|
102
|
+
lineId: string;
|
|
103
|
+
variantId: string;
|
|
104
|
+
qty: number;
|
|
105
|
+
sortOrder: number;
|
|
106
|
+
isAddon: boolean;
|
|
107
|
+
title: string | null;
|
|
108
|
+
sku: string | null;
|
|
109
|
+
productSlug?: string | null;
|
|
110
|
+
displayLabel?: string | null;
|
|
111
|
+
accentColor?: string | null;
|
|
112
|
+
unitPrice?: AccountSupportMoney | null;
|
|
113
|
+
lineSubtotal?: AccountSupportMoney | null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface AccountSubscription {
|
|
117
|
+
subscriptionId: string;
|
|
118
|
+
shippingAddressId: string | null;
|
|
119
|
+
status: "pending_activation" | "active" | "paused" | "cancelled" | "activation_failed" | "completed";
|
|
120
|
+
pausePreset: "2_weeks" | "1_month" | "indefinite" | null;
|
|
121
|
+
pauseStartedAt: string | null;
|
|
122
|
+
pauseEndsAt: string | null;
|
|
123
|
+
cadenceDays: number;
|
|
124
|
+
nextCycleAt: string | null;
|
|
125
|
+
deliveryAlignment?: { state: "protected" | "aligned" };
|
|
126
|
+
editCutoffAt: string | null;
|
|
127
|
+
canEditUpcomingPackage: boolean;
|
|
128
|
+
editBlockedReason: "not_active" | "edit_window_closed" | "cycle_locked" | "payment_blocked" | "missing_payment_method" | null;
|
|
129
|
+
paymentMethodKind: string | null;
|
|
130
|
+
paymentMethodStatus?: SubscriptionPaymentMethodStatus;
|
|
131
|
+
templateVersion: number;
|
|
132
|
+
sizeConstraint: Record<string, unknown> | null;
|
|
133
|
+
packageSummary: string | null;
|
|
134
|
+
recurringPrice: {
|
|
135
|
+
subtotalGross: AccountSupportMoney;
|
|
136
|
+
totalGross: AccountSupportMoney;
|
|
137
|
+
currency: string;
|
|
138
|
+
source: "frozen_quote_line" | "missing"
|
|
139
|
+
}
|
|
140
|
+
| null;
|
|
141
|
+
nextCharge?: { totalGross: AccountSupportMoney; starterStage: "delivery2" | "graduation" | null } | null;
|
|
142
|
+
lines: AccountSubscriptionLine[];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface AccountTrackingReference {
|
|
146
|
+
providerKind: string;
|
|
147
|
+
trackingNumber: string;
|
|
148
|
+
trackingUrl: string | null;
|
|
149
|
+
carrierKind: string | null;
|
|
150
|
+
service: string | null;
|
|
151
|
+
updatedAt: string | null
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface AccountTrackingEvent {
|
|
155
|
+
eventType: string;
|
|
156
|
+
label: string;
|
|
157
|
+
occurredAt: string | null;
|
|
158
|
+
source: "webhook" | "reconciliation" | "manual" | "simulator" | "fulfillment"
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface AccountInvoiceSummary {
|
|
162
|
+
invoiceId: string;
|
|
163
|
+
orderId: string;
|
|
164
|
+
invoiceRef: string;
|
|
165
|
+
status: string;
|
|
166
|
+
providerInvoiceNumber: string | null;
|
|
167
|
+
ksefNumber: string | null;
|
|
168
|
+
ksefStatus: string;
|
|
169
|
+
documentDeliveryStatus: CustomerDocumentDeliveryStatus;
|
|
170
|
+
totalGross: AccountSupportMoney;
|
|
171
|
+
issuedAt: string | null;
|
|
172
|
+
downloadAvailable: boolean;
|
|
173
|
+
downloadUrl: string | null;
|
|
174
|
+
createdAt: string;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface AccountInvoiceDocument extends Omit<AccountInvoiceSummary, "totalGross"> {
|
|
178
|
+
documentKey: string;
|
|
179
|
+
role: "original" | "correction" | "replacement";
|
|
180
|
+
artifact: "invoice" | "correction";
|
|
181
|
+
isCurrent: boolean;
|
|
182
|
+
totalGross: AccountSupportMoney | null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export interface AccountOrderSummary {
|
|
186
|
+
orderId: string;
|
|
187
|
+
orderRef: string;
|
|
188
|
+
orderNumber: string | null;
|
|
189
|
+
subscriptionId: string | null;
|
|
190
|
+
status: string;
|
|
191
|
+
paymentStatus: string | null;
|
|
192
|
+
fulfillmentStatus: string | null;
|
|
193
|
+
customerFulfillmentStep?: "paid" | "accepted" | "packing" | "transit" | "delivered" | "exception" | "cancelled";
|
|
194
|
+
total: AccountSupportMoney;
|
|
195
|
+
trackingNumber: string | null;
|
|
196
|
+
trackingNumbers: string[];
|
|
197
|
+
trackingUrl: string | null;
|
|
198
|
+
carrierKind: string | null;
|
|
199
|
+
service: string | null;
|
|
200
|
+
trackingReferences: AccountTrackingReference[];
|
|
201
|
+
trackingTimeline: AccountTrackingEvent[];
|
|
202
|
+
deliverySelection: AccountDeliverySelection | null;
|
|
203
|
+
invoice: AccountInvoiceSummary | null;
|
|
204
|
+
invoiceDocuments: AccountInvoiceDocument[];
|
|
205
|
+
invoiceRequestStatus: "not_requested" | "preparing" | "available";
|
|
206
|
+
createdAt: string;
|
|
207
|
+
updatedAt: string;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface AccountOrderLine {
|
|
211
|
+
lineId: string;
|
|
212
|
+
skuId: string | null;
|
|
213
|
+
title: string;
|
|
214
|
+
quantity: number;
|
|
215
|
+
unitPrice: AccountSupportMoney;
|
|
216
|
+
total: AccountSupportMoney;
|
|
217
|
+
variantName: string | null;
|
|
218
|
+
productSlug?: string | null;
|
|
219
|
+
displayLabel?: string | null;
|
|
220
|
+
accentColor?: string | null;
|
|
221
|
+
listTotal?: AccountSupportMoney | null;
|
|
222
|
+
discount?: AccountSupportMoney | null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export interface AccountFulfillmentSummary {
|
|
226
|
+
fulfillmentOrderId: string | null;
|
|
227
|
+
status: string | null;
|
|
228
|
+
providerKind: string | null;
|
|
229
|
+
trackingNumber: string | null;
|
|
230
|
+
trackingNumbers: string[];
|
|
231
|
+
trackingReferences: AccountTrackingReference[];
|
|
232
|
+
lastEventType: string | null;
|
|
233
|
+
updatedAt: string | null;
|
|
234
|
+
trackingTimeline: AccountTrackingEvent[];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export interface AccountOrderDetail extends AccountOrderSummary {
|
|
238
|
+
subtotal: AccountSupportMoney;
|
|
239
|
+
discount: AccountSupportMoney;
|
|
240
|
+
shipping: AccountSupportMoney;
|
|
241
|
+
tax: AccountSupportMoney;
|
|
242
|
+
lines: AccountOrderLine[];
|
|
243
|
+
fulfillment: AccountFulfillmentSummary | null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export interface AccountOrderList {
|
|
247
|
+
contractVersion: "customer.orders.v2";
|
|
248
|
+
orders: AccountOrderSummary[]
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export interface AccountOrderDetailResponse {
|
|
252
|
+
contractVersion: "customer.orders.v2";
|
|
253
|
+
order: AccountOrderDetail
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export interface AccountActionRequired {
|
|
257
|
+
actionId: string;
|
|
258
|
+
kind: "payment_recovery"
|
|
259
|
+
| "payment_method_missing"
|
|
260
|
+
| "subscription_blocked"
|
|
261
|
+
| "order_attention"
|
|
262
|
+
| "invoice_attention";
|
|
263
|
+
severity: "info" | "warning" | "critical";
|
|
264
|
+
entityType: "subscription" | "order" | "invoice" | "account";
|
|
265
|
+
entityId: string | null;
|
|
266
|
+
subscriptionId: string | null;
|
|
267
|
+
orderId: string | null;
|
|
268
|
+
messageCode: AccountActionMessage;
|
|
269
|
+
blockedReason: AccountActionMessage | null;
|
|
270
|
+
title: string;
|
|
271
|
+
body: string | null;
|
|
272
|
+
cta: "repair_payment" | "view_subscription" | "view_order" | "contact_support" | null;
|
|
273
|
+
failureCause: "needs_confirmation"
|
|
274
|
+
| "method_cannot_recur"
|
|
275
|
+
| "method_dead"
|
|
276
|
+
| "method_data_invalid"
|
|
277
|
+
| "method_missing"
|
|
278
|
+
| "unknown";
|
|
279
|
+
recoveryEligible: boolean;
|
|
280
|
+
dueAt: string | null;
|
|
281
|
+
nextRetryAt: string | null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export type AccountActionMessage = "payment_blocked"
|
|
285
|
+
| "payment_expired"
|
|
286
|
+
| "missing_payment_method"
|
|
287
|
+
| "payment_failed"
|
|
288
|
+
| "payment_requires_action"
|
|
289
|
+
| "subscription_edit_blocked"
|
|
290
|
+
| "invoice_pdf_unavailable"
|
|
291
|
+
| "subscription_activation_missing_mandate";
|
|
292
|
+
|
|
293
|
+
export interface AccountSnapshot<Extension = unknown> {
|
|
294
|
+
contractVersion: "customer.account.v3";
|
|
295
|
+
profile: AccountProfile;
|
|
296
|
+
extension: Extension;
|
|
297
|
+
subscriptions: AccountSubscription[];
|
|
298
|
+
addresses: AccountAddress[];
|
|
299
|
+
ordererProfiles: AccountBillingProfile[];
|
|
300
|
+
billingProfiles: AccountBillingProfile[];
|
|
301
|
+
paymentPreferences: AccountPaymentPreference[];
|
|
302
|
+
deliveryPreferences: AccountDeliveryPreference[];
|
|
303
|
+
recentOrders: AccountOrderSummary[];
|
|
304
|
+
events: AccountEvent[];
|
|
305
|
+
actionRequired: AccountActionRequired[];
|
|
306
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { SubscriptionCapabilities } from "./commands.js";
|
|
2
|
+
export type AccountSupportCapability<Code extends string> = { supported: true } | { supported: false; refusalCode: Code };
|
|
3
|
+
export type AccountSupportLaneUnavailable<Code extends string> = Extract<AccountSupportCapability<Code>, { supported: false }>;
|
|
4
|
+
/** Explicit driver selections; importing the module installs no capability. */
|
|
5
|
+
export interface AccountSupportCapabilities {
|
|
6
|
+
graph: "managed" | "native";
|
|
7
|
+
accountRead: AccountSupportCapability<"host_binding_required">;
|
|
8
|
+
customer360Read: AccountSupportCapability<"host_binding_required">;
|
|
9
|
+
invoiceAccess: AccountSupportCapability<"host_binding_required">;
|
|
10
|
+
paymentMethodSetup: AccountSupportCapability<"unsupported">;
|
|
11
|
+
commerceRecovery: AccountSupportCapability<"host_binding_required">;
|
|
12
|
+
configuredPurchase: AccountSupportCapability<"host_binding_required">;
|
|
13
|
+
operatorSubscription: AccountSupportCapability<"operator_subscription_authority_unavailable">;
|
|
14
|
+
operatorContact: AccountSupportCapability<"operator_subscription_authority_unavailable">;
|
|
15
|
+
subscriptions: SubscriptionCapabilities;
|
|
16
|
+
communicationGifts: AccountSupportCapability<"unsupported_preference">;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Per-action/subcase truth, independent of the host's unrelated read selections. */
|
|
20
|
+
export interface OperatorAuthorityCapabilities {
|
|
21
|
+
graph: "managed" | "native";
|
|
22
|
+
email: AccountSupportCapability<"operator_subscription_authority_unavailable">;
|
|
23
|
+
phone: AccountSupportCapability<"operator_subscription_authority_unavailable">;
|
|
24
|
+
link: AccountSupportCapability<"operator_subscription_authority_unavailable">;
|
|
25
|
+
address: AccountSupportCapability<"operator_subscription_authority_unavailable">;
|
|
26
|
+
shortBand: AccountSupportCapability<"operator_subscription_authority_unavailable">;
|
|
27
|
+
unpaidResume: AccountSupportCapability<"operator_subscription_authority_unavailable">;
|
|
28
|
+
preparedAttemptPause: AccountSupportCapability<"operator_subscription_authority_unavailable">;
|
|
29
|
+
pause: { supported: true }; resume: { supported: true }; slide: { supported: true };
|
|
30
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import type { CustomerContext } from "./account.js";
|
|
2
|
+
import type { AccountSupportCapabilities, AccountSupportLaneUnavailable } from "./capabilities.js";
|
|
3
|
+
/** Local transcriptions of owner contracts; portFit checks equality against the owners. */
|
|
4
|
+
export interface SubscriptionCommandContext {
|
|
5
|
+
actor: { kind: "customer" | "operator"; actorId: string; clientId: string };
|
|
6
|
+
subscriptionId: string; idempotencyKey: string; now: string;
|
|
7
|
+
}
|
|
8
|
+
export type SubscriptionCommand = SubscriptionCommandContext & (
|
|
9
|
+
| { action: "pause"; pausePreset: string; reason: string | null; survey: string | null; saveOffer: string | null }
|
|
10
|
+
| { action: "resume" | "skip" | "cancel"; reason: string | null; confirmedChargeTiming: boolean }
|
|
11
|
+
| { action: "slide"; newNextCycleAt: string; confirmedChargeTiming: boolean }
|
|
12
|
+
| { action: "edit_future_terms"; shippingAddressId: string; planDays: number; cadenceDays: number;
|
|
13
|
+
expectedTemplateVersion: number;
|
|
14
|
+
/** Empty string means no quote (semantic no-op). */
|
|
15
|
+
acceptedQuoteHash: string; confirmedChargeTiming: boolean;
|
|
16
|
+
coreLines: readonly SubscriptionBundleLine[]; addonLines: readonly SubscriptionBundleLine[];
|
|
17
|
+
compositionConstraint: string }
|
|
18
|
+
);
|
|
19
|
+
export interface SubscriptionBundleLine { sku: string; quantity: number }
|
|
20
|
+
export interface SubscriptionCommandReceipt {
|
|
21
|
+
actorId: string; idempotencyKey: string; commandFingerprint: string;
|
|
22
|
+
outcome: "applied" | "replayed" | "noop"; subscriptionId: string;
|
|
23
|
+
originalState: string; nextCycleAt: string | null; revision: string; eventId: string | null;
|
|
24
|
+
}
|
|
25
|
+
export interface SubscriptionCommandRefusal {
|
|
26
|
+
outcome: "refused";
|
|
27
|
+
/** Stable machine code; never provider, PostgreSQL diagnostic or customer text.
|
|
28
|
+
* Unrecognized native or host diagnostics map to subscription_command_invalid_input. */
|
|
29
|
+
reason:
|
|
30
|
+
| "multi_cycle_prepaid_unsupported"
|
|
31
|
+
| "subscription_command_addon_not_found"
|
|
32
|
+
| "subscription_command_bundle_total_mismatch"
|
|
33
|
+
| "subscription_command_charge_timing_not_confirmed"
|
|
34
|
+
| "subscription_command_idempotency_conflict"
|
|
35
|
+
| "subscription_command_invalid_composition_constraint"
|
|
36
|
+
| "subscription_command_invalid_input"
|
|
37
|
+
| "subscription_command_not_active"
|
|
38
|
+
| "subscription_command_operation_busy"
|
|
39
|
+
| "subscription_command_pause_forbidden"
|
|
40
|
+
| "subscription_command_payment_blocked"
|
|
41
|
+
| "subscription_command_preview_unsupported"
|
|
42
|
+
| "subscription_command_renewal_terms_missing"
|
|
43
|
+
| "subscription_command_unsupported_action"
|
|
44
|
+
| "subscription_edit_catalog_unavailable"
|
|
45
|
+
| "subscription_edit_invalid_lines"
|
|
46
|
+
| "subscription_edit_invalid_request"
|
|
47
|
+
| "subscription_edit_quote_expired"
|
|
48
|
+
| "subscription_edit_quote_required"
|
|
49
|
+
| "subscription_edit_stale_template"
|
|
50
|
+
| "subscription_edit_window_closed"
|
|
51
|
+
| "subscription_lifecycle_invalid_transition"
|
|
52
|
+
| "subscription_lifecycle_invalid_skip"
|
|
53
|
+
| "subscription_lifecycle_invalid_slide"
|
|
54
|
+
| "subscription_lifecycle_payment_blocked";
|
|
55
|
+
}
|
|
56
|
+
export type SubscriptionUnsupportedCode = "multi_cycle_prepaid_unsupported" | "native_generic_bundle_commands_unsupported" | "native_command_bound_receipts_unsupported" | "native_prepared_attempt_store_unsupported";
|
|
57
|
+
export interface SubscriptionUnsupported { supported: false; refusalCode: SubscriptionUnsupportedCode }
|
|
58
|
+
/** Readiness must be selected explicitly; a port declaration does not install a store. */
|
|
59
|
+
export interface SubscriptionCapabilities {
|
|
60
|
+
graph: "managed" | "native";
|
|
61
|
+
multiCyclePrepaid: { supported: false; refusalCode: "multi_cycle_prepaid_unsupported" };
|
|
62
|
+
genericBundleCommands: { supported: true } | { supported: false; refusalCode: "native_generic_bundle_commands_unsupported" };
|
|
63
|
+
commandBoundReceipts: { supported: true } | { supported: false; refusalCode: "native_command_bound_receipts_unsupported" };
|
|
64
|
+
preparedAttemptStore: { supported: true } | { supported: false; refusalCode: "native_prepared_attempt_store_unsupported" };
|
|
65
|
+
retryRecovery: "unavailable" | "selected";
|
|
66
|
+
stockRecovery: "unavailable" | "selected";
|
|
67
|
+
deliveryAdmission: "required";
|
|
68
|
+
executorCount: 1;
|
|
69
|
+
defaultEnabled: false;
|
|
70
|
+
}
|
|
71
|
+
/** Exact raw native owner command; its result still requires a host codec. */
|
|
72
|
+
export interface NativeSubscriptionCommand {
|
|
73
|
+
actorId: string; clientId: string; idempotencyKey: string; subscriptionId: string;
|
|
74
|
+
action: string; payload: Record<string, unknown>; now: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Driver DTOs are explicit type arguments, never a claim that all graphs implement them. */
|
|
78
|
+
export interface SelfServiceCommandFacade<ManagedRequest, ManagedPreviewRequest, ManagedPreview, ManagedReceipt,
|
|
79
|
+
ProfileRequest, ProfileResult, MethodRequest, MethodResult, CommerceRequest, CommerceResult, RecoveryRequest, RecoveryResult, ControlSnapshot = unknown> {
|
|
80
|
+
capabilities: AccountSupportCapabilities;
|
|
81
|
+
managedSubscription: {
|
|
82
|
+
previewAction(userId: string, request: ManagedPreviewRequest): Promise<ManagedPreview | null | AccountSupportLaneUnavailable<"host_binding_required">>;
|
|
83
|
+
applyAction(userId: string, request: ManagedRequest): Promise<ManagedReceipt | null | AccountSupportLaneUnavailable<"host_binding_required">>;
|
|
84
|
+
};
|
|
85
|
+
nativeLegacySubscription: {
|
|
86
|
+
previewAction(userId: string, request: ManagedPreviewRequest): Promise<ManagedPreview | null | AccountSupportLaneUnavailable<"host_binding_required">>;
|
|
87
|
+
applyAction(userId: string, request: ManagedRequest): Promise<ManagedReceipt | null | AccountSupportLaneUnavailable<"host_binding_required">>;
|
|
88
|
+
};
|
|
89
|
+
subscriptionControl: { getSnapshot(userId: string): Promise<ControlSnapshot | AccountSupportLaneUnavailable<"host_binding_required">> };
|
|
90
|
+
nativeRawSubscription: {
|
|
91
|
+
preview(command: NativeSubscriptionCommand): Promise<Record<string, unknown> | AccountSupportLaneUnavailable<"host_binding_required">>;
|
|
92
|
+
apply(command: NativeSubscriptionCommand): Promise<Record<string, unknown> | AccountSupportLaneUnavailable<"host_binding_required">>;
|
|
93
|
+
};
|
|
94
|
+
nativeSubscription: {
|
|
95
|
+
apply(command: SubscriptionCommand): Promise<SubscriptionCommandReceipt | SubscriptionCommandRefusal | SubscriptionUnsupported | AccountSupportLaneUnavailable<"host_binding_required">>;
|
|
96
|
+
preview(command: Extract<SubscriptionCommand, { action: "edit_future_terms" }>): Promise<Record<string, unknown> | SubscriptionCommandRefusal | AccountSupportLaneUnavailable<"host_binding_required">>;
|
|
97
|
+
};
|
|
98
|
+
profile: { updateProfile(userId: string, request: ProfileRequest): Promise<ProfileResult | null | AccountSupportLaneUnavailable<"host_binding_required">> };
|
|
99
|
+
paymentMethod: { execute(userId: string, request: MethodRequest): Promise<MethodResult | AccountSupportLaneUnavailable<"unsupported">> };
|
|
100
|
+
commerce: { execute(userId: string, request: CommerceRequest): Promise<CommerceResult | AccountSupportLaneUnavailable<"host_binding_required">> };
|
|
101
|
+
recovery: { startPaymentRecovery(userId: string, request: RecoveryRequest): Promise<RecoveryResult | null | AccountSupportLaneUnavailable<"host_binding_required">> };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface AccountSupportMoney { amountMinor: number; currency: string }
|
|
105
|
+
export type SubscriptionPaymentMethodStatus = "usable" | "expiring" | "missing" | "revoked" | "requires_action" | "invalid" | "provider_disabled";
|
|
106
|
+
export type SubscriptionRenewalBlockStatus = "blocked_preflight" | "payment_pending" | "dunning" | "paid_fulfillment_pending" | "fulfillment_blocked" | "catalog_blocked" | "customer_action_required";
|
|
107
|
+
export type EligibilityRefusal =
|
|
108
|
+
| "not_active"
|
|
109
|
+
| "edit_window_closed"
|
|
110
|
+
| "cycle_locked"
|
|
111
|
+
| "payment_blocked"
|
|
112
|
+
| "missing_payment_method"
|
|
113
|
+
| "invalid_address"
|
|
114
|
+
| null;
|
|
115
|
+
|
|
116
|
+
export interface LegacySubscriptionPreviewResponse {
|
|
117
|
+
preview: {
|
|
118
|
+
subscriptionId: string;
|
|
119
|
+
action: string;
|
|
120
|
+
canApply: boolean;
|
|
121
|
+
blockedReason: EligibilityRefusal;
|
|
122
|
+
nextCycleAt: string | null;
|
|
123
|
+
editCutoffAt: string | null;
|
|
124
|
+
templateVersion: number | null;
|
|
125
|
+
lockedCycle?: {
|
|
126
|
+
locked: boolean;
|
|
127
|
+
status: SubscriptionRenewalBlockStatus | null;
|
|
128
|
+
};
|
|
129
|
+
futureTemplate?: {
|
|
130
|
+
templateVersion: number | null;
|
|
131
|
+
effectiveCycleAt: string | null;
|
|
132
|
+
priceAgreementPolicy: "lock_until_edit";
|
|
133
|
+
};
|
|
134
|
+
paymentMethodStatus?: SubscriptionPaymentMethodStatus;
|
|
135
|
+
deliverability?: {
|
|
136
|
+
status: "address_book_validated" | "blocked" | "unknown";
|
|
137
|
+
blockedReason: EligibilityRefusal;
|
|
138
|
+
};
|
|
139
|
+
catalogAvailability?: {
|
|
140
|
+
status: "available" | "blocked" | "unknown";
|
|
141
|
+
blockedReason: EligibilityRefusal;
|
|
142
|
+
};
|
|
143
|
+
quoteHash?: string;
|
|
144
|
+
quoteExpiresAt?: string;
|
|
145
|
+
currentTotal?: AccountSupportMoney;
|
|
146
|
+
newTotal?: AccountSupportMoney;
|
|
147
|
+
delta?: AccountSupportMoney;
|
|
148
|
+
chargeTiming?: {
|
|
149
|
+
requiresConfirmation: boolean;
|
|
150
|
+
confirmed: boolean;
|
|
151
|
+
earliestChargeAt: string | null;
|
|
152
|
+
};
|
|
153
|
+
dateAvailability?: {
|
|
154
|
+
earliestAllowedAt: string;
|
|
155
|
+
latestAllowedAt: string;
|
|
156
|
+
availableDates: string[];
|
|
157
|
+
blockedReason: EligibilityRefusal;
|
|
158
|
+
};
|
|
159
|
+
packageEdit?: {
|
|
160
|
+
currentRecurringPrice: AccountSupportMoney;
|
|
161
|
+
newRecurringPrice: AccountSupportMoney;
|
|
162
|
+
delta: AccountSupportMoney;
|
|
163
|
+
quoteHash: string;
|
|
164
|
+
effectiveCycleAt: string | null;
|
|
165
|
+
priceAgreementPolicy: "lock_until_edit";
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
/** Already resolved host request. The codec never supplies policy or catalogue fields. */
|
|
172
|
+
export interface LegacySubscriptionRequest<Action extends string = string, Payload extends Record<string, unknown> = Record<string, unknown>> {
|
|
173
|
+
subscriptionId: string;
|
|
174
|
+
idempotencyKey: string;
|
|
175
|
+
action: Action;
|
|
176
|
+
payload: Payload;
|
|
177
|
+
}
|
|
178
|
+
export interface RawSubscriptionCommands {
|
|
179
|
+
preview(command: NativeSubscriptionCommand): Promise<Record<string, unknown>>;
|
|
180
|
+
apply(command: NativeSubscriptionCommand): Promise<Record<string, unknown>>;
|
|
181
|
+
}
|
|
182
|
+
/** Only checked native quote records reach a host projection. */
|
|
183
|
+
export interface NativeSubscriptionPreview extends Record<string, unknown> {
|
|
184
|
+
status: "previewed";
|
|
185
|
+
canApply: true;
|
|
186
|
+
templateVersion: number;
|
|
187
|
+
quoteHash: string;
|
|
188
|
+
quoteExpiresAt: string;
|
|
189
|
+
currentTotalMinor: number;
|
|
190
|
+
newTotalMinor: number;
|
|
191
|
+
lines: { variantId: string; quantity: number; isAddon: boolean; sortOrder: number; unitPriceMinor: number; currency: string }[];
|
|
192
|
+
priceAgreementPolicy: { kind: "lock_until_edit"; proration: "none"; cycleEffect: "future_unlocked_cycles_only" };
|
|
193
|
+
}
|
|
194
|
+
export type NativePreviewProjection<Preview> =
|
|
195
|
+
| { kind: "projected"; preview: Preview }
|
|
196
|
+
| { kind: "owner"; result: Record<string, unknown> };
|
|
197
|
+
export interface SubscriptionDriver<Request, Preview, Receipt> {
|
|
198
|
+
preview(context: CustomerContext, request: Request): Promise<Preview>;
|
|
199
|
+
apply(context: CustomerContext, request: Request): Promise<Receipt>;
|
|
200
|
+
}
|
|
201
|
+
/** Browser-safe transcription of M07 collection; instantiate with its branded handoff. */
|
|
202
|
+
export interface MethodSetupInput {
|
|
203
|
+
customer: string;
|
|
204
|
+
metadata: Record<string, string>;
|
|
205
|
+
idempotencyKey: string;
|
|
206
|
+
}
|
|
207
|
+
export type MethodSetupResult<Handoff> =
|
|
208
|
+
| { kind: "ready"; setupRef: string; handoff: Handoff; replayed: boolean }
|
|
209
|
+
| { kind: "unsupported" };
|
|
210
|
+
export interface MethodSetupPort<Handoff> {
|
|
211
|
+
setupMethod(input: MethodSetupInput): Promise<MethodSetupResult<Handoff>>;
|
|
212
|
+
}
|
|
213
|
+
/** Host parts retain repricing, quote validation, authorization and error construction. */
|
|
214
|
+
export interface NativeLegacySubscriptionParts<Request, PreviewRequest, Preview, Receipt, ControlSnapshot = unknown> {
|
|
215
|
+
explicitPause: { applyAction(userId: string, request: Request): Promise<Receipt | null> };
|
|
216
|
+
control: LegacySubscriptionPort<Request, PreviewRequest, Preview, Receipt> & {
|
|
217
|
+
hasSubscription(userId: string, subscriptionId: string): Promise<boolean>;
|
|
218
|
+
getSnapshot(userId: string): Promise<ControlSnapshot>;
|
|
219
|
+
};
|
|
220
|
+
ownershipRefusal(): unknown;
|
|
221
|
+
}
|
|
222
|
+
export interface LegacySubscriptionPort<Request, PreviewRequest, Preview, Receipt> {
|
|
223
|
+
previewAction(userId: string, request: PreviewRequest): Promise<Preview | null>;
|
|
224
|
+
applyAction(userId: string, request: Request): Promise<Receipt | null>;
|
|
225
|
+
}
|
|
226
|
+
/** The host supplies capabilities and explicitly selects each owner lane. */
|
|
227
|
+
export type SelfServiceCommandFacadeInput<Request, PreviewRequest, Preview, Receipt,
|
|
228
|
+
ProfileRequest, ProfileResult, MethodRequest, MethodResult, CommerceRequest, CommerceResult, RecoveryRequest, RecoveryResult, ControlSnapshot = unknown> =
|
|
229
|
+
{
|
|
230
|
+
context: CustomerContext;
|
|
231
|
+
capabilities: AccountSupportCapabilities;
|
|
232
|
+
managedSubscription?: LegacySubscriptionPort<Request, PreviewRequest, Preview, Receipt>;
|
|
233
|
+
nativeLegacySubscription?: NativeLegacySubscriptionParts<Request, PreviewRequest, Preview, Receipt, ControlSnapshot>;
|
|
234
|
+
nativeRawSubscription?: RawSubscriptionCommands;
|
|
235
|
+
nativeSubscription?: {
|
|
236
|
+
apply(command: SubscriptionCommand): Promise<SubscriptionCommandReceipt | SubscriptionCommandRefusal | SubscriptionUnsupported>;
|
|
237
|
+
preview(command: Extract<SubscriptionCommand, { action: "edit_future_terms" }>): Promise<Record<string, unknown> | SubscriptionCommandRefusal>;
|
|
238
|
+
};
|
|
239
|
+
profile?: { updateProfile(userId: string, request: ProfileRequest): Promise<ProfileResult | null> };
|
|
240
|
+
paymentMethod?: { execute(userId: string, request: MethodRequest): Promise<MethodResult> };
|
|
241
|
+
commerce?: { execute(userId: string, request: CommerceRequest): Promise<CommerceResult> };
|
|
242
|
+
recovery?: { startPaymentRecovery(userId: string, request: RecoveryRequest): Promise<RecoveryResult | null> };
|
|
243
|
+
};
|