@stewardhq/sdk 0.1.0-rc.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.
Files changed (48) hide show
  1. package/README.md +437 -0
  2. package/dist/_chunks/errors.js +73 -0
  3. package/dist/_chunks/events.d.ts +537 -0
  4. package/dist/_chunks/events.js +350 -0
  5. package/dist/_chunks/index.d.ts +6005 -0
  6. package/dist/_chunks/locale.d.ts +17 -0
  7. package/dist/_chunks/locale.js +17 -0
  8. package/dist/_chunks/src.js +1202 -0
  9. package/dist/_chunks/steward.d.ts +2588 -0
  10. package/dist/_chunks/steward.js +578 -0
  11. package/dist/_chunks/validators.d.ts +23 -0
  12. package/dist/_chunks/validators.js +106 -0
  13. package/dist/_chunks/webhook-core.d.ts +32 -0
  14. package/dist/_chunks/webhook-core.js +64 -0
  15. package/dist/_chunks/webhook.d.ts +22 -0
  16. package/dist/_chunks/webhook.js +53 -0
  17. package/dist/contract.d.ts +4 -0
  18. package/dist/contract.js +4 -0
  19. package/dist/index.d.ts +268 -0
  20. package/dist/index.js +784 -0
  21. package/dist/server.d.ts +236 -0
  22. package/dist/server.js +384 -0
  23. package/dist/testing/fixtures/events/LOCK.json +27 -0
  24. package/dist/testing/fixtures/events/account.deleted.json +36 -0
  25. package/dist/testing/fixtures/events/account.state_changed.json +53 -0
  26. package/dist/testing/fixtures/events/account.updated.json +37 -0
  27. package/dist/testing/fixtures/events/checkout.completed.json +36 -0
  28. package/dist/testing/fixtures/events/checkout.expired.json +26 -0
  29. package/dist/testing/fixtures/events/checkout.failed.json +27 -0
  30. package/dist/testing/fixtures/events/invoice.created.json +37 -0
  31. package/dist/testing/fixtures/events/invoice.issued.json +38 -0
  32. package/dist/testing/fixtures/events/invoice.voided.json +39 -0
  33. package/dist/testing/fixtures/events/subscription.activated.json +40 -0
  34. package/dist/testing/fixtures/events/subscription.cancel_scheduled.json +38 -0
  35. package/dist/testing/fixtures/events/subscription.canceled.json +29 -0
  36. package/dist/testing/fixtures/events/subscription.expired.json +27 -0
  37. package/dist/testing/fixtures/events/subscription.payment_failed.json +38 -0
  38. package/dist/testing/fixtures/events/subscription.reactivated.json +36 -0
  39. package/dist/testing/fixtures/events/subscription.renewed.json +39 -0
  40. package/dist/testing/fixtures/events/subscription.suspended.json +36 -0
  41. package/dist/testing/fixtures/events/subscription.terminated.json +28 -0
  42. package/dist/testing.d.ts +629 -0
  43. package/dist/testing.js +3759 -0
  44. package/dist/webhook/web.d.ts +17 -0
  45. package/dist/webhook/web.js +66 -0
  46. package/dist/webhook.d.ts +3 -0
  47. package/dist/webhook.js +3 -0
  48. package/package.json +70 -0
@@ -0,0 +1,629 @@
1
+ import { $t as EventRecord, Cr as CatalogSyncInput, Dr as FeatureDef, Ft as ErrorCode, Gn as PortalSessionCreateInputSchema, Gt as CancelSubscriptionResponse, Pn as GrantCreateInputSchema, Qn as CheckoutPrefill, Rn as Stats, Tr as CatalogSyncResult, Un as PortalSession, Ut as BillingProfileResponse, Vt as AccountUpsertResponse, Wr as EntitlementValue, Zt as EventList, ar as CheckoutSessionCreateParsed, cn as InvoiceIssueResponse, dr as Consent, ei as Interval, gn as InvoiceIssueInputSchema, gt as PlanSetInputSchema, jn as CancelSubscriptionInputSchema, mn as Invoice, nr as CheckoutSession, nt as ProductSettingsPatch, or as CheckoutSessionCreated, pr as LegacyCheckoutSessionCreateParsed, qt as CheckoutSessionResponse, tn as GrantCreateResponse, tt as ProductSettings, wr as CatalogSyncInputSchema, wt as ResyncResult, yr as AccountsRefreshResult, yt as PlanSetResponse, zt as AccountDetail } from "./_chunks/index.js";
2
+ import { B as AccountUpsertInputSchema, C as AccountDeleteResponse, D as AccountInclude, F as AccountState, V as BillingProfile, a as BillingEvent, f as EventType, o as BillingEventOf, q as SubscriptionStatus, u as EventDetail } from "./_chunks/events.js";
3
+ import { a as Steward, c as StewardOptions } from "./_chunks/steward.js";
4
+ import { z } from "zod";
5
+ //#endregion
6
+ //#region src/testing/world.d.ts
7
+ type Ms = number;
8
+ type DunningAction = "retry_payment" | "suspend" | "terminate";
9
+ /** Servisin `products.dunning_policy` şekli. */
10
+ type FakeDunningPolicy = {
11
+ readonly steps: readonly {
12
+ readonly afterDays: number;
13
+ readonly action: DunningAction;
14
+ }[];
15
+ /** true (servisin varsayılanı): adımlar yalnızca sayılır, askı/sonlandırma yok. */
16
+ readonly dryRun: boolean;
17
+ };
18
+ /** Servisin `DEFAULT_DUNNING_POLICY` kopyası (`domain/dunning-policy.ts`). */
19
+ export declare const DEFAULT_DUNNING_POLICY: FakeDunningPolicy;
20
+ type PlanRow = {
21
+ code: string;
22
+ name: string;
23
+ rank: number;
24
+ sellable: boolean;
25
+ active: boolean;
26
+ entitlements: Record<string, EntitlementValue>;
27
+ };
28
+ type PriceRow = {
29
+ id: string;
30
+ planCode: string;
31
+ interval: Interval;
32
+ intervalCount: number;
33
+ currency: string;
34
+ amountMinor: number;
35
+ taxInclusive: boolean;
36
+ taxRateBps: number;
37
+ active: boolean;
38
+ createdAt: Ms;
39
+ };
40
+ type AccountRow = {
41
+ ref: string;
42
+ displayName: string;
43
+ metadata: Record<string, string>;
44
+ createdAt: Ms;
45
+ version: number;
46
+ /** Saklı durum; ilk commit'e kadar null. */
47
+ state: AccountState | null;
48
+ /** Yazılan her durum (version sırasıyla); `simulate.deliver({outOfOrder})` eski durumu buradan alır. */
49
+ history: AccountState[];
50
+ profile: BillingProfile | null;
51
+ /** KVKK anonimleştirme anı (F4e, `deleteAccount`); doluysa hesap yolunun uçları 410. */
52
+ deletedAt?: Ms;
53
+ };
54
+ type SubscriptionRow = {
55
+ id: string;
56
+ accountRef: string;
57
+ priceId: string;
58
+ providerSubscriptionRef: string;
59
+ status: SubscriptionStatus;
60
+ cancelAtPeriodEnd: boolean;
61
+ cancelAt: Ms | null;
62
+ cancelReason: string | null;
63
+ canceledAt: Ms | null;
64
+ startedAt: Ms;
65
+ currentPeriodStart: Ms | null;
66
+ currentPeriodEnd: Ms | null;
67
+ dunningStartedAt: Ms | null;
68
+ pastDueSince: Ms | null;
69
+ dunningStep: number;
70
+ nextDunningAt: Ms | null;
71
+ /**
72
+ * Sağlayıcıya giden son "ödemeyi yeniden dene" (portal ya da dunning adımı). Servis bunu
73
+ * denetim izinden okur (`lastPaymentRetryAt`); portal sıklık sınırı (F3a) buna bakar.
74
+ */
75
+ lastPaymentRetryAt: Ms | null;
76
+ };
77
+ /** Hosted müşteri portalı oturumu (F3a; servisin `portal_sessions`). Satır değişmez; süresi geçince silinir. */
78
+ type PortalSessionRow = {
79
+ id: string;
80
+ accountRef: string;
81
+ /** Sayfa anahtarı (`url`deki `k`; sahtede düz saklanır, servis yalnızca sha256'sını). */
82
+ pageKey: string;
83
+ locale: "tr" | "en";
84
+ /** "Ürüne dön"; verilmediyse null (sayfa `settings.appUrl`e döner). */
85
+ returnUrl: string | null;
86
+ actorRef: string;
87
+ expiresAt: Ms;
88
+ createdAt: Ms;
89
+ };
90
+ type SessionRow = {
91
+ id: string;
92
+ accountRef: string;
93
+ priceId: string;
94
+ status: CheckoutSession["status"];
95
+ locale: string;
96
+ returnUrl: string;
97
+ /** Hosted oturum (F2b): başarı adresi (yer tutucularıyla); eski gövdede null. */
98
+ successUrl: string | null;
99
+ /** Hosted: "Vazgeç" hedefi; verilmediyse null (dönüş `settings.appUrl`). */
100
+ cancelUrl: string | null;
101
+ /** Hosted: onaylatılacak belgeler (eski gövdede boş). */
102
+ consentDocs: {
103
+ document: string;
104
+ version: string;
105
+ url: string;
106
+ }[];
107
+ /** Hosted: ürünün ön doldurması; oturum kapanınca silinir (servis K21). */
108
+ prefill: CheckoutPrefill | null;
109
+ /** Hosted sayfa anahtarı (`url`deki `k`; sahtede düz saklanır); oturum kapanınca null. */
110
+ pageKey: string | null;
111
+ consents: Consent[];
112
+ actorRef: string | null;
113
+ failureReason: string | null;
114
+ subscriptionId: string | null;
115
+ expiresAt: Ms;
116
+ createdAt: Ms;
117
+ completedAt: Ms | null;
118
+ /** Sahte sağlayıcı formu; hosted oturumda sahte sayfanın `details` adımı olmadığından null. */
119
+ providerToken: string | null;
120
+ };
121
+ type PaymentRow = {
122
+ id: string;
123
+ subscriptionId: string;
124
+ providerOrderRef: string;
125
+ status: "succeeded" | "failed";
126
+ attempt: number;
127
+ amountMinor: number;
128
+ currency: string;
129
+ periodStart: Ms | null;
130
+ periodEnd: Ms | null;
131
+ occurredAt: Ms;
132
+ createdAt: Ms;
133
+ };
134
+ type InvoiceRow = {
135
+ id: string;
136
+ accountRef: string;
137
+ subscriptionId: string | null;
138
+ paymentId: string | null;
139
+ status: Invoice["status"];
140
+ number: string | null;
141
+ currency: string;
142
+ subtotalMinor: number;
143
+ taxMinor: number;
144
+ totalMinor: number;
145
+ taxRateBps: number;
146
+ buyer: BillingProfile;
147
+ lines: Invoice["lines"];
148
+ externalRef: string | null;
149
+ issuedAt: Ms | null;
150
+ createdAt: Ms;
151
+ document: {
152
+ bytes: Uint8Array;
153
+ sha256: string;
154
+ } | null;
155
+ /** Manuel fatura (F4f); ödemeli faturada null. */
156
+ dueAt?: Ms | null;
157
+ note?: string | null;
158
+ voidedAt?: Ms | null;
159
+ voidReason?: string | null;
160
+ };
161
+ type GrantRow = {
162
+ id: string;
163
+ accountRef: string;
164
+ planCode: string | null;
165
+ overrides: Record<string, EntitlementValue>;
166
+ reason: string;
167
+ actorRef: string | null;
168
+ startsAt: Ms;
169
+ endsAt: Ms | null;
170
+ revokedAt: Ms | null;
171
+ createdAt: Ms;
172
+ startApplied: boolean;
173
+ endApplied: boolean;
174
+ };
175
+ type DeliveryRow = {
176
+ status: "pending" | "delivered" | "dead";
177
+ attempts: number;
178
+ lastHttpStatus: number | null;
179
+ lastError: string | null;
180
+ nextAttemptAt: Ms;
181
+ deliveredAt: Ms | null;
182
+ };
183
+ /** Yönetim ucuyla açılan endpoint (`admin.ts`): yalnızca kayıt, teslimat yok; sır saklanmaz. */
184
+ type AdminEndpointRow = {
185
+ id: string;
186
+ url: string;
187
+ status: "active" | "inactive";
188
+ eventTypes: EventType[];
189
+ description: string | null;
190
+ createdAt: Ms;
191
+ rotatedAt: Ms | null;
192
+ };
193
+ type EventRow = {
194
+ id: string;
195
+ accountRef: string;
196
+ type: EventType;
197
+ /** Servisin outbox'taki gövdesi: eski tiplerde de `data.account` tam durum. */
198
+ payload: BillingEvent;
199
+ /** Endpoint yoksa ya da filtresi tipi içermiyorsa null. */
200
+ delivery: DeliveryRow | null;
201
+ };
202
+ type World = {
203
+ readonly productCode: string;
204
+ /** Ürün ayarları (`GET/PATCH /v1/admin/settings`; checkout TTL ve dönüş origin'i buradan). */
205
+ settings: ProductSettings;
206
+ /**
207
+ * true: dönüş origin'i ayarlanana kadar (`appUrl`/`extraReturnOrigins` boş ve yamada
208
+ * verilmedi) her `returnUrl` kabul — sahtenin kolaylığı; servis ayar ister.
209
+ */
210
+ permissiveReturnOrigins: boolean;
211
+ adminEndpoints: AdminEndpointRow[];
212
+ readonly dunningPolicy: FakeDunningPolicy;
213
+ /** Endpoint'in aldığı tipler; webhook yoksa null. */
214
+ readonly endpoint: {
215
+ readonly id: string;
216
+ readonly types: ReadonlySet<string>;
217
+ } | null;
218
+ features: FeatureDef[];
219
+ plans: Map<string, PlanRow>;
220
+ prices: PriceRow[];
221
+ defaultPlanCode: string | null;
222
+ accounts: Map<string, AccountRow>;
223
+ subscriptions: SubscriptionRow[];
224
+ sessions: SessionRow[];
225
+ portalSessions: PortalSessionRow[];
226
+ payments: PaymentRow[];
227
+ invoices: InvoiceRow[];
228
+ grants: GrantRow[];
229
+ events: EventRow[];
230
+ now(): Ms;
231
+ /** Saati ileri alır (geri almaz). */
232
+ setNow(ms: Ms): void;
233
+ /** Saate göre monoton UUIDv7. */
234
+ id(): string;
235
+ };
236
+ declare namespace operations_d_exports {
237
+ export { Failure, PORTAL_CANCEL_REASON, PORTAL_INVOICE_LIMIT, PORTAL_RETRY_INTERVAL_MS, PortalActionResult, PortalView, Result, accountDetail, advanceTo, applyRenewalPayment, cancelCheckout, cancelSubscription, checkoutSessionResponse, completeCheckout, createCheckoutSession, createGrant, createHostedCheckoutSession, createPortalSession, customerActorRef, deleteAccount, failCheckout, hostedPageUrl, hostedPortalUrl, issueInvoice, listEvents, livePortalSession, openHostedSession, openPortalSession, portalCancel, portalInvoiceDocument, portalRetryPayment, portalView, putBillingProfile, redeliverEvent, refreshAccounts, resyncAccount, revokeGrant, sessionRow, setPlan, stats, syncCatalog, updatePortalProfile, upsertAccount, validatePortalProfile };
238
+ }
239
+ type Failure = {
240
+ ok: false;
241
+ code: ErrorCode;
242
+ message?: string;
243
+ };
244
+ type Result<T> = {
245
+ ok: true;
246
+ value: T;
247
+ } | Failure;
248
+ type CatalogInput = z.output<typeof CatalogSyncInputSchema>;
249
+ type UpsertInput = z.output<typeof AccountUpsertInputSchema>;
250
+ /** Eski gövde (K16); hosted gövde `HostedCheckoutInput` (http.ts `isLegacyCheckoutInput` ile ayırır). */
251
+ type CheckoutInput = LegacyCheckoutSessionCreateParsed;
252
+ type HostedCheckoutInput = CheckoutSessionCreateParsed;
253
+ type CancelInput = z.output<typeof CancelSubscriptionInputSchema>;
254
+ type GrantInput = z.output<typeof GrantCreateInputSchema>;
255
+ type IssueInput = z.output<typeof InvoiceIssueInputSchema>;
256
+ type PlanSetInput = z.output<typeof PlanSetInputSchema>;
257
+ /** `syncCatalogWithProvider`: K4 tamlığı, farkın uygulanması, etkilenen hesapların yenilenmesi. */
258
+ declare function syncCatalog(w: World, input: CatalogInput): Result<CatalogSyncResult>;
259
+ /** `refreshAccounts` (`POST /v1/admin/accounts/refresh`). */
260
+ declare function refreshAccounts(w: World): AccountsRefreshResult;
261
+ declare function upsertAccount(w: World, ref: string, input: UpsertInput): {
262
+ account: AccountUpsertResponse;
263
+ created: boolean;
264
+ };
265
+ declare function putBillingProfile(w: World, ref: string, profile: BillingProfile): Result<BillingProfileResponse>;
266
+ declare function accountDetail(w: World, ref: string, include: readonly AccountInclude[]): AccountDetail | null;
267
+ /** Hosted checkout sayfası (servisin `hostedCheckoutUrl`); sahtede `hosted.ts` sunar. */
268
+ declare const hostedPageUrl: (baseUrl: string, id: string, pageKey: string) => string;
269
+ declare function createCheckoutSession(w: World, baseUrl: string, ref: string, input: CheckoutInput, actorRef: string): Result<CheckoutSessionCreated>;
270
+ /**
271
+ * `createHostedCheckoutSession` (hosted gövde, F2b): sağlayıcıya gidilmez; doğrulama sırası ve
272
+ * kodları servisle aynı (hesap, dönüş origin'leri, canlı abonelik, satılabilir fiyat). Hesap
273
+ * yoksa `displayName` ile açılır. `url` sahte steward'ın hosted sayfasıdır (`hosted.ts`).
274
+ */
275
+ declare function createHostedCheckoutSession(w: World, baseUrl: string, ref: string, input: HostedCheckoutInput, actorRef: string): Result<CheckoutSessionCreated>;
276
+ declare function checkoutSessionResponse(w: World, id: string): CheckoutSessionResponse | null;
277
+ declare function sessionRow(w: World, id: string): SessionRow;
278
+ /** Hosted oturumun sayfası açık mı: anahtar bu oturumun, oturum açık ve süresi geçmemiş (servis `openSession`). */
279
+ declare function openHostedSession(w: World, id: string, pageKey: string): SessionRow | null;
280
+ /** `completeCheckoutReturn` başarısızlık yolu: yalnızca `open` oturum kapanır. */
281
+ declare function failCheckout(w: World, session: SessionRow, reason: string): void;
282
+ /**
283
+ * Hosted sayfada vazgeç (`HostedCheckoutPort.cancel`; sahte sayfada sağlayıcı formu olmadığından
284
+ * her zaman kapanır): yalnızca açık oturum; `canceled`, iç neden `checkout.canceled`. Dönüş
285
+ * adresi `cancelUrl ?? settings.appUrl` (yoksa null). Açık olmayan oturumda değişiklik yok.
286
+ */
287
+ declare function cancelCheckout(w: World, session: SessionRow): string | null;
288
+ /** `completeCheckoutReturn` başarı yolu: abonelik, ilk tahsilat, event'ler, taslak fatura. */
289
+ declare function completeCheckout(w: World, session: SessionRow): void;
290
+ declare function issueInvoice(w: World, id: string, input: IssueInput): Result<InvoiceIssueResponse>;
291
+ declare function cancelSubscription(w: World, id: string, input: CancelInput, actorRef: string): Result<CancelSubscriptionResponse>;
292
+ declare function createGrant(w: World, ref: string, input: GrantInput, actorRef: string): Result<GrantCreateResponse>;
293
+ /** Zaten iptal edilmiş grant: idempotent başarı. */
294
+ declare function revokeGrant(w: World, id: string): boolean;
295
+ /**
296
+ * `setPlan` (K6): aktif plan grant'leri (henüz başlamamışlar dahil; override
297
+ * grant'leri hariç) iptal + yeni plan grant'i + `commitAccountChange(grant.plan_set)`.
298
+ * Tek aktif plan grant'i aynı plan/`endsAt`/`overrides` ise korunur. Canlı abonelik
299
+ * planı hak kuralında hedefi geçiyorsa `strict` → `subscription_plan_higher` (yazım
300
+ * yok), `ensure` → uyarı. Sahtede eşzamanlılık yok (servis advisory lock'la sıralar).
301
+ */
302
+ declare function setPlan(w: World, ref: string, input: PlanSetInput, actorRef: string): Result<PlanSetResponse>;
303
+ /** `resyncAccount`: saklı durum `account.state_changed` (`cause: "resync"`) olarak; version aynı. */
304
+ declare function resyncAccount(w: World, ref: string): Result<ResyncResult>;
305
+ /**
306
+ * `deleteAccount` (`domain/account-deletion.ts`, F4e): önkoşullar sırayla (hesap yok 404,
307
+ * silinmiş 410, canlı abonelik, formu açık checkout — sahte steward'da yalnızca eski
308
+ * gövdeli oturumun formu vardır) ve hiçbiri tutmazsa değişiklik yok. Sonra anonimleştirme:
309
+ * ad/metadata/profil, oturumların ön doldurması/anahtarı ve onay izinin `ipHash`/`userAgent`'ı,
310
+ * portal oturumları, aktif grant'ler; eski event yüklerinde `displayName`. Faturalar kalır.
311
+ */
312
+ declare function deleteAccount(w: World, ref: string, actorRef: string): Result<AccountDeleteResponse>;
313
+ declare function stats(w: World): Stats;
314
+ /** Portal sayfası (servisin `hostedPortalUrl`); sahtede `portal.ts` sunar. */
315
+ declare const hostedPortalUrl: (baseUrl: string, id: string, pageKey: string) => string;
316
+ declare const PORTAL_CANCEL_REASON = "customer_portal";
317
+ /** Abonelik başına portal "yeniden dene" aralığı (sağlayıcıya giden son retry'dan itibaren; servisle aynı). */
318
+ declare const PORTAL_RETRY_INTERVAL_MS: number;
319
+ declare const PORTAL_INVOICE_LIMIT = 24;
320
+ /** Portal aksiyonlarının aktörü (servisin `customerActorRef`). */
321
+ declare const customerActorRef: (accountRef: string) => string;
322
+ /**
323
+ * Portal aksiyonunun sonucu: `ok` ya da servisin portal portundaki `not_allowed` nedeni
324
+ * (değişiklik yok). Sahte sağlayıcı hata vermediği için `provider_error` yok.
325
+ */
326
+ type PortalActionResult = "ok" | "no_subscription" | "already_scheduled" | "nothing_to_retry" | "no_failed_payment" | "retry_too_soon";
327
+ type PortalSessionInput = z.output<typeof PortalSessionCreateInputSchema>;
328
+ /**
329
+ * `createPortalSession`: hesap yoksa `account_not_found` (checkout'taki gibi açılmaz), `returnUrl`
330
+ * origin'i izinli değilse `return_url_not_allowed`; dil verilmezse `defaultLocale`, süre
331
+ * `portalTtlMinutes`. Anahtar 32 bayt base64url. Durum değişmez, event yok.
332
+ */
333
+ declare function createPortalSession(w: World, baseUrl: string, ref: string, input: PortalSessionInput, actorRef: string): Result<PortalSession>;
334
+ /** Süresi geçmemiş portal oturumu (anahtarsız; `simulate`). Yoksa null. */
335
+ declare function livePortalSession(w: World, id: string): PortalSessionRow | null;
336
+ /** Servisin `validSession`'ı: bilinmeyen id, yanlış anahtar ve süresi geçmiş oturum AYNI sonuç (null). */
337
+ declare function openPortalSession(w: World, id: string, pageKey: string): PortalSessionRow | null;
338
+ type PortalView = {
339
+ session: PortalSessionRow;
340
+ account: AccountRow;
341
+ state: AccountState;
342
+ planName: string;
343
+ subscription: {
344
+ row: SubscriptionRow;
345
+ price: PriceRow;
346
+ planName: string;
347
+ canCancel: boolean;
348
+ canRetryPayment: boolean;
349
+ } | null;
350
+ /** En yeni önce, en çok 24 (ödemeli taslak dahil; manuel taslak yok). */
351
+ invoices: InvoiceRow[];
352
+ /** `returnUrl ?? settings.appUrl`; yoksa null. */
353
+ returnUrl: string | null;
354
+ };
355
+ /** Servisin portal `load` görünümü (sayfa için). */
356
+ declare function portalView(w: World, session: PortalSessionRow): PortalView;
357
+ /**
358
+ * Portal iptali (`HostedPortalPort.cancelAtPeriodEnd`): canlı abonelik dönem sonunda
359
+ * (`cancelSubscription`, neden `customer_portal`, aktör `customer:<ref>`; `/v1` iptaliyle aynı
360
+ * kurallar ve event'ler). Zaten planlıysa `already_scheduled` (iz/event yok).
361
+ */
362
+ declare function portalCancel(w: World, session: PortalSessionRow): PortalActionResult;
363
+ /**
364
+ * Portal "ödemeyi yeniden dene" (`HostedPortalPort.retryPayment`): past_due/suspended abonelikte
365
+ * son başarısız sipariş sahte sağlayıcıya gönderilmiş sayılır — DURUM DEĞİŞMEZ, event yok;
366
+ * tahsilatın sonucu servisteki gibi sonradan gelir (`simulate.renewal(ref)` aynı siparişi
367
+ * başarılı/`fail` sayar). Abonelik başına 10 dakikada bir (portal ya da dunning retry'ı).
368
+ */
369
+ declare function portalRetryPayment(w: World, session: PortalSessionRow): PortalActionResult;
370
+ /**
371
+ * Servisin `validateProfile`'ı (hosted-checkout.ts `checkProfile`): alan anahtarları
372
+ * `profile.<alan>`, alan başına ilk kod; öncelik required > invalid_kind > invalid_characters >
373
+ * şema kodları > VKN/E.164. Değer koda girmez.
374
+ */
375
+ declare function validatePortalProfile(input: Record<string, unknown>): {
376
+ ok: true;
377
+ profile: BillingProfile;
378
+ } | {
379
+ ok: false;
380
+ fieldErrors: Record<string, string>;
381
+ };
382
+ /**
383
+ * Portal profil güncellemesi (`HostedPortalPort.updateProfile`): ülke verilmezse saklı değer
384
+ * korunur (yoksa TR); bireyselde kurumsal alanlar düşer. Durumda yalnızca profilin varlığı ve
385
+ * türü olduğundan event yalnızca bunlar değişince (`billing_profile.updated`).
386
+ */
387
+ declare function updatePortalProfile(w: World, session: PortalSessionRow, input: Record<string, unknown>): {
388
+ ok: true;
389
+ } | {
390
+ ok: false;
391
+ fieldErrors: Record<string, string>;
392
+ };
393
+ /** Portal belge indirme: yalnızca oturumun hesabının kesilmiş ve belgeli faturası; yoksa null. */
394
+ declare function portalInvoiceDocument(w: World, session: PortalSessionRow, invoiceId: string): {
395
+ bytes: Uint8Array;
396
+ filename: string;
397
+ } | null;
398
+ declare function listEvents(w: World, query: {
399
+ accountRef?: string;
400
+ cursor?: string;
401
+ limit: number;
402
+ }): Result<EventList>;
403
+ declare function redeliverEvent(w: World, id: string): EventRecord | null;
404
+ /**
405
+ * Tahsilat sonucu. Dunning'deki abonelikte AYNI başarısız siparişin yeniden
406
+ * denemesidir (iyzico retry'ı; servisin `existingPayment` yolu), aksi halde
407
+ * mevcut dönem sonundan başlayan yeni sipariş.
408
+ */
409
+ declare function applyRenewalPayment(w: World, sub: SubscriptionRow, outcome: "succeeded" | "failed"): void;
410
+ /** Saati `target`a ilerletir; her zamanlanmış iş kendi anında koşar. */
411
+ declare function advanceTo(w: World, target: Ms): void;
412
+ //#endregion
413
+ //#region src/testing/fake-steward.d.ts
414
+ /** Sahte steward'ın teslimat yaptığı ürün alıcısı. */
415
+ type FakeWebhookHandler = (req: Request) => Response | Promise<Response>;
416
+ type FakeWebhookOptions = {
417
+ /** İmza sırrı; dizi verilirse rotasyondaki gibi hepsiyle imzalanır. */
418
+ secret: string | readonly string[];
419
+ /** Ürünün alıcısı, ör. `(req) => webhooks.handle(req)`. Verilmezse `fake.connect(handler)` ile sonradan. */
420
+ handler?: FakeWebhookHandler;
421
+ /**
422
+ * Endpoint filtresi. Verilmezse `["account.state_changed"]` (yönetim ucunun yeni ürüne
423
+ * varsayılanı); `"legacy"`: `LEGACY_EVENT_TYPES` (pod CLI `add-endpoint`'in NULL filtresi, 15 eski tip).
424
+ */
425
+ eventTypes?: readonly EventType[] | "legacy";
426
+ /** İsteğin adresi (yalnızca `Request.url`). */
427
+ url?: string;
428
+ };
429
+ type FakeStewardOptions = {
430
+ /** Oluşturmada senkronlanan katalog. Verilmezse katalog uçları `catalog_not_configured`, hesap açılışı 500 (servis gibi). */
431
+ catalog?: CatalogSyncInput | {
432
+ toSyncInput(): CatalogSyncInput;
433
+ };
434
+ /** Ürün kapsamlı anahtar; verilmezse `stw_…` biçiminde üretilir. */
435
+ apiKey?: string;
436
+ /** Admin kapsamlı anahtar; verilmezse üretilir. */
437
+ adminApiKey?: string;
438
+ /** Varsayılan `http://steward.test`. Bu tabanın dışındaki istekler ağ hatası alır. */
439
+ baseUrl?: string;
440
+ /** Event'lerin `product` alanı ve `/v1/me`. Varsayılan `example`. */
441
+ productCode?: string;
442
+ /** Sahte saatin kaynağı; `simulate.advance`/`renewal` bunun üstüne ekler. Varsayılan gerçek saat. */
443
+ now?: () => Date;
444
+ webhook?: FakeWebhookOptions;
445
+ /**
446
+ * Başlangıç ürün ayarları (`PATCH /v1/admin/settings` yamasıyla aynı biçim). Sağlayıcı
447
+ * her zaman sahte (`provider: "fake"`). Dönüş origin'i (`appUrl`, `extraReturnOrigins`,
448
+ * `returnOrigins`) hiç verilmez ve yamalanmazsa her `returnUrl` kabul edilir.
449
+ */
450
+ settings?: ProductSettingsPatch;
451
+ /** Checkout `returnUrl` origin listesi = `settings.extraReturnOrigins` (ikisi birlikte verilemez). */
452
+ returnOrigins?: readonly string[];
453
+ /** = `settings.checkoutTtlMinutes`; varsayılan 30 (sahte sağlayıcının formu da 30 dk). */
454
+ checkoutTtlMinutes?: number;
455
+ /** Varsayılan servisinki: `dryRun: true` (adımlar sayılır, askı/sonlandırma yok). */
456
+ dunningPolicy?: FakeDunningPolicy;
457
+ /** `fake.steward` ek ayarları (bağlantı ayarları sahteden gelir). */
458
+ steward?: Omit<StewardOptions, "baseUrl" | "apiKey" | "adminBaseUrl" | "adminApiKey" | "fetch" | "env">;
459
+ /** 500'e dönüşen iç hatalar (servis gibi). Varsayılan `console`. */
460
+ logger?: {
461
+ error(message: string): void;
462
+ };
463
+ };
464
+ /** Sahteye gelen istek (sırasıyla). `status` 0: ağ hatası (kesinti ya da taban dışı adres). */
465
+ type FakeRequest = {
466
+ method: string;
467
+ url: string;
468
+ /** Tabana göre yol + sorgu, ör. `/v1/accounts/org_1/state`. */
469
+ path: string;
470
+ headers: Record<string, string>;
471
+ body: string | null;
472
+ status: number;
473
+ };
474
+ type FakeDelivery = {
475
+ event: BillingEvent;
476
+ body: string;
477
+ headers: Record<string, string>;
478
+ response: Response;
479
+ };
480
+ type FakeDeliverOptions = {
481
+ ref: string;
482
+ /** `account.state_changed` için `detail.cause`; varsayılan `account.refreshed`. */
483
+ cause?: string;
484
+ /** Aynı event (aynı id ve gövde) ikinci kez. */
485
+ duplicate?: boolean;
486
+ /** Güncel event'ten sonra bir önceki version'ın durumuyla eski bir event. */
487
+ outOfOrder?: boolean;
488
+ };
489
+ /** `simulate.portalCancel/portalRetryPayment` sonucu. */
490
+ type FakePortalActionResult = {
491
+ /**
492
+ * `ok` ya da servisin portal portundaki `not_allowed` nedeni (hiçbir şey değişmedi):
493
+ * `no_subscription`, `already_scheduled` (iptal), `nothing_to_retry` / `no_failed_payment` /
494
+ * `retry_too_soon` (yeniden dene). Sayfa bunları `error=` koduna eşler.
495
+ */
496
+ result: PortalActionResult;
497
+ /** İşlem sonrası hesap durumu. */
498
+ account: AccountState;
499
+ };
500
+ /** `simulate.completeHostedCheckout` sonucu. */
501
+ type FakeHostedCheckoutResult = {
502
+ /** Oturum id'si (`url`'den). */
503
+ id: string;
504
+ /** Sayfanın 303 yönlendirdiği adres: `successUrl`, yer tutucular servisle aynı kuralla dolu. */
505
+ redirectUrl: string;
506
+ /** İşlem sonrası oturum (`completed` ya da `failed`; `completed`'da `account`). */
507
+ session: CheckoutSessionResponse;
508
+ };
509
+ type FakeSimulate = {
510
+ /**
511
+ * Sağlayıcı formu ödendi: abonelik, ilk tahsilat, fatura taslağı, event'ler. Açık olmayan
512
+ * oturumda değişiklik yok. Hosted oturumda önce sayfanın `details` izi yazılır (profil yoksa
513
+ * ön doldurmadan sahte profil, onay izi; `billing_profile.updated`).
514
+ */
515
+ checkoutCompleted(sessionId: string): Promise<CheckoutSessionResponse>;
516
+ /** Kart reddedildi (`fake:card_declined`). */
517
+ checkoutFailed(sessionId: string, options?: {
518
+ reason?: string;
519
+ }): Promise<CheckoutSessionResponse>;
520
+ /**
521
+ * Hosted checkout'u açılış yanıtındaki `url`'den tarayıcı gibi sürer (F2b): sahte sayfayı açar
522
+ * (`fake.fetch`, `fake.requests`'e yazılır), "Tamamla" (`outcome: "succeeded"`, varsayılan) ya da
523
+ * "Reddet" gönderir, 303 dönüş adresini verir. Sonuç `checkoutCompleted/Failed` ile aynıdır.
524
+ * Sayfa 404 (bilinmeyen, kapalı ya da süresi geçmiş oturum, yanlış anahtar) ya da kesinti fırlatır.
525
+ */
526
+ completeHostedCheckout(url: string, options?: {
527
+ outcome?: "succeeded" | "failed";
528
+ }): Promise<FakeHostedCheckoutResult>;
529
+ /**
530
+ * Müşteri hosted sayfada vazgeçti (F2b): oturum `canceled`, iç neden `checkout.canceled`
531
+ * (eski tipli event yok). Yalnızca hosted oturum; açık olmayan oturumda değişiklik yok.
532
+ */
533
+ checkoutCanceled(sessionId: string): Promise<CheckoutSessionResponse>;
534
+ /**
535
+ * Yenileme tahsilatı. Saat önce `advanceDays` kadar, verilmezse dönem sonuna
536
+ * ilerler (arada zamanlanmış işler koşar). Başarı → `subscription.renewed`
537
+ * (+fatura; dunning'deyse `subscription.reactivated`), `fail` →
538
+ * `subscription.payment_failed`, `past_due`, dunning başlar. Dunning'deki
539
+ * abonelikte aynı başarısız siparişin yeniden denemesidir.
540
+ */
541
+ renewal(ref: string, options?: {
542
+ advanceDays?: number;
543
+ fail?: boolean;
544
+ }): Promise<AccountState>;
545
+ /**
546
+ * Müşteri portalda "Dönem sonunda iptal"i onayladı (F3a; sayfanın `cancel` + `confirm=1`
547
+ * adımıyla aynı işlem): canlı abonelik dönem sonunda iptal edilir — `subscriptions.cancel`
548
+ * (`at_period_end`) ile aynı kurallar ve event'ler (`subscription.cancel_scheduled`,
549
+ * `account.state_changed`), neden `customer_portal`, aktör `customer:<ref>`. Zaten planlıysa
550
+ * `already_scheduled`, abonelik yoksa `no_subscription` (değişiklik yok). Bilinmeyen ya da
551
+ * süresi geçmiş oturum fırlatır (sayfa 404 verirdi).
552
+ */
553
+ portalCancel(portalSessionId: string): Promise<FakePortalActionResult>;
554
+ /**
555
+ * Müşteri portalda "Ödemeyi yeniden dene"ye bastı (F3a): past_due/suspended abonelikte istek
556
+ * kabul edilir (`ok`) ama DURUM DEĞİŞMEZ — servisteki gibi tahsilatın sonucu sonradan gelir:
557
+ * `renewal(ref)` aynı başarısız siparişi başarılı sayar (`subscription.reactivated`),
558
+ * `renewal(ref, {fail: true})` yine başarısız. Abonelik başına 10 dakikada bir (`retry_too_soon`;
559
+ * saat `advance` ile ilerler); aktif abonelikte `nothing_to_retry`. Bilinmeyen ya da süresi
560
+ * geçmiş oturum fırlatır.
561
+ */
562
+ portalRetryPayment(portalSessionId: string): Promise<FakePortalActionResult>;
563
+ /** Saati ilerletir; her zamanlanmış iş kendi anında koşar. Süresiz çağrı yalnızca vadesi gelenleri koşar. */
564
+ advance(duration?: {
565
+ days?: number;
566
+ hours?: number;
567
+ minutes?: number;
568
+ }): Promise<void>;
569
+ /** `network`: fetch `TypeError("fetch failed")` ile reddeder; `503`: `database_unavailable` (tekrar denenebilir). */
570
+ outage(on: boolean, options?: {
571
+ mode?: "network" | "503";
572
+ }): void;
573
+ /** Hesabın GÜNCEL durumuyla `type` event'ini alıcıya gönderir (outbox'a yazılmaz, filtre uygulanmaz). */
574
+ deliver(type: EventType, options: FakeDeliverOptions): Promise<FakeDelivery[]>;
575
+ /** Bekleyen (teslim edilmemiş) outbox event'lerini sırayla alıcıya gönderir. */
576
+ flush(): Promise<FakeDelivery[]>;
577
+ };
578
+ type FakeSteward = {
579
+ readonly baseUrl: string;
580
+ readonly apiKey: string;
581
+ readonly adminApiKey: string;
582
+ readonly fetch: typeof fetch;
583
+ readonly steward: Steward;
584
+ readonly requests: readonly FakeRequest[];
585
+ readonly simulate: FakeSimulate;
586
+ /**
587
+ * Webhook alıcısını bağlar ya da değiştirir (`webhook.handler` yerine): sahte steward ürün
588
+ * uygulamasından önce kurulur, uygulama `fake.steward`/`fake.fetch` ile oluşturulup sonra bağlanır.
589
+ * `webhook` seçeneği (sır, filtre) verilmemişse fırlatır.
590
+ */
591
+ connect(handler: FakeWebhookHandler): void;
592
+ /** Yayınlanan event'ler, yayın sırasıyla (`GET /v1/events`'in içeriği; eski tiplerde de tam durum). */
593
+ events(ref?: string): readonly BillingEvent[];
594
+ /** Sahte saat. */
595
+ now(): Date;
596
+ };
597
+ export declare function createFakeSteward(options?: FakeStewardOptions): FakeSteward;
598
+ //#endregion
599
+ //#region src/testing/signed-event.d.ts
600
+ type SignedEventOverrides<T extends EventType = EventType> = {
601
+ id?: string;
602
+ apiVersion?: string;
603
+ createdAt?: string;
604
+ product?: string;
605
+ accountRef?: string;
606
+ data?: {
607
+ /** Fixture'ın hesabına sığ birleşir; tam `AccountState` verilebilir (F1+ sunucu eski tiplere de koyar). */
608
+ account?: Partial<AccountState>;
609
+ detail?: Partial<EventDetail<T>>;
610
+ };
611
+ };
612
+ type SignedEventOptions = {
613
+ /** İmza sırları (`yeni,eski` rotasyonu için dizi). Verilmezse `STEWARD_WEBHOOK_SECRET`. */
614
+ secret?: string | readonly string[];
615
+ /** Unix saniye; varsayılan şimdi. */
616
+ timestamp?: number;
617
+ };
618
+ type SignedEvent<T extends EventType = EventType> = {
619
+ event: BillingEventOf<T>;
620
+ /** Ham gövde (imzalanan metin). */
621
+ body: string;
622
+ /** İmza başlıkları + `content-type`. */
623
+ headers: Record<string, string>;
624
+ /** Alıcıya verilecek `POST` isteği (her çağrıda yeni `Request`). */
625
+ request(url?: string): Request;
626
+ };
627
+ export declare function signedEvent<T extends EventType>(type: T, overrides?: SignedEventOverrides<T>, options?: SignedEventOptions): SignedEvent<T>;
628
+ //#endregion
629
+ export type { FakeDeliverOptions, FakeDelivery, FakeDunningPolicy, FakeHostedCheckoutResult, FakePortalActionResult, FakeRequest, FakeSimulate, FakeSteward, FakeStewardOptions, FakeWebhookHandler, FakeWebhookOptions, SignedEvent, SignedEventOptions, SignedEventOverrides };