@voltro/plugin-economy 0.0.0-stage → 0.87.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 (177) hide show
  1. package/CHANGELOG.md +24329 -0
  2. package/LICENSE +61 -0
  3. package/README.md +2719 -2
  4. package/SECURITY.md +56 -0
  5. package/THIRD-PARTY-NOTICES.md +9189 -0
  6. package/dist/amount.js +21 -0
  7. package/dist/appleIap.server.js +263 -0
  8. package/dist/appleIapNotifications.server.js +163 -0
  9. package/dist/appleIapTrust.server.js +31 -0
  10. package/dist/bundleValue.server.js +113 -0
  11. package/dist/canonicalDataJson.js +11 -0
  12. package/dist/capture.js +54 -0
  13. package/dist/codePort.fixture.js +124 -0
  14. package/dist/codeStore.server.js +440 -0
  15. package/dist/codeTables.entity.js +44 -0
  16. package/dist/codes.d.ts +150 -0
  17. package/dist/codes.js +83 -0
  18. package/dist/components.d.ts +397 -0
  19. package/dist/components.js +284 -0
  20. package/dist/consistency.server.js +116 -0
  21. package/dist/containerStore.server.js +688 -0
  22. package/dist/containerStore.testFixtures.js +76 -0
  23. package/dist/containerTables.entity.js +46 -0
  24. package/dist/containers.d.ts +311 -0
  25. package/dist/containers.fixture.js +129 -0
  26. package/dist/containers.js +215 -0
  27. package/dist/containers.server.js +40 -0
  28. package/dist/containersPort.fixture.js +75 -0
  29. package/dist/currency.js +33 -0
  30. package/dist/declarationFixtures.js +62 -0
  31. package/dist/economy-app/events/economy.refundReversed.event.ts +1 -0
  32. package/dist/economy-app/events/economy.refunded.event.ts +1 -0
  33. package/dist/economyPort.fixture.js +125 -0
  34. package/dist/errors.d.ts +14 -0
  35. package/dist/errors.js +22 -0
  36. package/dist/events.d.ts +46 -0
  37. package/dist/events.js +1 -0
  38. package/dist/googleIap.server.js +392 -0
  39. package/dist/googleIapNotifications.server.js +131 -0
  40. package/dist/googleIapSubscription.server.js +184 -0
  41. package/dist/iap.d.ts +309 -0
  42. package/dist/iap.js +304 -0
  43. package/dist/iap.testFixtures.js +145 -0
  44. package/dist/iapAccountTables.entity.js +40 -0
  45. package/dist/iapAccounts.server.js +201 -0
  46. package/dist/iapBinding.server.js +524 -0
  47. package/dist/iapCheckout.server.js +98 -0
  48. package/dist/iapCompletion.server.js +56 -0
  49. package/dist/iapCurrentState.server.js +108 -0
  50. package/dist/iapCurrentState.testFixtures.js +23 -0
  51. package/dist/iapCurrentStatePort.fixture.js +64 -0
  52. package/dist/iapEntitlements.d.ts +79 -0
  53. package/dist/iapEntitlements.js +58 -0
  54. package/dist/iapEntitlements.server.js +49 -0
  55. package/dist/iapEntitlementsBinding.server.js +106 -0
  56. package/dist/iapEntitlementsPort.fixture.js +81 -0
  57. package/dist/iapInstalledEntitlements.testFixtures.js +76 -0
  58. package/dist/iapInstalledNotifications.testFixtures.js +94 -0
  59. package/dist/iapInventoryRefunds.server.js +262 -0
  60. package/dist/iapNotificationCustodyPort.fixture.js +68 -0
  61. package/dist/iapNotificationPort.fixture.js +74 -0
  62. package/dist/iapNotificationProcessor.server.js +135 -0
  63. package/dist/iapNotificationProcessor.testFixtures.js +64 -0
  64. package/dist/iapNotificationProcessorPort.fixture.js +56 -0
  65. package/dist/iapNotificationSchedule.server.js +70 -0
  66. package/dist/iapNotificationStore.server.js +353 -0
  67. package/dist/iapNotificationStore.testFixtures.js +32 -0
  68. package/dist/iapNotificationTables.entity.js +49 -0
  69. package/dist/iapNotificationTarget.server.js +37 -0
  70. package/dist/iapNotificationTarget.testFixtures.js +30 -0
  71. package/dist/iapNotifications.d.ts +644 -0
  72. package/dist/iapNotifications.js +6 -0
  73. package/dist/iapNotifications.server.js +96 -0
  74. package/dist/iapNotificationsBinding.server.js +233 -0
  75. package/dist/iapNotificationsSystemPort.fixture.js +51 -0
  76. package/dist/iapPlugin.testFixtures.js +60 -0
  77. package/dist/iapPort.fixture.js +75 -0
  78. package/dist/iapProviderCapture.server.js +18 -0
  79. package/dist/iapRecovery.d.ts +146 -0
  80. package/dist/iapRecovery.js +90 -0
  81. package/dist/iapRefundEventDelivery.server.js +83 -0
  82. package/dist/iapRefundEvents.js +45 -0
  83. package/dist/iapRefundEventsPort.fixture.js +60 -0
  84. package/dist/iapRefundPort.fixture.js +55 -0
  85. package/dist/iapRefundTables.entity.js +56 -0
  86. package/dist/iapRefunds.d.ts +755 -0
  87. package/dist/iapRefunds.js +6 -0
  88. package/dist/iapRefunds.server.js +554 -0
  89. package/dist/iapRefunds.testFixtures.js +65 -0
  90. package/dist/iapSpendAdmission.server.js +99 -0
  91. package/dist/iapStore.server.js +741 -0
  92. package/dist/iapStore.testFixtures.js +128 -0
  93. package/dist/iapTables.entity.js +81 -0
  94. package/dist/iapVerification.server.js +121 -0
  95. package/dist/index.d.ts +2984 -0
  96. package/dist/index.js +15 -0
  97. package/dist/intentionRecovery.js +134 -0
  98. package/dist/ledger.js +78 -0
  99. package/dist/loyalty.d.ts +274 -0
  100. package/dist/loyalty.fixture.js +93 -0
  101. package/dist/loyalty.js +312 -0
  102. package/dist/loyaltyBinding.server.js +204 -0
  103. package/dist/loyaltyPort.fixture.js +87 -0
  104. package/dist/loyaltyShopSource.server.js +91 -0
  105. package/dist/loyaltyStore.server.js +553 -0
  106. package/dist/loyaltyStore.testFixtures.js +83 -0
  107. package/dist/loyaltyTables.entity.js +50 -0
  108. package/dist/offerMerchandising.fixture.js +115 -0
  109. package/dist/offerMerchandising.js +283 -0
  110. package/dist/offerPolicies.d.ts +441 -0
  111. package/dist/offerPolicies.fixture.js +54 -0
  112. package/dist/offerPolicies.js +268 -0
  113. package/dist/piggyBanks.d.ts +272 -0
  114. package/dist/piggyBanks.fixture.js +71 -0
  115. package/dist/piggyBanks.js +201 -0
  116. package/dist/piggyBinding.server.js +205 -0
  117. package/dist/piggyPort.fixture.js +73 -0
  118. package/dist/piggyStore.server.js +611 -0
  119. package/dist/piggyStore.testFixtures.js +98 -0
  120. package/dist/piggyTables.entity.js +41 -0
  121. package/dist/plugin.js +1048 -0
  122. package/dist/port.js +13 -0
  123. package/dist/price.d.ts +99 -0
  124. package/dist/price.js +144 -0
  125. package/dist/purchaseRecovery.js +56 -0
  126. package/dist/reports.server.js +115 -0
  127. package/dist/rpc.d.ts +3201 -0
  128. package/dist/rpc.js +1201 -0
  129. package/dist/sanctionsTradeAuthority.server.js +80 -0
  130. package/dist/server.d.ts +3856 -0
  131. package/dist/server.js +43 -0
  132. package/dist/shop.d.ts +279 -0
  133. package/dist/shop.js +196 -0
  134. package/dist/shop.testFixtures.js +114 -0
  135. package/dist/shopPort.fixture.js +72 -0
  136. package/dist/shopPresentation.js +68 -0
  137. package/dist/shopStore.server.js +624 -0
  138. package/dist/shopTables.entity.js +63 -0
  139. package/dist/shopUiCopy.js +57 -0
  140. package/dist/spendLimits.d.ts +140 -0
  141. package/dist/spendLimits.fixture.js +53 -0
  142. package/dist/spendLimits.js +155 -0
  143. package/dist/spendLimitsPort.fixture.js +120 -0
  144. package/dist/subscriptionBinding.server.js +211 -0
  145. package/dist/subscriptionStore.server.js +643 -0
  146. package/dist/subscriptionStore.testFixtures.js +72 -0
  147. package/dist/subscriptionTables.entity.js +37 -0
  148. package/dist/subscriptions.d.ts +394 -0
  149. package/dist/subscriptions.fixture.js +97 -0
  150. package/dist/subscriptions.js +213 -0
  151. package/dist/subscriptionsPort.fixture.js +76 -0
  152. package/dist/systemFixtures.js +89 -0
  153. package/dist/tables.entity.js +83 -0
  154. package/dist/testing.d.ts +4615 -0
  155. package/dist/testing.js +29 -0
  156. package/dist/tradeAuthority.server.js +172 -0
  157. package/dist/tradeBinding.server.js +201 -0
  158. package/dist/tradeComponents.js +342 -0
  159. package/dist/tradeEscrow.server.js +232 -0
  160. package/dist/tradeEscrow.testFixtures.js +133 -0
  161. package/dist/tradeExpiry.server.js +43 -0
  162. package/dist/tradeRecovery.js +48 -0
  163. package/dist/tradeStore.server.js +750 -0
  164. package/dist/tradeStore.testFixtures.js +73 -0
  165. package/dist/tradeTables.entity.js +66 -0
  166. package/dist/tradeUiCopy.js +79 -0
  167. package/dist/trades.d.ts +246 -0
  168. package/dist/trades.fixture.js +103 -0
  169. package/dist/trades.js +289 -0
  170. package/dist/tradesPort.fixture.js +94 -0
  171. package/dist/walletEscrow.js +106 -0
  172. package/dist/walletEscrow.server.js +376 -0
  173. package/dist/walletKeys.server.js +5 -0
  174. package/dist/walletStore.js +571 -0
  175. package/dist/web.d.ts +2257 -0
  176. package/dist/web.js +9 -0
  177. package/package.json +161 -4
@@ -0,0 +1,4615 @@
1
+ import { CatalogImport } from '@voltro/plugin-inventory/server';
2
+ import { CatalogScope } from '@voltro/plugin-inventory';
3
+ import { CatalogSnapshot } from '@voltro/plugin-inventory';
4
+ import { Context } from 'effect';
5
+ import { DataStore } from '@voltro/database';
6
+ import { Effect } from 'effect';
7
+ import { FieldCipher } from '@voltro/database';
8
+ import { InMemoryDataStore } from '@voltro/runtime';
9
+ import { InventoryError } from '@voltro/plugin-inventory';
10
+ import { InventoryInstance } from '@voltro/plugin-inventory';
11
+ import { InventoryReceipt } from '@voltro/plugin-inventory';
12
+ import { InventoryReservationTransferReceipt } from '@voltro/plugin-inventory/server';
13
+ import { InventorySqlService } from '@voltro/plugin-inventory/server';
14
+ import { ItemContract } from '@voltro/plugin-inventory';
15
+ import { JWSTransactionDecodedPayload } from '@apple/app-store-server-library';
16
+ import { Layer } from 'effect';
17
+ import { PluginPrivacyTarget } from '@voltro/protocol';
18
+ import { PreparedSqlMutation } from '@voltro/database/sql';
19
+ import { Schema } from 'effect';
20
+ import { Subject } from '@voltro/protocol';
21
+ import { VoltroPlugin } from '@voltro/protocol';
22
+ import { YieldableError } from 'effect/Cause';
23
+
24
+ declare const acceptedCompletion: unique symbol;
25
+
26
+ /** SQL-issued capability. A player reference or serialized object cannot authorize provider completion. */
27
+ declare interface AcceptedIapCompletion {
28
+ readonly [acceptedCompletion]: true;
29
+ readonly orderId: string;
30
+ }
31
+
32
+ /** Read-only SQL custody capability, never current provider state or refund authority. */
33
+ declare interface AcceptedIapNotificationTarget {
34
+ readonly [acceptedNotificationTarget]: true;
35
+ readonly orderId: string;
36
+ readonly noticeId: string;
37
+ }
38
+
39
+ declare const acceptedNotificationTarget: unique symbol;
40
+
41
+ declare interface CodeDefinition {
42
+ readonly id: string;
43
+ readonly version?: string | undefined;
44
+ readonly campaign: string;
45
+ readonly items?: readonly {
46
+ readonly itemId: string | undefined;
47
+ readonly qty: number;
48
+ }[];
49
+ readonly currencies?: readonly {
50
+ readonly currency: string | undefined;
51
+ readonly amount: EconomyAmountInput;
52
+ }[];
53
+ readonly from?: number | null;
54
+ readonly until?: number | null;
55
+ readonly limits: {
56
+ readonly global: number;
57
+ readonly perPlayer: number;
58
+ };
59
+ }
60
+
61
+ declare type CodeEligibility = (input: CodeEligibilityContext) => boolean;
62
+
63
+ declare interface CodeEligibilityContext {
64
+ readonly definition: PromoCode;
65
+ readonly at: number;
66
+ readonly claims: number;
67
+ readonly playerClaims: number;
68
+ }
69
+
70
+ export declare const codeEligibilityFixture: () => Readonly<{
71
+ currencies: {
72
+ id: string;
73
+ kind: "soft";
74
+ cap: string;
75
+ }[];
76
+ context: Readonly<{
77
+ definition: PromoCode;
78
+ at: 1;
79
+ claims: 0;
80
+ playerClaims: 0;
81
+ }>;
82
+ }>;
83
+
84
+ export declare const codeEligibilitySystemFixture: Readonly<{
85
+ kind: "slot";
86
+ defaultImplementation: CodeEligibility;
87
+ defaultProbe: {
88
+ run: () => Effect.Effect<boolean, never, never>;
89
+ expected: boolean;
90
+ };
91
+ overrideProbe: {
92
+ run: () => Effect.Effect<boolean, never, never>;
93
+ expected: boolean;
94
+ };
95
+ }>;
96
+
97
+ declare type CodeObservation = {
98
+ readonly requestId: string;
99
+ readonly state: 'not-observed' | 'pending';
100
+ } | {
101
+ readonly requestId: string;
102
+ readonly state: 'fulfilled';
103
+ readonly receipt: CodeReceipt;
104
+ };
105
+
106
+ declare class CodePort extends CodePort_base {
107
+ }
108
+
109
+ declare const CodePort_base: Context.ServiceClass<CodePort, "@voltro/CodePort", CodeService>;
110
+
111
+ /** Actual installed memory-store adapter, one fresh instance per scope. Code
112
+ * values are runtime-generated; no credential literal or production backend. */
113
+ export declare const codePortFixture: Effect.Effect<Readonly<{
114
+ plugin: EconomyPlugin;
115
+ store: InMemoryDataStore;
116
+ input: Readonly<{
117
+ code: string;
118
+ requestId: "fixture.redeem";
119
+ }>;
120
+ close: Effect.Effect<void, never, never>;
121
+ }>, EconomyError, never>;
122
+
123
+ export declare const codePortSystemFixture: Readonly<{
124
+ kind: "port";
125
+ tag: typeof CodePort;
126
+ fakeLayer: Layer.Layer<EconomyPort | CodePort, EconomyError, never>;
127
+ probe: {
128
+ run: () => Effect.Effect<{
129
+ same: boolean;
130
+ state: "fulfilled" | "pending" | "not-observed";
131
+ balance: string | undefined;
132
+ }, EconomyError, never>;
133
+ expected: {
134
+ same: boolean;
135
+ state: string;
136
+ balance: string;
137
+ };
138
+ };
139
+ }>;
140
+
141
+ declare interface CodeReceipt {
142
+ readonly operationId: string;
143
+ readonly codeId: string;
144
+ readonly version: string;
145
+ readonly campaign: string;
146
+ readonly at: number;
147
+ readonly transfers: readonly {
148
+ readonly txId: string;
149
+ readonly currency: string;
150
+ readonly amount: string;
151
+ readonly at: number;
152
+ }[];
153
+ readonly grants: readonly {
154
+ readonly operationId: string;
155
+ readonly kind: 'grant';
156
+ readonly changes: readonly {
157
+ readonly instanceId: string;
158
+ readonly delta: number;
159
+ }[];
160
+ readonly at: number;
161
+ }[];
162
+ }
163
+
164
+ declare interface CodeService {
165
+ readonly create: (subject: Subject, input: CreateCodeInput) => Effect.Effect<void, EconomyError>;
166
+ readonly redeem: (subject: Subject, input: RedeemCodeInput) => Effect.Effect<CodeReceipt, EconomyError>;
167
+ readonly receipt: (subject: Subject, input: RedeemCodeInput) => Effect.Effect<CodeObservation, EconomyError>;
168
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
169
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
170
+ }
171
+
172
+ export declare const codesFakeLayer: Layer.Layer<EconomyPort | CodePort, EconomyError, never>;
173
+
174
+ declare interface Container {
175
+ readonly id: string;
176
+ readonly version: string;
177
+ readonly group: string;
178
+ readonly price: Price;
179
+ readonly drops: readonly ContainerDrop[];
180
+ readonly pity: Readonly<{
181
+ guaranteed: string;
182
+ after: number;
183
+ softFrom: number | null;
184
+ softRampBps: number;
185
+ }> | null;
186
+ readonly countries: readonly string[];
187
+ }
188
+
189
+ declare interface ContainerAccessContext {
190
+ readonly container: Container;
191
+ readonly at: number;
192
+ readonly isMinor: boolean;
193
+ readonly country: string;
194
+ readonly eligible: boolean;
195
+ }
196
+
197
+ declare interface ContainerAccessSource {
198
+ readonly read: (input: {
199
+ readonly tenantId: string;
200
+ readonly ownerId: string;
201
+ readonly container: Container;
202
+ readonly at: number;
203
+ }) => Effect.Effect<{
204
+ readonly isMinor: boolean;
205
+ readonly country: string;
206
+ readonly eligible: boolean;
207
+ readonly adjustments: readonly VirtualPriceAdjustment[];
208
+ }, EconomyError>;
209
+ }
210
+
211
+ declare interface ContainerDefinition {
212
+ readonly id: string;
213
+ readonly version?: string | undefined;
214
+ readonly group?: string;
215
+ readonly price: PriceDefinition;
216
+ readonly drops: readonly ContainerDropDefinition[];
217
+ readonly pity?: {
218
+ readonly guaranteed: string;
219
+ readonly after: number;
220
+ readonly softFrom?: number;
221
+ readonly softRampBps?: number;
222
+ };
223
+ readonly countries?: readonly string[];
224
+ }
225
+
226
+ declare interface ContainerDistribution {
227
+ readonly entries: readonly ContainerProbabilityWeight[];
228
+ readonly total: string;
229
+ readonly guaranteed: boolean;
230
+ }
231
+
232
+ declare interface ContainerDrop {
233
+ readonly id: string;
234
+ readonly rarity: string;
235
+ readonly weight: string;
236
+ readonly items: readonly {
237
+ readonly itemId: string;
238
+ readonly qty: number;
239
+ }[];
240
+ readonly currencies: readonly {
241
+ readonly currency: string;
242
+ readonly amount: string;
243
+ }[];
244
+ }
245
+
246
+ declare interface ContainerDropDefinition {
247
+ readonly id: string;
248
+ readonly rarity: string;
249
+ readonly weight: EconomyAmountInput;
250
+ readonly items?: readonly {
251
+ readonly itemId: string | undefined;
252
+ readonly qty: number;
253
+ }[];
254
+ readonly currencies?: readonly {
255
+ readonly currency: string | undefined;
256
+ readonly amount: EconomyAmountInput;
257
+ }[];
258
+ }
259
+
260
+ declare type ContainerEligibility = (input: ContainerAccessContext) => boolean;
261
+
262
+ export declare const containerEligibilitySystemFixture: Readonly<{
263
+ kind: "slot";
264
+ defaultImplementation: ContainerEligibility;
265
+ defaultProbe: {
266
+ run: () => Effect.Effect<boolean, never, never>;
267
+ expected: boolean;
268
+ };
269
+ overrideProbe: {
270
+ run: () => Effect.Effect<boolean, never, never>;
271
+ expected: boolean;
272
+ };
273
+ }>;
274
+
275
+ export declare const containerFixture: () => Readonly<{
276
+ container: Container;
277
+ currencies: {
278
+ id: string;
279
+ kind: "soft";
280
+ cap: string;
281
+ }[];
282
+ misses: 0;
283
+ realMoney: false;
284
+ }>;
285
+
286
+ declare type ContainerObservation = {
287
+ readonly requestId: string;
288
+ readonly state: 'not-observed' | 'pending';
289
+ } | {
290
+ readonly requestId: string;
291
+ readonly state: 'fulfilled';
292
+ readonly receipt: ContainerReceipt;
293
+ };
294
+
295
+ declare interface ContainerOpenInput {
296
+ readonly containerId: string;
297
+ readonly containerVersion: string;
298
+ readonly containerDigest: string;
299
+ readonly disclosureDigest: string;
300
+ readonly requestId: string;
301
+ readonly currency?: string | undefined;
302
+ }
303
+
304
+ declare interface ContainerProbabilityContext {
305
+ readonly container: Container;
306
+ readonly misses: number;
307
+ readonly currencies: readonly CurrencyDefinition[];
308
+ readonly realMoney?: boolean;
309
+ }
310
+
311
+ declare type ContainerProbabilityResolver = (input: ContainerProbabilityContext) => readonly ContainerProbabilityWeight[];
312
+
313
+ export declare const containerProbabilitySystemFixture: Readonly<{
314
+ kind: "slot";
315
+ defaultImplementation: ContainerProbabilityResolver;
316
+ defaultProbe: {
317
+ run: () => Effect.Effect< ContainerDistribution, never, never>;
318
+ expected: {
319
+ entries: {
320
+ dropId: string;
321
+ weight: string;
322
+ }[];
323
+ total: string;
324
+ guaranteed: boolean;
325
+ };
326
+ };
327
+ overrideProbe: {
328
+ run: () => Effect.Effect< ContainerDistribution, never, never>;
329
+ expected: {
330
+ entries: {
331
+ dropId: string;
332
+ weight: string;
333
+ }[];
334
+ total: string;
335
+ guaranteed: boolean;
336
+ };
337
+ };
338
+ }>;
339
+
340
+ declare interface ContainerProbabilityWeight {
341
+ readonly dropId: string;
342
+ readonly weight: string;
343
+ }
344
+
345
+ /** A deterministic boundary draw for pure sampler composition only; not a SQL,
346
+ * entropy or persisted-Pity substitute. */
347
+ export declare const containerRandomFakeLayer: Layer.Layer<ContainerRandomPort, never, never>;
348
+
349
+ /** Server authority only: no player draw/seed/state is accepted by any route. */
350
+ declare class ContainerRandomPort extends ContainerRandomPort_base {
351
+ }
352
+
353
+ declare const ContainerRandomPort_base: Context.ServiceClass<ContainerRandomPort, "@voltro/ContainerRandomPort", ContainerRandomService>;
354
+
355
+ export declare const containerRandomPortSystemFixture: Readonly<{
356
+ kind: "port";
357
+ tag: typeof ContainerRandomPort;
358
+ fakeLayer: Layer.Layer<ContainerRandomPort, never, never>;
359
+ probe: {
360
+ run: () => Effect.Effect<string, EconomyError, never>;
361
+ expected: string;
362
+ };
363
+ }>;
364
+
365
+ declare interface ContainerRandomService {
366
+ readonly draw: (exclusiveMaximum: string) => Effect.Effect<string, EconomyError>;
367
+ }
368
+
369
+ declare interface ContainerReceipt {
370
+ readonly operationId: string;
371
+ readonly containerId: string;
372
+ readonly containerVersion: string;
373
+ readonly containerDigest: string;
374
+ readonly dropId: string;
375
+ readonly rarity: string;
376
+ readonly previousMisses: number;
377
+ readonly misses: number;
378
+ readonly sequence: number;
379
+ readonly at: number;
380
+ readonly distribution: ContainerDistribution;
381
+ readonly pricePaid: ShopOrderReceipt['pricePaid'];
382
+ readonly transfers: ShopOrderReceipt['transfers'];
383
+ readonly grants: ShopOrderReceipt['grants'];
384
+ }
385
+
386
+ declare interface ContainerRollContext {
387
+ readonly distribution: ContainerDistribution;
388
+ readonly draw: string;
389
+ }
390
+
391
+ declare type ContainerRollResolver = (input: ContainerRollContext) => string;
392
+
393
+ export declare const containerRollSystemFixture: Readonly<{
394
+ kind: "slot";
395
+ defaultImplementation: ContainerRollResolver;
396
+ defaultProbe: {
397
+ run: () => Effect.Effect<string, never, never>;
398
+ expected: string;
399
+ };
400
+ overrideProbe: {
401
+ run: () => Effect.Effect<string, never, never>;
402
+ expected: string;
403
+ };
404
+ }>;
405
+
406
+ export declare const containersFakeLayer: Layer.Layer<EconomyPort | ContainersPort, never, never>;
407
+
408
+ declare class ContainersPort extends ContainersPort_base {
409
+ }
410
+
411
+ declare const ContainersPort_base: Context.ServiceClass<ContainersPort, "@voltro/ContainersPort", ContainersService>;
412
+
413
+ /** Actual installed memory-store plugin with explicit fixture-only trusted
414
+ * facts and deterministic RNG seams, no production age/entropy authority. */
415
+ export declare const containersPortFixture: Effect.Effect<Readonly<{
416
+ store: InMemoryDataStore;
417
+ plugin: EconomyPlugin;
418
+ base: {
419
+ base: {
420
+ economy: EconomySqlService;
421
+ inventory: InventorySqlService;
422
+ catalog: Readonly<{
423
+ read: (scope: CatalogScope) => Effect.Effect< CatalogSnapshot, InventoryError>;
424
+ importSnapshot: (subject: Subject, input: CatalogImport) => Effect.Effect< CatalogSnapshot, InventoryError>;
425
+ }>;
426
+ options: ShopStoreOptions;
427
+ shop: ShopSqlService;
428
+ user: {
429
+ readonly type: "user";
430
+ readonly id: string;
431
+ readonly tenantId: string;
432
+ readonly kind?: "guest" | undefined;
433
+ readonly scopes?: readonly string[] | undefined;
434
+ readonly metadata?: {
435
+ readonly [x: string]: unknown;
436
+ } | undefined;
437
+ } | {
438
+ readonly type: "apiKey";
439
+ readonly id: string;
440
+ readonly tenantId: string;
441
+ readonly scopes?: readonly string[] | undefined;
442
+ readonly metadata?: {
443
+ readonly [x: string]: unknown;
444
+ } | undefined;
445
+ } | {
446
+ readonly type: "serviceAccount";
447
+ readonly id: string;
448
+ readonly tenantId: string;
449
+ readonly scopes?: readonly string[] | undefined;
450
+ readonly metadata?: {
451
+ readonly [x: string]: unknown;
452
+ } | undefined;
453
+ readonly auditActorId?: string | null;
454
+ } | {
455
+ readonly type: "anonymous";
456
+ readonly id: null;
457
+ readonly tenantId: string | null;
458
+ readonly credentialRejected?: string | undefined;
459
+ } | {
460
+ readonly type: "system";
461
+ readonly id: string;
462
+ readonly tenantId: null;
463
+ readonly scopes?: readonly string[] | undefined;
464
+ readonly metadata?: {
465
+ readonly [x: string]: unknown;
466
+ } | undefined;
467
+ } | {
468
+ readonly type: "service";
469
+ readonly id: string;
470
+ readonly service: string;
471
+ readonly tenantId: null;
472
+ readonly scopes?: readonly string[] | undefined;
473
+ readonly metadata?: {
474
+ readonly [x: string]: unknown;
475
+ } | undefined;
476
+ };
477
+ buy: ShopBuyInput;
478
+ };
479
+ options: ContainersStoreOptions;
480
+ service: ContainersService;
481
+ draws: () => number;
482
+ };
483
+ close: Effect.Effect<void, never, never>;
484
+ }>, never, never>;
485
+
486
+ export declare const containersPortSystemFixture: Readonly<{
487
+ kind: "port";
488
+ tag: typeof ContainersPort;
489
+ fakeLayer: Layer.Layer<EconomyPort | ContainersPort, never, never>;
490
+ probe: {
491
+ run: () => Effect.Effect<{
492
+ same: boolean;
493
+ misses: number;
494
+ balance: string | undefined;
495
+ }, EconomyError, never>;
496
+ expected: {
497
+ same: boolean;
498
+ misses: number;
499
+ balance: string;
500
+ };
501
+ };
502
+ }>;
503
+
504
+ declare interface ContainersService {
505
+ readonly list: (subject: Subject) => Effect.Effect<readonly ContainerView[], EconomyError>;
506
+ readonly open: (subject: Subject, input: ContainerOpenInput) => Effect.Effect<ContainerReceipt, EconomyError>;
507
+ readonly receipt: (subject: Subject, input: ContainerOpenInput) => Effect.Effect<ContainerObservation, EconomyError>;
508
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
509
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
510
+ }
511
+
512
+ declare interface ContainersStoreOptions {
513
+ readonly containers: readonly (ContainerDefinition | Container)[];
514
+ readonly currencies: readonly CurrencyDefinition[];
515
+ readonly realMoney?: boolean;
516
+ readonly economy: Pick<EconomySqlService, 'prepareTransfer'>;
517
+ readonly inventory?: Pick<InventorySqlService, 'prepareGrant'> | undefined;
518
+ /** App-owned trusted age/country authority. No adult/allowed fallback. */
519
+ readonly accessSource: ContainerAccessSource;
520
+ readonly version: string;
521
+ readonly probability?: ContainerProbabilityResolver | undefined;
522
+ readonly roll?: ContainerRollResolver | undefined;
523
+ readonly random?: ContainerRandomService | undefined;
524
+ readonly eligibility?: ContainerEligibility | undefined;
525
+ readonly priceResolver?: PriceResolver | undefined;
526
+ readonly systemAccount?: string | undefined;
527
+ readonly maxHistoryRows?: number | undefined;
528
+ readonly now?: (() => number) | undefined;
529
+ }
530
+
531
+ declare interface ContainerView {
532
+ readonly container: Container;
533
+ readonly containerDigest: string;
534
+ readonly disclosureDigest: string;
535
+ readonly misses: number;
536
+ readonly price: Price;
537
+ readonly disclosure: ReturnType<typeof discloseContainer>;
538
+ }
539
+
540
+ declare interface CreateCodeInput {
541
+ readonly tenantId: string;
542
+ readonly code: string;
543
+ readonly definition: CodeDefinition | PromoCode;
544
+ }
545
+
546
+ declare interface CurrencyDefinition {
547
+ readonly id: string;
548
+ readonly kind: 'soft' | 'hard';
549
+ readonly cap?: EconomyAmountInput;
550
+ readonly premium?: boolean;
551
+ readonly display?: {
552
+ readonly nameKey: string;
553
+ } | null;
554
+ }
555
+
556
+ /** Public disclosure uses the exact same captured distribution as the draw. */
557
+ declare const discloseContainer: (input: ContainerProbabilityContext, slot?: ContainerProbabilityResolver) => Readonly<{
558
+ guaranteed: boolean;
559
+ probabilities: readonly Readonly<{
560
+ dropId: string;
561
+ numerator: string;
562
+ denominator: string;
563
+ }>[];
564
+ }>;
565
+
566
+ declare type EconomyAccount = {
567
+ readonly kind: 'wallet';
568
+ readonly ownerId: string;
569
+ readonly ownerKind: WalletOwnerKind;
570
+ } | {
571
+ readonly kind: 'system';
572
+ readonly id: string;
573
+ };
574
+
575
+ declare type EconomyAmountInput = string | number | bigint;
576
+
577
+ declare type EconomyChannel = 'web' | 'ios' | 'android' | 'steam';
578
+
579
+ declare interface EconomyConsistencyInput {
580
+ readonly tenantId: string;
581
+ readonly maxLedgerRows?: number | undefined;
582
+ readonly maxWallets?: number;
583
+ }
584
+
585
+ declare interface EconomyConsistencyResult {
586
+ readonly checked: number;
587
+ readonly mismatches: number;
588
+ readonly matches: boolean;
589
+ readonly ledgerRows: number;
590
+ }
591
+
592
+ declare interface EconomyConsistencyRunner {
593
+ readonly checkConsistency: (subject: Subject, input: EconomyConsistencyInput) => Effect.Effect<EconomyConsistencyResult, EconomyError>;
594
+ }
595
+
596
+ /** Redacted codes; never carries owners, amounts or provider payloads. */
597
+ declare class EconomyError extends EconomyError_base {
598
+ }
599
+
600
+ declare const EconomyError_base: Schema.Class<EconomyError, Schema.TaggedStruct<"EconomyError", {
601
+ readonly code: Schema.Literals<readonly ["invalid_currency", "invalid_amount", "overflow", "invalid_price", "real_money_disabled", "invalid_input", "invalid_record", "currency_cap_reached", "insufficient_funds", "conflict", "forbidden", "unavailable", "not_found", "erased", "limit_exceeded", "already_redeemed"]>;
602
+ }>, YieldableError>;
603
+
604
+ /** One fresh memory fixture per acquired scope; neither a SQL adapter nor a runtime backend. */
605
+ export declare const economyFakeLayer: Layer.Layer<EconomyPort, never, never>;
606
+
607
+ declare interface EconomyPlugin extends VoltroPlugin {
608
+ readonly economy: {
609
+ readonly service: () => EconomyService;
610
+ readonly prepareEscrowReserve: EconomySqlService['prepareEscrowReserve'];
611
+ readonly prepareEscrowRelease: EconomySqlService['prepareEscrowRelease'];
612
+ readonly prepareEscrowSettle: EconomySqlService['prepareEscrowSettle'];
613
+ readonly prepareEscrowTaxSettle: EconomySqlService['prepareEscrowTaxSettle'];
614
+ readonly prepareEscrowReserveTaxSettle: EconomySqlService['prepareEscrowReserveTaxSettle'];
615
+ readonly escrow: EconomySqlService['escrow'];
616
+ readonly prepareTransfer: EconomySqlService['prepareTransfer'];
617
+ readonly checkConsistency: EconomyConsistencyRunner['checkConsistency'];
618
+ readonly iap: () => IapSystemService;
619
+ readonly iapEntitlements: () => IapEntitlementsService;
620
+ readonly iapNotifications: () => IapNotificationsSystemService;
621
+ readonly iapRefundEvents: () => IapRefundEventsService;
622
+ readonly shop: () => ShopService;
623
+ readonly codes: () => CodeService;
624
+ readonly containers: () => ContainersService;
625
+ readonly subscriptions: () => SubscriptionsService;
626
+ readonly loyalty: () => LoyaltyService;
627
+ readonly trades: () => TradesService;
628
+ readonly expireTrade: TradesSqlService['expire'];
629
+ readonly expireDueTrades: TradesSqlService['expireDue'];
630
+ readonly piggyBanks: () => PiggyBanksService;
631
+ /** Trusted reference-price projection; never grants value. */
632
+ readonly price: (subject: Subject, offerId: string, adjustments?: readonly VirtualPriceAdjustment[]) => Effect.Effect<Price, EconomyError>;
633
+ };
634
+ }
635
+
636
+ declare class EconomyPort extends EconomyPort_base {
637
+ }
638
+
639
+ declare const EconomyPort_base: Context.ServiceClass<EconomyPort, "@voltro/EconomyPort", EconomyService>;
640
+
641
+ /** Explicit test-only memory port; no SQL/native/production evidence follows. */
642
+ export declare const economyPortFixture: (input: EconomyPortFixtureOptions) => Readonly<{
643
+ service: Readonly<EconomyService>;
644
+ layer: Layer.Layer<EconomyPort, never, never>;
645
+ close: Effect.Effect<void, never, never>;
646
+ }>;
647
+
648
+ export declare interface EconomyPortFixtureOptions {
649
+ readonly currencies: readonly CurrencyDefinition[];
650
+ readonly allowedServices?: readonly string[] | undefined;
651
+ readonly now?: (() => number) | undefined;
652
+ }
653
+
654
+ export declare const economyPortSystemFixture: Readonly<{
655
+ kind: "port";
656
+ tag: typeof EconomyPort;
657
+ fakeLayer: Layer.Layer<EconomyPort, never, never>;
658
+ probe: {
659
+ run: () => Effect.Effect<{
660
+ original: boolean;
661
+ balance: string | undefined;
662
+ matches: boolean;
663
+ }, EconomyError, never>;
664
+ expected: {
665
+ original: boolean;
666
+ balance: string;
667
+ matches: boolean;
668
+ };
669
+ };
670
+ }>;
671
+
672
+ declare interface EconomyReceipt {
673
+ readonly txId: string;
674
+ readonly currency: string;
675
+ readonly amount: string;
676
+ readonly at: number;
677
+ }
678
+
679
+ declare interface EconomyReference {
680
+ readonly kind: string;
681
+ readonly id: string;
682
+ }
683
+
684
+ declare interface EconomyService {
685
+ /** Trusted server transfer, never a player-authored value mutation. */
686
+ readonly transfer: (subject: Subject, input: LedgerTransferInput) => Effect.Effect<EconomyReceipt, EconomyError>;
687
+ readonly mine: (subject: Subject) => Effect.Effect<readonly EconomyWallet[], EconomyError>;
688
+ readonly auditWallet: (subject: Subject, tenantId: string, account: EconomyAccount, currency: string) => Effect.Effect<EconomyWalletAudit, EconomyError>;
689
+ readonly rebuildWallet: (subject: Subject, tenantId: string, account: EconomyAccount, currency: string) => Effect.Effect<EconomyWalletAudit, EconomyError>;
690
+ }
691
+
692
+ declare interface EconomySqlService extends EconomySystemService, WalletEscrowParticipant {
693
+ /** Trusted original purchase evidence required; no player price, owner or state authority. */
694
+ readonly prepareIapRefund: (subject: Subject, proof: VerifiedIapCurrentState, input: IapWalletRefundInput) => Effect.Effect<PreparedSqlMutation<EconomyReceipt>, EconomyError>;
695
+ readonly prepareTransfer: (subject: Subject, input: LedgerTransferInput) => Effect.Effect<PreparedSqlMutation<EconomyReceipt>, EconomyError>;
696
+ }
697
+
698
+ /** Full installed replacement adds both privacy hooks; the dependent port stays four methods. */
699
+ declare interface EconomySystemService extends EconomyService {
700
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
701
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
702
+ }
703
+
704
+ declare interface EconomyWallet {
705
+ readonly currency: string;
706
+ readonly balance: string;
707
+ readonly updatedAt: number | null;
708
+ }
709
+
710
+ declare interface EconomyWalletAudit {
711
+ readonly currency: string;
712
+ readonly balance: string;
713
+ readonly ledgerBalance: string;
714
+ readonly ledgerRows: number;
715
+ readonly matches: boolean;
716
+ }
717
+
718
+ declare type EscrowWallet = Extract<EconomyAccount, {
719
+ readonly kind: 'wallet';
720
+ }>;
721
+
722
+ declare type ExperimentBucket = (input: OfferExperimentContext) => string;
723
+
724
+ export declare const experimentBucketSystemFixture: Readonly<{
725
+ kind: "slot";
726
+ defaultImplementation: ExperimentBucket;
727
+ defaultProbe: {
728
+ run: () => Effect.Effect<string | undefined, never, never>;
729
+ expected: string;
730
+ };
731
+ overrideProbe: {
732
+ run: () => Effect.Effect<string | undefined, never, never>;
733
+ expected: string;
734
+ };
735
+ }>;
736
+
737
+ declare interface FlashOffer {
738
+ readonly id: string;
739
+ readonly version: string;
740
+ readonly offer: ShopOffer;
741
+ readonly triggerId: string;
742
+ readonly durationMs: number;
743
+ }
744
+
745
+ declare interface FlashOfferContext {
746
+ readonly flash: FlashOffer;
747
+ readonly currencies: readonly CurrencyDefinition[];
748
+ readonly realMoney?: boolean;
749
+ readonly tenantId: string;
750
+ readonly ownerId: string;
751
+ readonly at: number;
752
+ readonly trigger: FlashTriggerReceipt | null;
753
+ readonly rulesVersion?: string;
754
+ }
755
+
756
+ export declare const flashOfferFixture: () => Readonly<{
757
+ flash: FlashOffer;
758
+ currencies: {
759
+ id: string;
760
+ kind: "soft";
761
+ }[];
762
+ tenantId: "fixture";
763
+ ownerId: "a";
764
+ at: 100;
765
+ trigger: {
766
+ tenantId: string;
767
+ ownerId: string;
768
+ authorityId: string;
769
+ eventId: string;
770
+ triggerId: string;
771
+ acceptedAt: number;
772
+ };
773
+ realMoney: false;
774
+ }>;
775
+
776
+ declare type FlashTriggerPolicy = (context: FlashOfferContext) => FlashWindow | null;
777
+
778
+ declare interface FlashTriggerReceipt {
779
+ readonly tenantId: string;
780
+ readonly ownerId: string;
781
+ readonly authorityId: string;
782
+ readonly eventId: string;
783
+ readonly triggerId: string;
784
+ readonly acceptedAt: number;
785
+ }
786
+
787
+ export declare const flashTriggerSystemFixture: Readonly<{
788
+ kind: "slot";
789
+ defaultImplementation: FlashTriggerPolicy;
790
+ defaultProbe: {
791
+ run: () => Effect.Effect<number | undefined, never, never>;
792
+ expected: number;
793
+ };
794
+ overrideProbe: {
795
+ run: () => Effect.Effect<number | undefined, never, never>;
796
+ expected: number;
797
+ };
798
+ }>;
799
+
800
+ declare interface FlashWindow {
801
+ readonly from: number;
802
+ readonly until: number;
803
+ }
804
+
805
+ declare interface IapAccountBindingService {
806
+ /** Observe existing SQL binding only; no mint, seed, fence update or ownership write. */
807
+ readonly observe: (scope: IapScope, channel: EconomyChannel, environment: IapEnvironment) => Effect.Effect<string, EconomyError>;
808
+ readonly read: (scope: IapScope, channel: EconomyChannel, environment: IapEnvironment) => Effect.Effect<string, EconomyError>;
809
+ readonly prepareBinding: (scope: IapScope, channel: EconomyChannel, environment: IapEnvironment, token: string) => Effect.Effect<PreparedSqlMutation<unknown>, EconomyError>;
810
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
811
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
812
+ }
813
+
814
+ declare type IapAccountSource = Pick<IapAccountBindingService, 'read' | 'prepareBinding'>;
815
+
816
+ declare class IapAccountsPort extends IapAccountsPort_base {
817
+ }
818
+
819
+ declare const IapAccountsPort_base: Context.ServiceClass<IapAccountsPort, "@voltro/IapAccountsPort", IapAccountBindingService>;
820
+
821
+ /** A web checkout about to start: nothing is paid or verified yet. */
822
+ declare interface IapCheckoutAdmission {
823
+ readonly scope: IapScope;
824
+ readonly product: IapProduct;
825
+ readonly at: number;
826
+ }
827
+
828
+ /** One purchase a provider is asked to start: the owner, the selected offer and where the
829
+ * player returns afterwards. The account token binds the later receipt to this owner. */
830
+ declare interface IapCheckoutContext {
831
+ readonly scope: IapScope;
832
+ readonly product: IapProduct;
833
+ readonly requestId: string;
834
+ readonly accountToken: string;
835
+ readonly returnUrl: string;
836
+ readonly at: number;
837
+ }
838
+
839
+ /** Provider-owned web checkout (Stripe, Paddle, an app's own PSP, ...). Construction does no
840
+ * I/O; `initialize` checks credentials. Verification of the paid checkout stays with the same
841
+ * provider's `IapVerificationAuthority`, which must check the bound account token. */
842
+ declare interface IapCheckoutService {
843
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
844
+ readonly start: (context: IapCheckoutContext) => Effect.Effect<IapCheckoutSession, EconomyError>;
845
+ }
846
+
847
+ /** A started provider checkout. `checkoutId` is the receipt the player redeems afterwards. */
848
+ declare interface IapCheckoutSession {
849
+ readonly checkoutId: string;
850
+ readonly redirectUrl: string;
851
+ readonly expiresAt: number;
852
+ }
853
+
854
+ declare interface IapCompletionReceipt {
855
+ readonly orderId: string;
856
+ readonly channel: EconomyChannel;
857
+ readonly operation: 'acknowledge' | 'consume' | 'finalize' | 'finish';
858
+ readonly observedAt: number;
859
+ }
860
+
861
+ declare interface IapCompletionService {
862
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
863
+ readonly complete: (evidence: AcceptedIapCompletion) => Effect.Effect<IapCompletionReceipt, EconomyError>;
864
+ }
865
+
866
+ declare interface IapCurrentPurchaseState {
867
+ readonly orderId: string;
868
+ readonly channel: IapRefundChannel;
869
+ readonly state: 'active' | 'partially-refunded' | 'revoked';
870
+ /** Exact cumulative provider refund share; never floating point or money sent by a player. */
871
+ readonly refundNumerator: number;
872
+ readonly refundDenominator: number;
873
+ readonly observedAt: number;
874
+ readonly revokedAt: number | null;
875
+ }
876
+
877
+ export declare const iapCurrentStateFakeLayer: Layer.Layer<IapCurrentStatePort, EconomyError, never>;
878
+
879
+ declare class IapCurrentStatePort extends IapCurrentStatePort_base {
880
+ }
881
+
882
+ declare const IapCurrentStatePort_base: Context.ServiceClass<IapCurrentStatePort, "@voltro/IapCurrentStatePort", IapCurrentStateService>;
883
+
884
+ /** Actual fresh SQL/account custody; explicit active provider observation seam, no refund or live provider. */
885
+ export declare const iapCurrentStatePortFixture: Effect.Effect<Readonly<{
886
+ service: Readonly<IapCurrentStateService>;
887
+ target: AcceptedIapNotificationTarget;
888
+ original: IapOrderReceipt;
889
+ close: Effect.Effect<void, never, never>;
890
+ }>, EconomyError, never>;
891
+
892
+ export declare const iapCurrentStatePortSystemFixture: Readonly<{
893
+ kind: "port";
894
+ tag: typeof IapCurrentStatePort;
895
+ fakeLayer: Layer.Layer<IapCurrentStatePort, EconomyError, never>;
896
+ probe: {
897
+ run: () => Effect.Effect<{
898
+ state: "revoked" | "active" | "partially-refunded";
899
+ share: number[];
900
+ copiedRefused: boolean;
901
+ actualOrder: boolean;
902
+ }, EconomyError, never>;
903
+ expected: {
904
+ state: string;
905
+ share: number[];
906
+ copiedRefused: boolean;
907
+ actualOrder: boolean;
908
+ };
909
+ };
910
+ }>;
911
+
912
+ declare interface IapCurrentStateService {
913
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
914
+ readonly read: (target: AcceptedIapNotificationTarget) => Effect.Effect<VerifiedIapCurrentState, EconomyError>;
915
+ }
916
+
917
+ declare type IapDebtPolicy = 'allowNegative' | 'blockSpend';
918
+
919
+ /** A purchase or subscription renewal the provider reports on its own (a webhook), for the
920
+ * owner it once received as account token at checkout. Redeemed through the same pipeline as a
921
+ * player's redeem: the same `requestId` + `receipt` converge on one order. */
922
+ declare interface IapDelivery {
923
+ readonly accountToken: string;
924
+ readonly offerId: string;
925
+ readonly offerVersion: string;
926
+ readonly requestId: string;
927
+ readonly receipt: string;
928
+ }
929
+
930
+ /** Provider-owned delivery reader on the channel's notification route. `read` authenticates the
931
+ * request and returns `null` when it carries nothing to deliver; the request then continues to
932
+ * the notification pipeline (refunds, disputes). */
933
+ declare interface IapDeliveryService {
934
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
935
+ readonly read: (input: IapNotificationInput) => Effect.Effect<IapDelivery | null, EconomyError>;
936
+ }
937
+
938
+ declare interface IapEntitlementObservation {
939
+ readonly offerId: string;
940
+ readonly entitled: boolean;
941
+ readonly expiresAt: number | null;
942
+ readonly observedAt: number;
943
+ }
944
+
945
+ declare type IapEntitlementPolicy = (context: IapEntitlementPolicyContext) => boolean;
946
+
947
+ declare interface IapEntitlementPolicyContext {
948
+ readonly period: IapSubscriptionPeriod;
949
+ readonly at: number;
950
+ }
951
+
952
+ /** Actual pure codec/default/policy path, separate from the installed SQL fixture. */
953
+ export declare const iapEntitlementPolicyFixture: () => Readonly<{
954
+ offerId: "fixture.subscription";
955
+ at: 150;
956
+ version: "fixture.entitlement.policy.v1";
957
+ periods: readonly IapSubscriptionPeriod[];
958
+ }>;
959
+
960
+ export declare const iapEntitlementPolicySystemFixture: Readonly<{
961
+ kind: "slot";
962
+ defaultImplementation: IapEntitlementPolicy;
963
+ defaultProbe: {
964
+ run: () => Effect.Effect< IapEntitlementObservation, never, never>;
965
+ expected: {
966
+ offerId: string;
967
+ entitled: boolean;
968
+ expiresAt: number;
969
+ observedAt: number;
970
+ };
971
+ };
972
+ overrideProbe: {
973
+ run: () => Effect.Effect< IapEntitlementObservation, never, never>;
974
+ expected: {
975
+ offerId: string;
976
+ entitled: boolean;
977
+ expiresAt: null;
978
+ observedAt: number;
979
+ };
980
+ };
981
+ }>;
982
+
983
+ export declare const iapEntitlementsFakeLayer: Layer.Layer<IapEntitlementsPort, unknown, never>;
984
+
985
+ declare interface IapEntitlementsPluginOptions {
986
+ readonly version: string;
987
+ readonly policy?: IapEntitlementPolicy;
988
+ readonly service?: IapEntitlementsService;
989
+ readonly querySources?: readonly string[] | undefined;
990
+ }
991
+
992
+ /** Complete replacement owns authoritative history, privacy and expiry admission. */
993
+ declare class IapEntitlementsPort extends IapEntitlementsPort_base {
994
+ }
995
+
996
+ declare const IapEntitlementsPort_base: Context.ServiceClass<IapEntitlementsPort, "@voltro/IapEntitlementsPort", IapEntitlementsService>;
997
+
998
+ /** Fresh installed account, accepted SQL period, wallet and inventory per scope.
999
+ * Provider verification/refund observations are private test seams, not store trust proofs.
1000
+ */
1001
+ export declare const iapEntitlementsPortFixture: Effect.Effect<Readonly<{
1002
+ service: IapEntitlementsService;
1003
+ fixture: {
1004
+ plugin: EconomyPlugin;
1005
+ options: IapPluginOptions;
1006
+ inventory: InventorySqlService;
1007
+ activate: () => Effect.Effect<void, unknown>;
1008
+ close: () => Effect.Effect<void, unknown>;
1009
+ setTime: (value: number) => void;
1010
+ verifications: () => number;
1011
+ base: {
1012
+ scope: {
1013
+ tenantId: string;
1014
+ ownerId: string;
1015
+ };
1016
+ transactionId: `${string}-${string}-${string}-${string}-${string}`;
1017
+ definition: IapProductDefinition;
1018
+ catalog: Readonly<{
1019
+ read: (scope: CatalogScope) => Effect.Effect< CatalogSnapshot, InventoryError>;
1020
+ importSnapshot: (subject: Subject, input: CatalogImport) => Effect.Effect< CatalogSnapshot, InventoryError>;
1021
+ }>;
1022
+ inventory: InventorySqlService;
1023
+ economy: EconomySqlService;
1024
+ accounts: IapAccountBindingService;
1025
+ options: IapStoreOptions;
1026
+ verificationOptions: {
1027
+ products: IapProductDefinition[];
1028
+ accounts: IapAccountBindingService;
1029
+ realMoney: boolean;
1030
+ channel: EconomyChannel;
1031
+ environment: IapEnvironment;
1032
+ applicationId: string;
1033
+ currencies: readonly CurrencyDefinition[];
1034
+ now?: (() => number) | undefined;
1035
+ };
1036
+ iap: IapSqlService;
1037
+ user: (ownerId?: string, tenant?: string) => Subject;
1038
+ machine: {
1039
+ readonly type: "user";
1040
+ readonly id: string;
1041
+ readonly tenantId: string;
1042
+ readonly kind?: "guest" | undefined;
1043
+ readonly scopes?: readonly string[] | undefined;
1044
+ readonly metadata?: {
1045
+ readonly [x: string]: unknown;
1046
+ } | undefined;
1047
+ } | {
1048
+ readonly type: "apiKey";
1049
+ readonly id: string;
1050
+ readonly tenantId: string;
1051
+ readonly scopes?: readonly string[] | undefined;
1052
+ readonly metadata?: {
1053
+ readonly [x: string]: unknown;
1054
+ } | undefined;
1055
+ } | {
1056
+ readonly type: "serviceAccount";
1057
+ readonly id: string;
1058
+ readonly tenantId: string;
1059
+ readonly scopes?: readonly string[] | undefined;
1060
+ readonly metadata?: {
1061
+ readonly [x: string]: unknown;
1062
+ } | undefined;
1063
+ readonly auditActorId?: string | null;
1064
+ } | {
1065
+ readonly type: "anonymous";
1066
+ readonly id: null;
1067
+ readonly tenantId: string | null;
1068
+ readonly credentialRejected?: string | undefined;
1069
+ } | {
1070
+ readonly type: "system";
1071
+ readonly id: string;
1072
+ readonly tenantId: null;
1073
+ readonly scopes?: readonly string[] | undefined;
1074
+ readonly metadata?: {
1075
+ readonly [x: string]: unknown;
1076
+ } | undefined;
1077
+ } | {
1078
+ readonly type: "service";
1079
+ readonly id: string;
1080
+ readonly service: string;
1081
+ readonly tenantId: null;
1082
+ readonly scopes?: readonly string[] | undefined;
1083
+ readonly metadata?: {
1084
+ readonly [x: string]: unknown;
1085
+ } | undefined;
1086
+ };
1087
+ policyId: string;
1088
+ item: ItemContract;
1089
+ verificationCalls: () => number;
1090
+ at: number;
1091
+ accountToken: `${string}-${string}-${string}-${string}-${string}`;
1092
+ receipt: string;
1093
+ currencies: ResolvedCurrency[];
1094
+ intent: {
1095
+ channel: "ios";
1096
+ offerId: string;
1097
+ offerVersion: string;
1098
+ requestId: string;
1099
+ receipt: string;
1100
+ };
1101
+ providerOptions: {
1102
+ realMoney: boolean;
1103
+ environment: IapEnvironment;
1104
+ applicationId: string;
1105
+ products: readonly IapProductDefinition[];
1106
+ currencies: readonly CurrencyDefinition[];
1107
+ accounts: IapAccountSource;
1108
+ now: () => number;
1109
+ };
1110
+ context: IapVerificationContext;
1111
+ apple: JWSTransactionDecodedPayload;
1112
+ facts: IapTransactionFacts;
1113
+ androidDefinition: IapProductDefinition;
1114
+ googleContext: IapVerificationContext;
1115
+ google: {
1116
+ purchaseStateContext: {
1117
+ purchaseState: string;
1118
+ };
1119
+ testPurchaseContext: {
1120
+ fopType: string;
1121
+ };
1122
+ obfuscatedExternalAccountId: `${string}-${string}-${string}-${string}-${string}`;
1123
+ purchaseCompletionTime: string;
1124
+ acknowledgementState: string;
1125
+ productLineItem: {
1126
+ productId: string;
1127
+ productOfferDetails: {
1128
+ quantity: number;
1129
+ refundableQuantity: number;
1130
+ consumptionState: string;
1131
+ };
1132
+ }[];
1133
+ };
1134
+ };
1135
+ store: DataStore;
1136
+ body: string;
1137
+ notice: {
1138
+ notice: IapNotificationNotice;
1139
+ options: IapNotificationTargetOptions;
1140
+ evidence: VerifiedIapNotification;
1141
+ };
1142
+ ready: () => number;
1143
+ reads: () => number;
1144
+ failProvider: (value: boolean) => void;
1145
+ beforeRead: (value: (() => Effect.Effect<void, EconomyError>) | undefined) => void;
1146
+ advance: (milliseconds: number) => void;
1147
+ currentRefund: (value: 0 | 1) => void;
1148
+ };
1149
+ close: Effect.Effect<void, never, never>;
1150
+ }>, unknown, never>;
1151
+
1152
+ export declare const iapEntitlementsPortSystemFixture: Readonly<{
1153
+ kind: "port";
1154
+ tag: typeof IapEntitlementsPort;
1155
+ fakeLayer: Layer.Layer<IapEntitlementsPort, unknown, never>;
1156
+ probe: {
1157
+ run: () => Effect.Effect<{
1158
+ accepted: boolean;
1159
+ ownsExpiry: boolean;
1160
+ foreign: boolean;
1161
+ expired: boolean;
1162
+ expiryIsReadOnly: boolean;
1163
+ }, unknown, never>;
1164
+ expected: {
1165
+ accepted: boolean;
1166
+ ownsExpiry: boolean;
1167
+ foreign: boolean;
1168
+ expired: boolean;
1169
+ expiryIsReadOnly: boolean;
1170
+ };
1171
+ };
1172
+ }>;
1173
+
1174
+ declare interface IapEntitlementsService {
1175
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
1176
+ readonly entitled: (subject: Subject, offerId: string) => Effect.Effect<IapEntitlementObservation, EconomyError>;
1177
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
1178
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
1179
+ }
1180
+
1181
+ declare type IapEnvironment = 'sandbox' | 'production';
1182
+
1183
+ export declare const iapFakeLayer: Layer.Layer<IapPort | IapAccountsPort | EconomyPort | IapSystemPort, unknown, never>;
1184
+
1185
+ /** Where an owner manages what they pay for (cancel a subscription, change the card). */
1186
+ declare interface IapManagementContext {
1187
+ readonly accountToken: string;
1188
+ /** The provider's original transaction IDs (subscriptions) of the owner's accepted orders on
1189
+ * this channel, newest first; empty when the owner holds none. */
1190
+ readonly originals: readonly string[];
1191
+ readonly returnUrl: string;
1192
+ readonly at: number;
1193
+ }
1194
+
1195
+ declare interface IapManagementService {
1196
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
1197
+ /** A provider page for this owner; `not_found` when the owner has nothing to manage there. */
1198
+ readonly open: (context: IapManagementContext) => Effect.Effect<{
1199
+ readonly url: string;
1200
+ }, EconomyError>;
1201
+ }
1202
+
1203
+ declare interface IapNotificationCipherSource {
1204
+ /** Default is the registered framework FieldCipher. Explicit sources use that same codec/row binding contract. */
1205
+ readonly read: () => FieldCipher | undefined;
1206
+ }
1207
+
1208
+ export declare const iapNotificationCustodyFakeLayer: Layer.Layer<IapNotificationCustodyPort, EconomyError, never>;
1209
+
1210
+ declare class IapNotificationCustodyPort extends IapNotificationCustodyPort_base {
1211
+ }
1212
+
1213
+ declare const IapNotificationCustodyPort_base: Context.ServiceClass<IapNotificationCustodyPort, "@voltro/IapNotificationCustodyPort", IapNotificationCustodyService>;
1214
+
1215
+ /** Fresh actual SQL + private framework cipher; explicit authenticated notice seam, no global cipher replacement. */
1216
+ export declare const iapNotificationCustodyPortFixture: Effect.Effect<Readonly<{
1217
+ service: Readonly<IapNotificationCustodyService>;
1218
+ evidence: VerifiedIapNotification;
1219
+ privacy: {
1220
+ tenantId: string;
1221
+ subjectId: string;
1222
+ kind: "guest";
1223
+ };
1224
+ close: Effect.Effect<void, never, never>;
1225
+ }>, EconomyError, never>;
1226
+
1227
+ export declare const iapNotificationCustodyPortSystemFixture: Readonly<{
1228
+ kind: "port";
1229
+ tag: typeof IapNotificationCustodyPort;
1230
+ fakeLayer: Layer.Layer<IapNotificationCustodyPort, EconomyError, never>;
1231
+ probe: {
1232
+ run: () => Effect.Effect<{
1233
+ state: "erased" | "expired" | "pending" | "processed";
1234
+ actualOrder: boolean;
1235
+ stableIdentity: boolean;
1236
+ restoredChannel: IapRefundChannel;
1237
+ pending: number;
1238
+ }, EconomyError, never>;
1239
+ expected: {
1240
+ state: string;
1241
+ actualOrder: boolean;
1242
+ stableIdentity: boolean;
1243
+ restoredChannel: string;
1244
+ pending: number;
1245
+ };
1246
+ };
1247
+ }>;
1248
+
1249
+ declare interface IapNotificationCustodyReceipt {
1250
+ readonly id: string;
1251
+ readonly orderId: string;
1252
+ readonly state: 'pending' | 'processed' | 'expired' | 'erased';
1253
+ }
1254
+
1255
+ declare interface IapNotificationCustodyService {
1256
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
1257
+ readonly retain: (evidence: VerifiedIapNotification) => Effect.Effect<IapNotificationCustodyReceipt, EconomyError>;
1258
+ /** Trusted server worker, not a player RPC. Restores only row-bound authenticated SQL custody. */
1259
+ readonly restore: (id: string) => Effect.Effect<VerifiedIapNotification, EconomyError>;
1260
+ /** Actual accepted refund capability and same-store journal fence required. */
1261
+ readonly markProcessed: (id: string, receipt: IapRefundReceipt) => Effect.Effect<IapNotificationCustodyReceipt, EconomyError>;
1262
+ readonly pending: () => Effect.Effect<readonly IapNotificationCustodyReceipt[], EconomyError>;
1263
+ readonly prune: () => Effect.Effect<number, EconomyError>;
1264
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
1265
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
1266
+ }
1267
+
1268
+ export declare const iapNotificationFakeLayer: Layer.Layer<IapNotificationPort, EconomyError, never>;
1269
+
1270
+ declare interface IapNotificationInput {
1271
+ readonly body: string;
1272
+ /** Actual HTTP Authorization header for authenticated Pub/Sub push; never query/body authority. */
1273
+ readonly authorization?: string;
1274
+ /** The request's other headers, lowercased and bounded, for providers that sign in a header
1275
+ * (Stripe: `stripe-signature`). Never authority by themselves. */
1276
+ readonly headers?: Readonly<Record<string, string>>;
1277
+ }
1278
+
1279
+ declare interface IapNotificationNotice {
1280
+ readonly channel: IapRefundChannel;
1281
+ readonly applicationId: string;
1282
+ /** RTDN itself does not identify sandbox/production. Fresh lookup must establish it. */
1283
+ readonly environment: IapEnvironment | null;
1284
+ readonly noticeId: string;
1285
+ readonly sourceType: string;
1286
+ readonly kind: 'one-time' | 'subscription' | 'voided' | 'review' | 'test';
1287
+ readonly occurredAt: number;
1288
+ readonly transactionId: string | null;
1289
+ readonly originalTransactionId: string | null;
1290
+ readonly storeProductId: string | null;
1291
+ }
1292
+
1293
+ declare class IapNotificationPort extends IapNotificationPort_base {
1294
+ }
1295
+
1296
+ declare const IapNotificationPort_base: Context.ServiceClass<IapNotificationPort, "@voltro/IapNotificationPort", IapNotificationService>;
1297
+
1298
+ /** Explicit synthetic TEST-notice authority only. No default adapter, signature, provider state or money writer. */
1299
+ export declare const iapNotificationPortFixture: Effect.Effect<Readonly<{
1300
+ service: Readonly<IapNotificationService>;
1301
+ close: Effect.Effect<void, never, never>;
1302
+ }>, EconomyError, never>;
1303
+
1304
+ export declare const iapNotificationPortSystemFixture: Readonly<{
1305
+ kind: "port";
1306
+ tag: typeof IapNotificationPort;
1307
+ fakeLayer: Layer.Layer<IapNotificationPort, EconomyError, never>;
1308
+ probe: {
1309
+ run: () => Effect.Effect<{
1310
+ kind: "test" | "subscription" | "one-time" | "voided" | "review";
1311
+ channel: IapRefundChannel;
1312
+ copiedRefused: boolean;
1313
+ noPurchaseAuthority: boolean;
1314
+ }, EconomyError, never>;
1315
+ expected: {
1316
+ kind: string;
1317
+ channel: string;
1318
+ copiedRefused: boolean;
1319
+ noPurchaseAuthority: boolean;
1320
+ };
1321
+ };
1322
+ }>;
1323
+
1324
+ export declare const iapNotificationProcessorFakeLayer: Layer.Layer<IapNotificationProcessorPort, EconomyError, never>;
1325
+
1326
+ declare class IapNotificationProcessorPort extends IapNotificationProcessorPort_base {
1327
+ }
1328
+
1329
+ declare const IapNotificationProcessorPort_base: Context.ServiceClass<IapNotificationProcessorPort, "@voltro/IapNotificationProcessorPort", IapNotificationProcessorService>;
1330
+
1331
+ export declare const iapNotificationProcessorPortFixture: Effect.Effect<Readonly<{
1332
+ service: Readonly<IapNotificationProcessorService>;
1333
+ body: string;
1334
+ privacy: {
1335
+ tenantId: string;
1336
+ subjectId: string;
1337
+ kind: "guest";
1338
+ };
1339
+ close: Effect.Effect<void, EconomyError, never>;
1340
+ }>, EconomyError, never>;
1341
+
1342
+ export declare const iapNotificationProcessorPortSystemFixture: Readonly<{
1343
+ kind: "port";
1344
+ tag: typeof IapNotificationProcessorPort;
1345
+ fakeLayer: Layer.Layer<IapNotificationProcessorPort, EconomyError, never>;
1346
+ probe: {
1347
+ run: () => Effect.Effect<{
1348
+ processed: boolean;
1349
+ replay: boolean;
1350
+ pending: number;
1351
+ providerCalls: number;
1352
+ }, EconomyError, never>;
1353
+ expected: {
1354
+ processed: boolean;
1355
+ replay: boolean;
1356
+ pending: number;
1357
+ providerCalls: number;
1358
+ };
1359
+ };
1360
+ }>;
1361
+
1362
+ declare interface IapNotificationProcessorService {
1363
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
1364
+ /** `null`: an authenticated test notice (a store's TEST message, a provider event with no
1365
+ * purchase in it) is acknowledged and not retained; no retry could ever process it. */
1366
+ readonly ingest: (input: IapNotificationInput) => Effect.Effect<IapNotificationCustodyReceipt | null, EconomyError>;
1367
+ readonly process: (id: string) => Effect.Effect<IapNotificationCustodyReceipt, EconomyError>;
1368
+ readonly pending: () => Effect.Effect<readonly IapNotificationCustodyReceipt[], EconomyError>;
1369
+ readonly prune: () => Effect.Effect<number, EconomyError>;
1370
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
1371
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
1372
+ }
1373
+
1374
+ declare interface IapNotificationService {
1375
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
1376
+ readonly verify: (input: IapNotificationInput) => Effect.Effect<VerifiedIapNotification, EconomyError>;
1377
+ }
1378
+
1379
+ declare interface IapNotificationsPluginOptions {
1380
+ readonly version: string;
1381
+ readonly pathPrefix?: string;
1382
+ readonly maxBodyBytes?: number;
1383
+ readonly bodyRetentionMs?: number | undefined;
1384
+ readonly debtPolicy?: IapDebtPolicy | undefined;
1385
+ readonly assets?: IapRefundAssetParticipant | undefined;
1386
+ readonly assetsVersion?: string | undefined;
1387
+ readonly maxNoticeRows?: number;
1388
+ readonly maxRefundRows?: number;
1389
+ readonly cipher?: IapNotificationCipherSource | undefined;
1390
+ /** Complete replacement owns authentication/current state/financial authority, queue and privacy. */
1391
+ readonly service?: IapNotificationsSystemService;
1392
+ /** Complete notification replacements may expose their own three-method event queue. */
1393
+ readonly refundEvents?: IapRefundEventsService;
1394
+ /** Custom borrowed-transaction SQL read. Omit when the custom system exposes no own order disposition. */
1395
+ readonly refundDisposition?: IapRefundDispositionSource | undefined;
1396
+ /** Runs after a notice changed a purchase (a refund, a revocation), e.g. to end game time at once:
1397
+ * `rooms.moderation.recheckAccess({ tenantId, subjectId: ownerId })`. Best effort: a failure is
1398
+ * ignored and the notice stays processed. Not with a complete replacement `service`. */
1399
+ readonly onChange?: ((change: IapPurchaseChange) => Effect.Effect<void, unknown>) | undefined;
1400
+ }
1401
+
1402
+ export declare const iapNotificationsSystemFakeLayer: Layer.Layer<IapNotificationsSystemPort, unknown, never>;
1403
+
1404
+ declare class IapNotificationsSystemPort extends IapNotificationsSystemPort_base {
1405
+ }
1406
+
1407
+ declare const IapNotificationsSystemPort_base: Context.ServiceClass<IapNotificationsSystemPort, "@voltro/IapNotificationsSystemPort", IapNotificationsSystemService>;
1408
+
1409
+ /** Actual installed SQL chain and privately owned framework cipher; provider authorities are explicit seams. */
1410
+ export declare const iapNotificationsSystemPortFixture: Effect.Effect<Readonly<{
1411
+ service: IapNotificationsSystemService;
1412
+ plugin: EconomyPlugin;
1413
+ user: {
1414
+ readonly type: "user";
1415
+ readonly id: string;
1416
+ readonly tenantId: string;
1417
+ readonly kind?: "guest" | undefined;
1418
+ readonly scopes?: readonly string[] | undefined;
1419
+ readonly metadata?: {
1420
+ readonly [x: string]: unknown;
1421
+ } | undefined;
1422
+ } | {
1423
+ readonly type: "apiKey";
1424
+ readonly id: string;
1425
+ readonly tenantId: string;
1426
+ readonly scopes?: readonly string[] | undefined;
1427
+ readonly metadata?: {
1428
+ readonly [x: string]: unknown;
1429
+ } | undefined;
1430
+ } | {
1431
+ readonly type: "serviceAccount";
1432
+ readonly id: string;
1433
+ readonly tenantId: string;
1434
+ readonly scopes?: readonly string[] | undefined;
1435
+ readonly metadata?: {
1436
+ readonly [x: string]: unknown;
1437
+ } | undefined;
1438
+ readonly auditActorId?: string | null;
1439
+ } | {
1440
+ readonly type: "anonymous";
1441
+ readonly id: null;
1442
+ readonly tenantId: string | null;
1443
+ readonly credentialRejected?: string | undefined;
1444
+ } | {
1445
+ readonly type: "system";
1446
+ readonly id: string;
1447
+ readonly tenantId: null;
1448
+ readonly scopes?: readonly string[] | undefined;
1449
+ readonly metadata?: {
1450
+ readonly [x: string]: unknown;
1451
+ } | undefined;
1452
+ } | {
1453
+ readonly type: "service";
1454
+ readonly id: string;
1455
+ readonly service: string;
1456
+ readonly tenantId: null;
1457
+ readonly scopes?: readonly string[] | undefined;
1458
+ readonly metadata?: {
1459
+ readonly [x: string]: unknown;
1460
+ } | undefined;
1461
+ };
1462
+ intent: {
1463
+ channel: "ios";
1464
+ offerId: string;
1465
+ offerVersion: string;
1466
+ requestId: string;
1467
+ receipt: string;
1468
+ };
1469
+ body: string;
1470
+ privacy: {
1471
+ tenantId: string;
1472
+ subjectId: string;
1473
+ kind: "guest";
1474
+ };
1475
+ close: Effect.Effect<void, never, never>;
1476
+ }>, unknown, never>;
1477
+
1478
+ export declare const iapNotificationsSystemPortSystemFixture: Readonly<{
1479
+ kind: "port";
1480
+ tag: typeof IapNotificationsSystemPort;
1481
+ fakeLayer: Layer.Layer<IapNotificationsSystemPort, unknown, never>;
1482
+ probe: {
1483
+ run: () => Effect.Effect<{
1484
+ processed: boolean;
1485
+ replay: boolean;
1486
+ providerCalls: number;
1487
+ pending: number;
1488
+ }, unknown, never>;
1489
+ expected: {
1490
+ processed: boolean;
1491
+ replay: boolean;
1492
+ providerCalls: number;
1493
+ pending: number;
1494
+ };
1495
+ };
1496
+ }>;
1497
+
1498
+ declare interface IapNotificationsSystemService {
1499
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
1500
+ readonly processor: (channel: EconomyChannel) => Effect.Effect<IapNotificationProcessorService, EconomyError>;
1501
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
1502
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
1503
+ }
1504
+
1505
+ declare interface IapNotificationTargetOptions {
1506
+ readonly realMoney: boolean;
1507
+ readonly channel: IapRefundChannel;
1508
+ readonly applicationId: string;
1509
+ readonly environment: IapEnvironment;
1510
+ }
1511
+
1512
+ declare type IapOrderObservation = {
1513
+ readonly requestId: string;
1514
+ readonly state: 'not-observed' | 'pending';
1515
+ } | {
1516
+ readonly requestId: string;
1517
+ readonly state: 'fulfilled';
1518
+ readonly receipt: IapOrderReceipt;
1519
+ readonly refund?: IapRefundDisposition;
1520
+ } | {
1521
+ readonly requestId: string;
1522
+ readonly state: 'partially-refunded' | 'refunded';
1523
+ readonly receipt: IapOrderReceipt;
1524
+ readonly refund: IapRefundDisposition;
1525
+ };
1526
+
1527
+ declare interface IapOrderReceipt {
1528
+ readonly orderId: string;
1529
+ readonly offerId: string;
1530
+ readonly offerVersion: string;
1531
+ readonly quantity: number;
1532
+ readonly at: number;
1533
+ readonly pricePaid: IapPaidAmount | null;
1534
+ readonly transfers: readonly {
1535
+ readonly txId: string;
1536
+ readonly currency: string;
1537
+ readonly amount: string;
1538
+ readonly at: number;
1539
+ }[];
1540
+ readonly grants: readonly {
1541
+ readonly operationId: string;
1542
+ readonly kind: 'grant';
1543
+ readonly changes: readonly {
1544
+ readonly instanceId: string;
1545
+ readonly delta: number;
1546
+ }[];
1547
+ readonly at: number;
1548
+ }[];
1549
+ }
1550
+
1551
+ /** Provider units retain their own decimal scale. Reference prices are never substituted. */
1552
+ declare interface IapPaidAmount {
1553
+ readonly currency: string;
1554
+ readonly units: string;
1555
+ readonly scale: number;
1556
+ }
1557
+
1558
+ declare interface IapPluginOptions {
1559
+ readonly version: string;
1560
+ readonly products: readonly IapProductDefinition[];
1561
+ readonly providers?: readonly IapProviderDefinition[];
1562
+ readonly namespace?: string;
1563
+ readonly admission?: IapStoreOptions['admission'] | undefined;
1564
+ readonly inventory?: IapStoreOptions['inventory'] | undefined;
1565
+ readonly systemAccount?: string | undefined;
1566
+ readonly maxHistoryRows?: number | undefined;
1567
+ readonly now?: (() => number) | undefined;
1568
+ readonly notifications?: IapNotificationsPluginOptions | undefined;
1569
+ readonly entitlements?: IapEntitlementsPluginOptions;
1570
+ readonly service?: IapSystemService;
1571
+ readonly querySources?: {
1572
+ readonly receipt: readonly string[];
1573
+ } | undefined;
1574
+ }
1575
+
1576
+ declare class IapPort extends IapPort_base {
1577
+ }
1578
+
1579
+ declare const IapPort_base: Context.ServiceClass<IapPort, "@voltro/IapPort", IapService>;
1580
+
1581
+ /** Fresh actual installed SQL account/custody/asset services. Provider and finish authority are explicit fixtures. */
1582
+ export declare const iapPortFixture: Effect.Effect<{
1583
+ base: {
1584
+ scope: {
1585
+ tenantId: string;
1586
+ ownerId: string;
1587
+ };
1588
+ transactionId: `${string}-${string}-${string}-${string}-${string}`;
1589
+ definition: IapProductDefinition;
1590
+ catalog: Readonly<{
1591
+ read: (scope: CatalogScope) => Effect.Effect< CatalogSnapshot, InventoryError>;
1592
+ importSnapshot: (subject: Subject, input: CatalogImport) => Effect.Effect< CatalogSnapshot, InventoryError>;
1593
+ }>;
1594
+ inventory: InventorySqlService;
1595
+ economy: EconomySqlService;
1596
+ accounts: IapAccountBindingService;
1597
+ options: IapStoreOptions;
1598
+ verificationOptions: {
1599
+ products: IapProductDefinition[];
1600
+ accounts: IapAccountBindingService;
1601
+ realMoney: boolean;
1602
+ channel: EconomyChannel;
1603
+ environment: IapEnvironment;
1604
+ applicationId: string;
1605
+ currencies: readonly CurrencyDefinition[];
1606
+ now?: (() => number) | undefined;
1607
+ };
1608
+ iap: IapSqlService;
1609
+ user: (ownerId?: string, tenant?: string) => Subject;
1610
+ machine: {
1611
+ readonly type: "user";
1612
+ readonly id: string;
1613
+ readonly tenantId: string;
1614
+ readonly kind?: "guest" | undefined;
1615
+ readonly scopes?: readonly string[] | undefined;
1616
+ readonly metadata?: {
1617
+ readonly [x: string]: unknown;
1618
+ } | undefined;
1619
+ } | {
1620
+ readonly type: "apiKey";
1621
+ readonly id: string;
1622
+ readonly tenantId: string;
1623
+ readonly scopes?: readonly string[] | undefined;
1624
+ readonly metadata?: {
1625
+ readonly [x: string]: unknown;
1626
+ } | undefined;
1627
+ } | {
1628
+ readonly type: "serviceAccount";
1629
+ readonly id: string;
1630
+ readonly tenantId: string;
1631
+ readonly scopes?: readonly string[] | undefined;
1632
+ readonly metadata?: {
1633
+ readonly [x: string]: unknown;
1634
+ } | undefined;
1635
+ readonly auditActorId?: string | null;
1636
+ } | {
1637
+ readonly type: "anonymous";
1638
+ readonly id: null;
1639
+ readonly tenantId: string | null;
1640
+ readonly credentialRejected?: string | undefined;
1641
+ } | {
1642
+ readonly type: "system";
1643
+ readonly id: string;
1644
+ readonly tenantId: null;
1645
+ readonly scopes?: readonly string[] | undefined;
1646
+ readonly metadata?: {
1647
+ readonly [x: string]: unknown;
1648
+ } | undefined;
1649
+ } | {
1650
+ readonly type: "service";
1651
+ readonly id: string;
1652
+ readonly service: string;
1653
+ readonly tenantId: null;
1654
+ readonly scopes?: readonly string[] | undefined;
1655
+ readonly metadata?: {
1656
+ readonly [x: string]: unknown;
1657
+ } | undefined;
1658
+ };
1659
+ policyId: string;
1660
+ item: ItemContract;
1661
+ verificationCalls: () => number;
1662
+ at: number;
1663
+ accountToken: `${string}-${string}-${string}-${string}-${string}`;
1664
+ receipt: string;
1665
+ currencies: ResolvedCurrency[];
1666
+ intent: {
1667
+ channel: "ios";
1668
+ offerId: string;
1669
+ offerVersion: string;
1670
+ requestId: string;
1671
+ receipt: string;
1672
+ };
1673
+ providerOptions: {
1674
+ realMoney: boolean;
1675
+ environment: IapEnvironment;
1676
+ applicationId: string;
1677
+ products: readonly IapProductDefinition[];
1678
+ currencies: readonly CurrencyDefinition[];
1679
+ accounts: IapAccountSource;
1680
+ now: () => number;
1681
+ };
1682
+ context: IapVerificationContext;
1683
+ apple: JWSTransactionDecodedPayload;
1684
+ facts: IapTransactionFacts;
1685
+ androidDefinition: IapProductDefinition;
1686
+ googleContext: IapVerificationContext;
1687
+ google: {
1688
+ purchaseStateContext: {
1689
+ purchaseState: string;
1690
+ };
1691
+ testPurchaseContext: {
1692
+ fopType: string;
1693
+ };
1694
+ obfuscatedExternalAccountId: `${string}-${string}-${string}-${string}-${string}`;
1695
+ purchaseCompletionTime: string;
1696
+ acknowledgementState: string;
1697
+ productLineItem: {
1698
+ productId: string;
1699
+ productOfferDetails: {
1700
+ quantity: number;
1701
+ refundableQuantity: number;
1702
+ consumptionState: string;
1703
+ };
1704
+ }[];
1705
+ };
1706
+ };
1707
+ plugin: EconomyPlugin;
1708
+ options: IapPluginOptions;
1709
+ store: DataStore;
1710
+ activate: () => Effect.Effect<void, unknown>;
1711
+ close: Effect.Effect<void, never, never>;
1712
+ initializations: () => number;
1713
+ verifications: () => number;
1714
+ }, unknown, never>;
1715
+
1716
+ /** Actual Tag probes, for a future complete System21 registration; no synthetic constant probe. */
1717
+ export declare const iapPortSystemFixture: Readonly<{
1718
+ kind: "port";
1719
+ tag: typeof IapPort;
1720
+ fakeLayer: Layer.Layer<IapPort | IapAccountsPort | EconomyPort | IapSystemPort, unknown, never>;
1721
+ probe: {
1722
+ run: () => Effect.Effect<{
1723
+ original: boolean;
1724
+ state: "fulfilled" | "pending" | "not-observed" | "partially-refunded" | "refunded";
1725
+ completion: boolean;
1726
+ balance: string | undefined;
1727
+ }, unknown, never>;
1728
+ expected: {
1729
+ original: boolean;
1730
+ state: string;
1731
+ completion: boolean;
1732
+ balance: string;
1733
+ };
1734
+ };
1735
+ }>;
1736
+
1737
+ declare interface IapProduct {
1738
+ readonly offer: ShopOffer;
1739
+ readonly channel: EconomyChannel;
1740
+ readonly kind: IapProductKind;
1741
+ readonly storeProductId: string;
1742
+ }
1743
+
1744
+ declare interface IapProductDefinition {
1745
+ readonly offer: ShopOfferDefinition | ShopOffer;
1746
+ readonly channel: EconomyChannel;
1747
+ readonly kind: IapProductKind;
1748
+ }
1749
+
1750
+ declare type IapProductKind = 'consumable' | 'non-consumable' | 'subscription';
1751
+
1752
+ declare interface IapProviderContext {
1753
+ readonly realMoney: true;
1754
+ readonly channel: EconomyChannel;
1755
+ readonly environment: IapEnvironment;
1756
+ readonly applicationId: string;
1757
+ readonly products: readonly IapProductDefinition[];
1758
+ readonly currencies: readonly CurrencyDefinition[];
1759
+ readonly accounts: IapAccountBindingService;
1760
+ readonly now: () => number;
1761
+ }
1762
+
1763
+ declare interface IapProviderDefinition {
1764
+ readonly channel: EconomyChannel;
1765
+ readonly environment: IapEnvironment;
1766
+ readonly applicationId: string;
1767
+ /** Server-owned verifier factory. Construction does no I/O; initialize is awaited at activation. */
1768
+ readonly create: (context: IapProviderContext) => IapVerificationService;
1769
+ /** Optional authenticated notification authority, initialized only when configured. */
1770
+ readonly createNotifications?: ((context: IapProviderContext) => IapNotificationService) | undefined;
1771
+ readonly createCurrentState?: ((context: IapProviderContext) => IapCurrentStateService) | undefined;
1772
+ /** Optional server completion authority, initialized before this IAP system activates. */
1773
+ readonly createCompletion?: ((context: IapProviderContext) => IapCompletionService) | undefined;
1774
+ /** Optional web checkout (`economy.iap.checkout`): the provider starts a hosted payment and
1775
+ * the player redeems its `checkoutId` as the receipt. Any payment provider fits. */
1776
+ readonly createCheckout?: ((context: IapProviderContext) => IapCheckoutService) | undefined;
1777
+ /** Origins a checkout may return to (`https://game.example`); required with `createCheckout`. */
1778
+ readonly checkoutReturnOrigins?: readonly string[] | undefined;
1779
+ /** Optional provider-delivered purchases and subscription renewals, read on the channel's
1780
+ * notification route (needs `notifications`). Each is redeemed for the owner of its account token. */
1781
+ readonly createDeliveries?: ((context: IapProviderContext) => IapDeliveryService) | undefined;
1782
+ /** Optional provider page where the owner manages subscriptions (`economy.iap.manage`). */
1783
+ readonly createManagement?: ((context: IapProviderContext) => IapManagementService) | undefined;
1784
+ }
1785
+
1786
+ declare interface IapPurchaseAdmission {
1787
+ readonly scope: IapScope;
1788
+ readonly product: IapProduct;
1789
+ readonly facts: IapTransactionFacts;
1790
+ readonly at: number;
1791
+ }
1792
+
1793
+ /** Required real SQL age/consent/spend eligibility authority. No permissive default. */
1794
+ declare interface IapPurchaseAdmissionSource {
1795
+ readonly prepare: (input: IapPurchaseAdmission) => Effect.Effect<PreparedSqlMutation<unknown>, EconomyError>;
1796
+ /** Optional check before a web checkout starts. Refuse what `prepare` would certainly
1797
+ * refuse, so the player is never charged for a purchase that cannot be redeemed;
1798
+ * `prepare` stays the authority. */
1799
+ readonly checkout?: (input: IapCheckoutAdmission) => Effect.Effect<void, EconomyError>;
1800
+ }
1801
+
1802
+ /** A store or provider notice changed a purchase: a refund, a revocation. `ownerId` is the player. */
1803
+ declare interface IapPurchaseChange {
1804
+ readonly tenantId: string;
1805
+ readonly ownerId: string;
1806
+ readonly orderId: string;
1807
+ readonly offerId: string;
1808
+ readonly offerVersion: string;
1809
+ readonly state: IapRefundReceipt['state'];
1810
+ }
1811
+
1812
+ declare interface IapReceiptIntent {
1813
+ readonly channel: EconomyChannel;
1814
+ readonly offerId: string;
1815
+ readonly offerVersion: string;
1816
+ readonly requestId: string;
1817
+ /** Opaque store evidence. Never log or persist this in player receipts. */
1818
+ readonly receipt: string;
1819
+ }
1820
+
1821
+ /** Read-only original-intention reference. Never send raw receipts in query parameters. */
1822
+ declare interface IapReceiptReference {
1823
+ readonly channel: EconomyChannel;
1824
+ readonly offerId: string;
1825
+ readonly offerVersion: string;
1826
+ readonly requestId: string;
1827
+ readonly receiptDigest: string;
1828
+ }
1829
+
1830
+ declare interface IapRefundAssetContext {
1831
+ readonly original: IapSqlOriginalPurchase;
1832
+ readonly state: IapCurrentPurchaseState;
1833
+ readonly previous: IapRefundReceipt | null;
1834
+ readonly sequence: number;
1835
+ }
1836
+
1837
+ /** Mandatory for orders with item grants. It must use the same real store and original grant custody. */
1838
+ declare interface IapRefundAssetParticipant {
1839
+ readonly prepare: (input: IapRefundAssetContext) => Effect.Effect<PreparedSqlMutation<unknown>, EconomyError>;
1840
+ readonly validate: (input: IapRefundAssetContext, receipt: unknown) => unknown;
1841
+ /** SQL-only audit of accepted movement receipts; no provider/application callbacks. */
1842
+ readonly audit: (tx: DataStore, input: IapRefundAssetContext, receipt: unknown) => Promise<void>;
1843
+ }
1844
+
1845
+ /** Channels whose providers report refunds and revocations to the economy. */
1846
+ declare type IapRefundChannel = 'ios' | 'android' | 'web';
1847
+
1848
+ declare interface IapRefundCurrency {
1849
+ readonly currency: string;
1850
+ readonly original: string;
1851
+ readonly refunded: string;
1852
+ }
1853
+
1854
+ /** Accepted SQL refund history only. This projection never authorizes a grant or completion. */
1855
+ declare interface IapRefundDisposition {
1856
+ readonly sequence: number;
1857
+ readonly state: 'active' | 'partially-refunded' | 'revoked';
1858
+ readonly refundNumerator: number;
1859
+ readonly refundDenominator: number;
1860
+ readonly observedAt: number;
1861
+ }
1862
+
1863
+ /** Borrowed transaction read only: no provider, nested transaction, seed, clock or mutation. */
1864
+ declare interface IapRefundDispositionSource {
1865
+ readonly read: (tx: DataStore, original: IapSqlOriginalPurchase) => Promise<IapRefundDisposition | null>;
1866
+ }
1867
+
1868
+ declare interface IapRefundEventContext {
1869
+ readonly namespace: string;
1870
+ readonly tenantId: string;
1871
+ readonly subjectId: string;
1872
+ readonly payload: IapRefundEventPayload;
1873
+ /** Complete bounded accepted history; these are observations, never a fraud verdict. */
1874
+ readonly refundedOrders: number;
1875
+ readonly observedOrders: number;
1876
+ readonly refundAdjustments: number;
1877
+ }
1878
+
1879
+ declare type IapRefundEventPayload = typeof IapRefundEventPayloadSchema.Type;
1880
+
1881
+ declare const IapRefundEventPayloadSchema: Schema.Struct<{
1882
+ readonly eventId: Schema.String;
1883
+ readonly orderId: Schema.String;
1884
+ readonly sequence: Schema.Number;
1885
+ readonly state: Schema.Literals<readonly ["active", "partially-refunded", "revoked"]>;
1886
+ readonly refundNumerator: Schema.Number;
1887
+ readonly refundDenominator: Schema.Number;
1888
+ readonly observedAt: Schema.Number;
1889
+ }>;
1890
+
1891
+ export declare const iapRefundEventsFakeLayer: Layer.Layer<IapRefundEventsPort, EconomyError, never>;
1892
+
1893
+ declare class IapRefundEventsPort extends IapRefundEventsPort_base {
1894
+ }
1895
+
1896
+ declare const IapRefundEventsPort_base: Context.ServiceClass<IapRefundEventsPort, "@voltro/IapRefundEventsPort", IapRefundEventsService>;
1897
+
1898
+ export declare const iapRefundEventsPortFixture: Effect.Effect<Readonly<{
1899
+ service: Readonly<IapRefundEventsService>;
1900
+ privacy: {
1901
+ tenantId: string;
1902
+ subjectId: string;
1903
+ kind: "guest";
1904
+ };
1905
+ create: () => Effect.Effect< IapRefundReceipt, EconomyError, never>;
1906
+ close: Effect.Effect<void, EconomyError, never>;
1907
+ }>, EconomyError, never>;
1908
+
1909
+ export declare const iapRefundEventsPortSystemFixture: Readonly<{
1910
+ kind: "port";
1911
+ tag: typeof IapRefundEventsPort;
1912
+ fakeLayer: Layer.Layer<IapRefundEventsPort, EconomyError, never>;
1913
+ probe: {
1914
+ run: () => Effect.Effect<{
1915
+ state: "revoked" | "active" | "partially-refunded";
1916
+ originals: number;
1917
+ pending: number;
1918
+ }, EconomyError, never>;
1919
+ expected: {
1920
+ state: string;
1921
+ originals: number;
1922
+ pending: number;
1923
+ };
1924
+ };
1925
+ }>;
1926
+
1927
+ declare interface IapRefundEventsService {
1928
+ readonly pending: (subject: Subject) => Effect.Effect<readonly PendingIapRefundEvent[], EconomyError>;
1929
+ readonly read: (subject: Subject, event: PendingIapRefundEvent) => Effect.Effect<IapRefundEventContext, EconomyError>;
1930
+ readonly acknowledge: (subject: Subject, event: PendingIapRefundEvent, deliveryVersion: string) => Effect.Effect<void, EconomyError>;
1931
+ }
1932
+
1933
+ export declare const iapRefundFakeLayer: Layer.Layer<IapRefundPort, EconomyError, never>;
1934
+
1935
+ declare interface IapRefundOptions {
1936
+ readonly realMoney: boolean;
1937
+ readonly namespace: string;
1938
+ readonly debtPolicy: IapDebtPolicy;
1939
+ readonly orders: Pick<IapSqlService, 'prepareCurrentState'>;
1940
+ readonly economy: Pick<EconomySqlService, 'prepareTransfer' | 'prepareIapRefund'>;
1941
+ readonly assets?: IapRefundAssetParticipant | undefined;
1942
+ readonly assetsVersion?: string | undefined;
1943
+ readonly maxHistoryRows?: number | undefined;
1944
+ }
1945
+
1946
+ declare class IapRefundPort extends IapRefundPort_base {
1947
+ }
1948
+
1949
+ declare const IapRefundPort_base: Context.ServiceClass<IapRefundPort, "@voltro/IapRefundPort", IapRefundService>;
1950
+
1951
+ /** Fresh actual SQL participants per scope; provider/authenticated-notice fixtures are explicit seams. */
1952
+ export declare const iapRefundPortFixture: Effect.Effect<Readonly<{
1953
+ service: Readonly<IapRefundService>;
1954
+ fixture: {
1955
+ store: DataStore;
1956
+ f: {
1957
+ scope: {
1958
+ tenantId: string;
1959
+ ownerId: string;
1960
+ };
1961
+ transactionId: `${string}-${string}-${string}-${string}-${string}`;
1962
+ definition: IapProductDefinition;
1963
+ catalog: Readonly<{
1964
+ read: (scope: CatalogScope) => Effect.Effect< CatalogSnapshot, InventoryError>;
1965
+ importSnapshot: (subject: Subject, input: CatalogImport) => Effect.Effect< CatalogSnapshot, InventoryError>;
1966
+ }>;
1967
+ inventory: InventorySqlService;
1968
+ economy: EconomySqlService;
1969
+ accounts: IapAccountBindingService;
1970
+ options: IapStoreOptions;
1971
+ verificationOptions: {
1972
+ products: IapProductDefinition[];
1973
+ accounts: IapAccountBindingService;
1974
+ realMoney: boolean;
1975
+ channel: EconomyChannel;
1976
+ environment: IapEnvironment;
1977
+ applicationId: string;
1978
+ currencies: readonly CurrencyDefinition[];
1979
+ now?: (() => number) | undefined;
1980
+ };
1981
+ iap: IapSqlService;
1982
+ user: (ownerId?: string, tenant?: string) => Subject;
1983
+ machine: {
1984
+ readonly type: "user";
1985
+ readonly id: string;
1986
+ readonly tenantId: string;
1987
+ readonly kind?: "guest" | undefined;
1988
+ readonly scopes?: readonly string[] | undefined;
1989
+ readonly metadata?: {
1990
+ readonly [x: string]: unknown;
1991
+ } | undefined;
1992
+ } | {
1993
+ readonly type: "apiKey";
1994
+ readonly id: string;
1995
+ readonly tenantId: string;
1996
+ readonly scopes?: readonly string[] | undefined;
1997
+ readonly metadata?: {
1998
+ readonly [x: string]: unknown;
1999
+ } | undefined;
2000
+ } | {
2001
+ readonly type: "serviceAccount";
2002
+ readonly id: string;
2003
+ readonly tenantId: string;
2004
+ readonly scopes?: readonly string[] | undefined;
2005
+ readonly metadata?: {
2006
+ readonly [x: string]: unknown;
2007
+ } | undefined;
2008
+ readonly auditActorId?: string | null;
2009
+ } | {
2010
+ readonly type: "anonymous";
2011
+ readonly id: null;
2012
+ readonly tenantId: string | null;
2013
+ readonly credentialRejected?: string | undefined;
2014
+ } | {
2015
+ readonly type: "system";
2016
+ readonly id: string;
2017
+ readonly tenantId: null;
2018
+ readonly scopes?: readonly string[] | undefined;
2019
+ readonly metadata?: {
2020
+ readonly [x: string]: unknown;
2021
+ } | undefined;
2022
+ } | {
2023
+ readonly type: "service";
2024
+ readonly id: string;
2025
+ readonly service: string;
2026
+ readonly tenantId: null;
2027
+ readonly scopes?: readonly string[] | undefined;
2028
+ readonly metadata?: {
2029
+ readonly [x: string]: unknown;
2030
+ } | undefined;
2031
+ };
2032
+ policyId: string;
2033
+ item: ItemContract;
2034
+ verificationCalls: () => number;
2035
+ at: number;
2036
+ accountToken: `${string}-${string}-${string}-${string}-${string}`;
2037
+ receipt: string;
2038
+ currencies: ResolvedCurrency[];
2039
+ intent: {
2040
+ channel: "ios";
2041
+ offerId: string;
2042
+ offerVersion: string;
2043
+ requestId: string;
2044
+ receipt: string;
2045
+ };
2046
+ providerOptions: {
2047
+ realMoney: boolean;
2048
+ environment: IapEnvironment;
2049
+ applicationId: string;
2050
+ products: readonly IapProductDefinition[];
2051
+ currencies: readonly CurrencyDefinition[];
2052
+ accounts: IapAccountSource;
2053
+ now: () => number;
2054
+ };
2055
+ context: IapVerificationContext;
2056
+ apple: JWSTransactionDecodedPayload;
2057
+ facts: IapTransactionFacts;
2058
+ androidDefinition: IapProductDefinition;
2059
+ googleContext: IapVerificationContext;
2060
+ google: {
2061
+ purchaseStateContext: {
2062
+ purchaseState: string;
2063
+ };
2064
+ testPurchaseContext: {
2065
+ fopType: string;
2066
+ };
2067
+ obfuscatedExternalAccountId: `${string}-${string}-${string}-${string}-${string}`;
2068
+ purchaseCompletionTime: string;
2069
+ acknowledgementState: string;
2070
+ productLineItem: {
2071
+ productId: string;
2072
+ productOfferDetails: {
2073
+ quantity: number;
2074
+ refundableQuantity: number;
2075
+ consumptionState: string;
2076
+ };
2077
+ }[];
2078
+ };
2079
+ };
2080
+ n: {
2081
+ notice: IapNotificationNotice;
2082
+ options: IapNotificationTargetOptions;
2083
+ evidence: VerifiedIapNotification;
2084
+ };
2085
+ original: IapOrderReceipt;
2086
+ target: AcceptedIapNotificationTarget;
2087
+ options: IapRefundOptions;
2088
+ assets: IapRefundAssetParticipant;
2089
+ refunds: IapRefundSqlService;
2090
+ observeProvider: (numerator: number, denominator?: number) => Promise< VerifiedIapCurrentState>;
2091
+ advance: (ms: number) => void;
2092
+ privacy: {
2093
+ tenantId: string;
2094
+ subjectId: string;
2095
+ kind: "guest";
2096
+ };
2097
+ };
2098
+ close: Effect.Effect<void, never, never>;
2099
+ }>, EconomyError, never>;
2100
+
2101
+ export declare const iapRefundPortSystemFixture: Readonly<{
2102
+ kind: "port";
2103
+ tag: typeof IapRefundPort;
2104
+ fakeLayer: Layer.Layer<IapRefundPort, EconomyError, never>;
2105
+ probe: {
2106
+ run: () => Effect.Effect<{
2107
+ refunded: string | undefined;
2108
+ wallet: string | undefined;
2109
+ stock: number;
2110
+ replay: boolean;
2111
+ rebuilt: boolean;
2112
+ }, EconomyError | InventoryError, never>;
2113
+ expected: {
2114
+ refunded: string;
2115
+ wallet: string;
2116
+ stock: number;
2117
+ replay: boolean;
2118
+ rebuilt: boolean;
2119
+ };
2120
+ };
2121
+ }>;
2122
+
2123
+ declare interface IapRefundReceipt {
2124
+ readonly orderId: string;
2125
+ readonly sequence: number;
2126
+ readonly state: IapCurrentPurchaseState['state'];
2127
+ readonly refundNumerator: number;
2128
+ readonly refundDenominator: number;
2129
+ readonly observedAt: number;
2130
+ readonly noticeId: string;
2131
+ readonly fingerprint: string;
2132
+ readonly debtPolicy: IapDebtPolicy;
2133
+ readonly currencies: readonly IapRefundCurrency[];
2134
+ readonly transfers: readonly EconomyReceipt[];
2135
+ /** Opaque actual asset participant receipt, validated by its captured participant. */
2136
+ readonly assets: unknown;
2137
+ }
2138
+
2139
+ declare interface IapRefundService {
2140
+ readonly reconcile: (proof: VerifiedIapCurrentState) => Effect.Effect<IapRefundReceipt, EconomyError>;
2141
+ readonly rebuild: (proof: VerifiedIapCurrentState) => Effect.Effect<IapRefundReceipt | null, EconomyError>;
2142
+ readonly observe: (scope: IapScope, orderId: string) => Effect.Effect<IapRefundReceipt | null, EconomyError>;
2143
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
2144
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
2145
+ }
2146
+
2147
+ /** SQL remains refund history and rebuild source. External provider I/O is complete before this writer. */
2148
+ declare interface IapRefundSqlService extends IapRefundService {
2149
+ readonly events: IapRefundEventsService;
2150
+ readonly readDisposition: IapRefundDispositionSource['read'];
2151
+ }
2152
+
2153
+ declare interface IapScope {
2154
+ readonly tenantId: string;
2155
+ readonly ownerId: string;
2156
+ }
2157
+
2158
+ declare interface IapService {
2159
+ readonly redeem: (subject: Subject, input: IapReceiptIntent) => Effect.Effect<IapOrderReceipt, EconomyError>;
2160
+ readonly receipt: (subject: Subject, input: IapReceiptReference) => Effect.Effect<IapOrderObservation, EconomyError>;
2161
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
2162
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
2163
+ }
2164
+
2165
+ /** SQL original after the owning adapter has validated its immutable custody and receipt. */
2166
+ declare interface IapSqlOriginalPurchase {
2167
+ readonly orderId: string;
2168
+ readonly scope: IapScope;
2169
+ readonly receipt: IapOrderReceipt;
2170
+ readonly product: IapProduct;
2171
+ readonly facts: IapTransactionFacts;
2172
+ readonly currencies: readonly CurrencyDefinition[];
2173
+ }
2174
+
2175
+ declare interface IapSqlService extends IapService {
2176
+ /** Complete owned accepted subscription periods. SQL reads only; never current provider status. */
2177
+ readonly subscriptionPeriods: (subject: Subject) => Effect.Effect<readonly IapSubscriptionPeriod[], EconomyError>;
2178
+ /** Server only: the provider's original transaction IDs (a Stripe subscription) of the owner's
2179
+ * accepted subscription orders on `channel`, newest period first. Never a player projection. */
2180
+ readonly subscriptionOriginals: (subject: Subject, channel: EconomyChannel) => Effect.Effect<readonly string[], EconomyError>;
2181
+ /** Actual original IAP/account/retained-notice SQL fences; joins the same-store financial mutation group. */
2182
+ readonly prepareCurrentState: (evidence: VerifiedIapCurrentState) => Effect.Effect<PreparedSqlMutation<IapCurrentPurchaseState>, EconomyError>;
2183
+ /** Trusted server declaration + authenticated notice; read accepted SQL, never seed or fulfill. */
2184
+ readonly prepareNotification: (options: IapNotificationTargetOptions, notice: VerifiedIapNotification) => Effect.Effect<AcceptedIapNotificationTarget, EconomyError>;
2185
+ /** Read accepted SQL original and issue a revalidated capability; no grant, clock or provider call. */
2186
+ readonly prepareCompletion: (subject: Subject, intent: Parameters<IapService['redeem']>[1]) => Effect.Effect<AcceptedIapCompletion, EconomyError>;
2187
+ }
2188
+
2189
+ declare interface IapStoreOptions {
2190
+ readonly realMoney: boolean;
2191
+ /** Operator-owned immutable work/privacy namespace, never an RPC input. */
2192
+ readonly namespace: string;
2193
+ readonly currencies: readonly CurrencyDefinition[];
2194
+ readonly verification: Pick<IapVerificationService, 'verify'>;
2195
+ readonly admission: IapPurchaseAdmissionSource;
2196
+ readonly economy: Pick<EconomySqlService, 'prepareTransfer'>;
2197
+ readonly inventory?: Pick<InventorySqlService, 'prepareGrant'> | undefined;
2198
+ readonly refundDisposition?: IapRefundDispositionSource | undefined;
2199
+ readonly systemAccount: string;
2200
+ readonly maxHistoryRows?: number | undefined;
2201
+ readonly now?: (() => number) | undefined;
2202
+ }
2203
+
2204
+ /** An accepted store period, not a fresh provider observation or financial capability. */
2205
+ declare interface IapSubscriptionPeriod {
2206
+ readonly orderId: string;
2207
+ readonly offerId: string;
2208
+ readonly offerVersion: string;
2209
+ readonly channel: EconomyChannel;
2210
+ readonly startsAt: number;
2211
+ readonly expiresAt: number;
2212
+ readonly refund: IapRefundDisposition | null;
2213
+ }
2214
+
2215
+ declare class IapSystemPort extends IapSystemPort_base {
2216
+ }
2217
+
2218
+ declare const IapSystemPort_base: Context.ServiceClass<IapSystemPort, "@voltro/IapSystemPort", IapSystemService>;
2219
+
2220
+ export declare const iapSystemPortSystemFixture: Readonly<{
2221
+ kind: "port";
2222
+ tag: typeof IapSystemPort;
2223
+ fakeLayer: Layer.Layer<IapPort | IapAccountsPort | EconomyPort | IapSystemPort, unknown, never>;
2224
+ probe: {
2225
+ run: () => Effect.Effect<{
2226
+ stable: boolean;
2227
+ publicUuid: boolean;
2228
+ erased: boolean;
2229
+ }, unknown, never>;
2230
+ expected: {
2231
+ stable: boolean;
2232
+ publicUuid: boolean;
2233
+ erased: boolean;
2234
+ };
2235
+ };
2236
+ }>;
2237
+
2238
+ /** Complete system replacement owns provider readiness, accounts, financial authority and privacy. */
2239
+ declare interface IapSystemService extends IapService {
2240
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
2241
+ readonly account: (subject: Subject, channel: EconomyChannel) => Effect.Effect<string, EconomyError>;
2242
+ /** Explicit retry of the same accepted original intent. Never redeems or grants. */
2243
+ readonly complete: (subject: Subject, intent: Parameters<IapService['redeem']>[1]) => Effect.Effect<IapCompletionReceipt, EconomyError>;
2244
+ }
2245
+
2246
+ declare interface IapTransactionFacts {
2247
+ readonly channel: EconomyChannel;
2248
+ readonly environment: IapEnvironment;
2249
+ readonly applicationId: string;
2250
+ readonly transactionId: string;
2251
+ readonly originalTransactionId: string | null;
2252
+ readonly storeProductId: string;
2253
+ readonly kind: IapProductKind;
2254
+ readonly quantity: number;
2255
+ readonly purchasedAt: number;
2256
+ readonly expiresAt: number | null;
2257
+ readonly paid: IapPaidAmount | null;
2258
+ readonly acknowledgement: 'none' | 'pending' | 'acknowledged';
2259
+ readonly consumption: 'none' | 'available' | 'consumed';
2260
+ }
2261
+
2262
+ declare interface IapVerificationContext {
2263
+ readonly scope: IapScope;
2264
+ readonly intent: IapReceiptIntent;
2265
+ readonly product: IapProduct;
2266
+ readonly accountToken: string;
2267
+ readonly at: number;
2268
+ }
2269
+
2270
+ declare interface IapVerificationService {
2271
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
2272
+ readonly verify: (scope: IapScope, input: IapReceiptIntent) => Effect.Effect<VerifiedIapPurchase, EconomyError>;
2273
+ }
2274
+
2275
+ declare interface IapWalletRefundInput {
2276
+ readonly requestId: string;
2277
+ readonly currency: string;
2278
+ readonly amount: string;
2279
+ readonly debtPolicy: IapDebtPolicy;
2280
+ }
2281
+
2282
+ declare interface LedgerPosting {
2283
+ readonly account: EconomyAccount;
2284
+ readonly debit: string;
2285
+ readonly credit: string;
2286
+ }
2287
+
2288
+ declare interface LedgerTransfer {
2289
+ readonly tenantId: string;
2290
+ readonly requestId: string;
2291
+ readonly currency: string;
2292
+ readonly amount: string;
2293
+ readonly reason: string;
2294
+ readonly ref: EconomyReference;
2295
+ readonly postings: readonly [LedgerPosting, LedgerPosting];
2296
+ }
2297
+
2298
+ export declare const ledgerTransferFixture: () => LedgerTransfer;
2299
+
2300
+ declare interface LedgerTransferInput {
2301
+ readonly tenantId: string;
2302
+ readonly requestId: string;
2303
+ readonly currency: string;
2304
+ readonly from: EconomyAccount;
2305
+ readonly to: EconomyAccount;
2306
+ readonly amount: EconomyAmountInput;
2307
+ readonly reason: string;
2308
+ readonly ref: EconomyReference;
2309
+ }
2310
+
2311
+ declare interface Loyalty {
2312
+ readonly id: string;
2313
+ readonly version: string;
2314
+ readonly titleKey: string;
2315
+ readonly pointsFrom: readonly {
2316
+ readonly id: string;
2317
+ readonly kind: 'purchase.real' | 'purchase.virtual';
2318
+ readonly currency: string;
2319
+ readonly numerator: string;
2320
+ readonly denominator: string;
2321
+ }[];
2322
+ readonly tiers: readonly {
2323
+ readonly id: string;
2324
+ readonly at: string;
2325
+ readonly perks: readonly LoyaltyPerk[];
2326
+ }[];
2327
+ readonly decay: 'none' | 'seasonal';
2328
+ }
2329
+
2330
+ /** Only trusted server callers name receipts. Amounts, weights, points, tiers
2331
+ * and seasons are never request fields. No player credit/reverse action. */
2332
+ declare interface LoyaltyCreditInput {
2333
+ readonly tenantId: string;
2334
+ readonly ownerId: string;
2335
+ readonly loyaltyId: string;
2336
+ readonly sourceId: string;
2337
+ readonly purchaseId: string;
2338
+ readonly requestId: string;
2339
+ }
2340
+
2341
+ declare interface LoyaltyDefinition {
2342
+ readonly id: string;
2343
+ readonly version?: string | undefined;
2344
+ readonly titleKey: string;
2345
+ readonly pointsFrom: readonly {
2346
+ readonly id: string;
2347
+ readonly kind: 'purchase.real' | 'purchase.virtual';
2348
+ readonly currency: string;
2349
+ readonly numerator: EconomyAmountInput;
2350
+ readonly denominator: EconomyAmountInput;
2351
+ }[];
2352
+ readonly tiers: readonly {
2353
+ readonly id: string;
2354
+ readonly at: EconomyAmountInput;
2355
+ readonly perks: readonly LoyaltyPerkDefinition[];
2356
+ }[];
2357
+ readonly decay?: 'none' | 'seasonal';
2358
+ }
2359
+
2360
+ export declare const loyaltyFakeLayer: Layer.Layer<EconomyPort | LoyaltyPort, EconomyError, never>;
2361
+
2362
+ export declare const loyaltyFixture: () => Readonly<{
2363
+ loyalty: Loyalty;
2364
+ currencies: {
2365
+ id: string;
2366
+ kind: "soft";
2367
+ }[];
2368
+ points: "1000";
2369
+ realMoney: false;
2370
+ }>;
2371
+
2372
+ declare interface LoyaltyMineInput {
2373
+ readonly loyaltyId: string;
2374
+ }
2375
+
2376
+ declare type LoyaltyObservation = {
2377
+ readonly requestId: string;
2378
+ readonly state: 'not-observed';
2379
+ } | {
2380
+ readonly requestId: string;
2381
+ readonly state: 'fulfilled';
2382
+ readonly receipt: LoyaltyReceipt;
2383
+ };
2384
+
2385
+ declare type LoyaltyPerk = Exclude<LoyaltyPerkDefinition, {
2386
+ readonly kind: 'inboxGift';
2387
+ }> | {
2388
+ readonly id: string;
2389
+ readonly kind: 'inboxGift';
2390
+ readonly items: readonly ShopItemGrant[];
2391
+ readonly currencies: readonly {
2392
+ readonly currency: string;
2393
+ readonly amount: string;
2394
+ }[];
2395
+ };
2396
+
2397
+ declare type LoyaltyPerkDefinition = {
2398
+ readonly id: string;
2399
+ readonly kind: 'xpMultiplier';
2400
+ readonly bps: number;
2401
+ } | {
2402
+ readonly id: string;
2403
+ readonly kind: 'discount';
2404
+ readonly bps: number;
2405
+ } | {
2406
+ readonly id: string;
2407
+ readonly kind: 'chatBadge';
2408
+ readonly badgeId: string;
2409
+ } | {
2410
+ readonly id: string;
2411
+ readonly kind: 'inboxGift';
2412
+ readonly items?: readonly ShopItemGrant[] | undefined;
2413
+ readonly currencies?: readonly ShopCurrencyGrant[] | undefined;
2414
+ };
2415
+
2416
+ declare interface LoyaltyPerksContext extends LoyaltyTierContext {
2417
+ readonly tierId: string;
2418
+ }
2419
+
2420
+ declare type LoyaltyPerksResolver = (input: LoyaltyPerksContext) => readonly LoyaltyPerk[];
2421
+
2422
+ export declare const loyaltyPerksSystemFixture: Readonly<{
2423
+ kind: "slot";
2424
+ defaultImplementation: LoyaltyPerksResolver;
2425
+ defaultProbe: {
2426
+ run: () => Effect.Effect<readonly LoyaltyPerk[], never, never>;
2427
+ expected: {
2428
+ id: string;
2429
+ kind: string;
2430
+ bps: number;
2431
+ }[];
2432
+ };
2433
+ overrideProbe: {
2434
+ run: () => Effect.Effect<readonly LoyaltyPerk[], never, never>;
2435
+ expected: never[];
2436
+ };
2437
+ }>;
2438
+
2439
+ declare type LoyaltyPointEntry = {
2440
+ readonly id: string;
2441
+ readonly sequence: number;
2442
+ readonly kind: 'credit';
2443
+ readonly at: number;
2444
+ readonly sourceId: string;
2445
+ readonly purchaseId: string;
2446
+ readonly seasonId: string | null;
2447
+ readonly purchaseAmount: string;
2448
+ readonly numerator: string;
2449
+ readonly denominator: string;
2450
+ readonly delta: string;
2451
+ } | {
2452
+ readonly id: string;
2453
+ readonly sequence: number;
2454
+ readonly kind: 'reverse';
2455
+ readonly at: number;
2456
+ readonly originalId: string;
2457
+ readonly refundId: string;
2458
+ readonly seasonId: string | null;
2459
+ readonly totalRefunded: string;
2460
+ readonly delta: string;
2461
+ };
2462
+
2463
+ declare interface LoyaltyPointProjection {
2464
+ readonly entries: readonly LoyaltyPointEntry[];
2465
+ readonly balances: readonly {
2466
+ readonly seasonId: string | null;
2467
+ readonly points: string;
2468
+ }[];
2469
+ readonly credits: readonly {
2470
+ readonly id: string;
2471
+ readonly refundedAmount: string;
2472
+ readonly remainingPoints: string;
2473
+ }[];
2474
+ }
2475
+
2476
+ declare class LoyaltyPort extends LoyaltyPort_base {
2477
+ }
2478
+
2479
+ declare const LoyaltyPort_base: Context.ServiceClass<LoyaltyPort, "@voltro/LoyaltyPort", LoyaltyService>;
2480
+
2481
+ /** Actual installed memory-store points port and actual virtual Shop receipt.
2482
+ * Seasons/refunds are named fixture sources; no production source is invented. */
2483
+ export declare const loyaltyPortFixture: Effect.Effect<Readonly<{
2484
+ store: InMemoryDataStore;
2485
+ plugin: EconomyPlugin;
2486
+ base: {
2487
+ base: {
2488
+ economy: EconomySqlService;
2489
+ inventory: InventorySqlService;
2490
+ catalog: Readonly<{
2491
+ read: (scope: CatalogScope) => Effect.Effect< CatalogSnapshot, InventoryError>;
2492
+ importSnapshot: (subject: Subject, input: CatalogImport) => Effect.Effect< CatalogSnapshot, InventoryError>;
2493
+ }>;
2494
+ options: ShopStoreOptions;
2495
+ shop: ShopSqlService;
2496
+ user: {
2497
+ readonly type: "user";
2498
+ readonly id: string;
2499
+ readonly tenantId: string;
2500
+ readonly kind?: "guest" | undefined;
2501
+ readonly scopes?: readonly string[] | undefined;
2502
+ readonly metadata?: {
2503
+ readonly [x: string]: unknown;
2504
+ } | undefined;
2505
+ } | {
2506
+ readonly type: "apiKey";
2507
+ readonly id: string;
2508
+ readonly tenantId: string;
2509
+ readonly scopes?: readonly string[] | undefined;
2510
+ readonly metadata?: {
2511
+ readonly [x: string]: unknown;
2512
+ } | undefined;
2513
+ } | {
2514
+ readonly type: "serviceAccount";
2515
+ readonly id: string;
2516
+ readonly tenantId: string;
2517
+ readonly scopes?: readonly string[] | undefined;
2518
+ readonly metadata?: {
2519
+ readonly [x: string]: unknown;
2520
+ } | undefined;
2521
+ readonly auditActorId?: string | null;
2522
+ } | {
2523
+ readonly type: "anonymous";
2524
+ readonly id: null;
2525
+ readonly tenantId: string | null;
2526
+ readonly credentialRejected?: string | undefined;
2527
+ } | {
2528
+ readonly type: "system";
2529
+ readonly id: string;
2530
+ readonly tenantId: null;
2531
+ readonly scopes?: readonly string[] | undefined;
2532
+ readonly metadata?: {
2533
+ readonly [x: string]: unknown;
2534
+ } | undefined;
2535
+ } | {
2536
+ readonly type: "service";
2537
+ readonly id: string;
2538
+ readonly service: string;
2539
+ readonly tenantId: null;
2540
+ readonly scopes?: readonly string[] | undefined;
2541
+ readonly metadata?: {
2542
+ readonly [x: string]: unknown;
2543
+ } | undefined;
2544
+ };
2545
+ buy: ShopBuyInput;
2546
+ };
2547
+ options: LoyaltyStoreOptions;
2548
+ service: LoyaltyService;
2549
+ credit: (purchaseId: string, requestId?: string) => {
2550
+ tenantId: string;
2551
+ ownerId: string;
2552
+ loyaltyId: string;
2553
+ sourceId: string;
2554
+ purchaseId: string;
2555
+ requestId: string;
2556
+ };
2557
+ setAt: (value: number) => void;
2558
+ };
2559
+ purchase: ShopOrderReceipt;
2560
+ accepted: LoyaltyReceipt;
2561
+ close: Effect.Effect<void, never, never>;
2562
+ }>, EconomyError, never>;
2563
+
2564
+ export declare const loyaltyPortSystemFixture: Readonly<{
2565
+ kind: "port";
2566
+ tag: typeof LoyaltyPort;
2567
+ fakeLayer: Layer.Layer<EconomyPort | LoyaltyPort, EconomyError, never>;
2568
+ probe: {
2569
+ run: () => Effect.Effect<{
2570
+ same: boolean;
2571
+ points: string;
2572
+ tier: string;
2573
+ }, EconomyError, never>;
2574
+ expected: {
2575
+ same: boolean;
2576
+ points: string;
2577
+ tier: string;
2578
+ };
2579
+ };
2580
+ }>;
2581
+
2582
+ /** Receipt authority must use immutable accepted SQL/provider evidence, verify
2583
+ * ownership and canonical purchase identity independently of source aliases,
2584
+ * and refuse revoked/unsettled/already-refunded purchases. No player evidence. */
2585
+ declare interface LoyaltyPurchaseEvidence {
2586
+ readonly tenantId: string;
2587
+ readonly ownerId: string;
2588
+ readonly authorityId: string;
2589
+ readonly purchaseId: string;
2590
+ readonly kind: 'purchase.real' | 'purchase.virtual';
2591
+ readonly currency: string;
2592
+ readonly amount: string;
2593
+ readonly settledAt: number;
2594
+ readonly refundedAmount: '0';
2595
+ }
2596
+
2597
+ declare interface LoyaltyRebuildInput extends LoyaltyMineInput {
2598
+ readonly tenantId: string;
2599
+ readonly ownerId: string;
2600
+ }
2601
+
2602
+ declare interface LoyaltyReceipt {
2603
+ readonly operationId: string;
2604
+ readonly requestId: string;
2605
+ readonly loyaltyId: string;
2606
+ readonly entry: LoyaltyPointEntry;
2607
+ readonly points: string;
2608
+ }
2609
+
2610
+ declare interface LoyaltyReceiptInput {
2611
+ readonly requestId: string;
2612
+ }
2613
+
2614
+ declare interface LoyaltyReceiptSource {
2615
+ readonly purchase: (input: {
2616
+ readonly tenantId: string;
2617
+ readonly ownerId: string;
2618
+ readonly source: Loyalty['pointsFrom'][number];
2619
+ readonly purchaseId: string;
2620
+ }) => Effect.Effect<LoyaltyPurchaseEvidence, EconomyError>;
2621
+ readonly refund: (input: {
2622
+ readonly tenantId: string;
2623
+ readonly ownerId: string;
2624
+ readonly purchase: LoyaltyPurchaseEvidence;
2625
+ readonly refundId: string;
2626
+ }) => Effect.Effect<LoyaltyRefundEvidence, EconomyError>;
2627
+ }
2628
+
2629
+ declare interface LoyaltyRefundEvidence {
2630
+ readonly tenantId: string;
2631
+ readonly ownerId: string;
2632
+ readonly authorityId: string;
2633
+ readonly purchaseId: string;
2634
+ readonly refundId: string;
2635
+ readonly totalRefunded: string;
2636
+ readonly refundedAt: number;
2637
+ }
2638
+
2639
+ declare interface LoyaltyReverseInput {
2640
+ readonly tenantId: string;
2641
+ readonly ownerId: string;
2642
+ readonly loyaltyId: string;
2643
+ readonly originalId: string;
2644
+ readonly refundId: string;
2645
+ readonly requestId: string;
2646
+ }
2647
+
2648
+ declare interface LoyaltySeason {
2649
+ readonly id: string;
2650
+ readonly startsAt: number;
2651
+ readonly endsAt: number;
2652
+ }
2653
+
2654
+ declare interface LoyaltySeasonSource {
2655
+ readonly read: (input: {
2656
+ readonly tenantId: string;
2657
+ readonly loyaltyId: string;
2658
+ readonly at: number;
2659
+ }) => Effect.Effect<LoyaltySeason, EconomyError>;
2660
+ }
2661
+
2662
+ declare interface LoyaltyService {
2663
+ readonly list: (subject: Subject) => Effect.Effect<readonly Loyalty[], EconomyError>;
2664
+ readonly mine: (subject: Subject, input: LoyaltyMineInput) => Effect.Effect<LoyaltyState, EconomyError>;
2665
+ readonly credit: (subject: Subject, input: LoyaltyCreditInput) => Effect.Effect<LoyaltyReceipt, EconomyError>;
2666
+ readonly reverse: (subject: Subject, input: LoyaltyReverseInput) => Effect.Effect<LoyaltyReceipt, EconomyError>;
2667
+ readonly receipt: (subject: Subject, input: LoyaltyReceiptInput) => Effect.Effect<LoyaltyObservation, EconomyError>;
2668
+ readonly rebuild: (subject: Subject, input: LoyaltyRebuildInput) => Effect.Effect<LoyaltyPointProjection, EconomyError>;
2669
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
2670
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
2671
+ }
2672
+
2673
+ declare interface LoyaltyState {
2674
+ readonly loyalty: Loyalty;
2675
+ readonly rulesVersion: string;
2676
+ readonly seasonId: string | null;
2677
+ readonly points: string;
2678
+ readonly tierId: string;
2679
+ readonly perks: readonly LoyaltyPerk[];
2680
+ }
2681
+
2682
+ declare interface LoyaltyStoreOptions {
2683
+ readonly loyalty: readonly (LoyaltyDefinition | Loyalty)[];
2684
+ readonly currencies: readonly CurrencyDefinition[];
2685
+ readonly realMoney?: boolean;
2686
+ readonly receiptSource: LoyaltyReceiptSource;
2687
+ readonly seasonSource?: LoyaltySeasonSource | undefined;
2688
+ readonly version: string;
2689
+ readonly tier?: LoyaltyTierResolver | undefined;
2690
+ readonly perks?: LoyaltyPerksResolver | undefined;
2691
+ readonly allowedServices?: readonly string[] | undefined;
2692
+ readonly maxHistoryRows?: number | undefined;
2693
+ readonly maxDefinitionRows?: number | undefined;
2694
+ readonly maxSeasonRows?: number | undefined;
2695
+ readonly now?: (() => number) | undefined;
2696
+ }
2697
+
2698
+ declare interface LoyaltyTierContext {
2699
+ readonly loyalty: Loyalty;
2700
+ readonly currencies: readonly CurrencyDefinition[];
2701
+ readonly realMoney?: boolean;
2702
+ readonly points: EconomyAmountInput;
2703
+ readonly rulesVersion?: string;
2704
+ }
2705
+
2706
+ declare type LoyaltyTierResolver = (context: LoyaltyTierContext) => string;
2707
+
2708
+ export declare const loyaltyTierSystemFixture: Readonly<{
2709
+ kind: "slot";
2710
+ defaultImplementation: LoyaltyTierResolver;
2711
+ defaultProbe: {
2712
+ run: () => Effect.Effect<string, never, never>;
2713
+ expected: string;
2714
+ };
2715
+ overrideProbe: {
2716
+ run: () => Effect.Effect<string, never, never>;
2717
+ expected: string;
2718
+ };
2719
+ }>;
2720
+
2721
+ declare interface OfferExperiment {
2722
+ readonly id: string;
2723
+ readonly version: string;
2724
+ readonly from: number;
2725
+ readonly until: number;
2726
+ readonly variants: readonly {
2727
+ readonly id: string;
2728
+ readonly weight: number;
2729
+ readonly offers: readonly ShopOffer[];
2730
+ }[];
2731
+ readonly metrics: readonly string[];
2732
+ }
2733
+
2734
+ declare interface OfferExperimentContext {
2735
+ readonly experiment: OfferExperiment;
2736
+ readonly tenantId: string;
2737
+ readonly key: string;
2738
+ readonly at: number;
2739
+ readonly currencies: readonly CurrencyDefinition[];
2740
+ readonly realMoney?: boolean;
2741
+ }
2742
+
2743
+ export declare const offerExperimentFixture: () => Readonly<{
2744
+ experiment: OfferExperiment;
2745
+ currencies: {
2746
+ id: string;
2747
+ kind: "soft";
2748
+ }[];
2749
+ tenantId: "fixture";
2750
+ key: "canonical.a";
2751
+ at: 1;
2752
+ realMoney: false;
2753
+ }>;
2754
+
2755
+ /** Actual pending SQL event authority. Copies and serialized payloads cannot authorize delivery acknowledgement. */
2756
+ declare interface PendingIapRefundEvent {
2757
+ readonly [refundEvent]: true;
2758
+ readonly eventId: string;
2759
+ }
2760
+
2761
+ declare interface PersonalizedOfferContext {
2762
+ readonly personalized: PersonalizedOffers;
2763
+ readonly currencies: readonly CurrencyDefinition[];
2764
+ readonly realMoney?: boolean;
2765
+ readonly tenantId: string;
2766
+ readonly key: string;
2767
+ readonly at: number;
2768
+ readonly ownedItemIds: readonly string[];
2769
+ readonly playStyles: readonly string[];
2770
+ readonly rulesVersion?: string;
2771
+ }
2772
+
2773
+ export declare const personalizedOfferFixture: () => Readonly<{
2774
+ personalized: PersonalizedOffers;
2775
+ currencies: {
2776
+ id: string;
2777
+ kind: "soft";
2778
+ }[];
2779
+ tenantId: "fixture";
2780
+ key: "canonical.a";
2781
+ at: 1;
2782
+ ownedItemIds: string[];
2783
+ playStyles: string[];
2784
+ realMoney: false;
2785
+ }>;
2786
+
2787
+ declare interface PersonalizedOffers {
2788
+ readonly id: string;
2789
+ readonly version: string;
2790
+ readonly slots: number;
2791
+ readonly rotationMs: number;
2792
+ readonly rotationAnchorAt: number;
2793
+ readonly pool: readonly {
2794
+ readonly offer: ShopOffer;
2795
+ readonly affinities: readonly string[];
2796
+ }[];
2797
+ }
2798
+
2799
+ declare type PersonalizedOfferSelector = (input: PersonalizedSelectorContext) => readonly string[];
2800
+
2801
+ declare interface PersonalizedSelectorContext extends PersonalizedOfferContext {
2802
+ readonly rotation: number;
2803
+ readonly rulesVersion: string;
2804
+ }
2805
+
2806
+ export declare const personalizedSelectorSystemFixture: Readonly<{
2807
+ kind: "slot";
2808
+ defaultImplementation: PersonalizedOfferSelector;
2809
+ defaultProbe: {
2810
+ run: () => Effect.Effect<string[] | undefined, never, never>;
2811
+ expected: string[];
2812
+ };
2813
+ overrideProbe: {
2814
+ run: () => Effect.Effect<string[] | undefined, never, never>;
2815
+ expected: string[];
2816
+ };
2817
+ }>;
2818
+
2819
+ declare interface PiggyBank {
2820
+ readonly id: string;
2821
+ readonly version: string;
2822
+ readonly titleKey: string;
2823
+ readonly currency: string;
2824
+ readonly cap: string;
2825
+ readonly unlock: Price;
2826
+ readonly fillFrom: readonly string[];
2827
+ readonly reset: 'onUnlock';
2828
+ }
2829
+
2830
+ declare interface PiggyBankDefinition {
2831
+ readonly id: string;
2832
+ readonly version?: string | undefined;
2833
+ readonly titleKey: string;
2834
+ readonly currency: string;
2835
+ readonly cap: EconomyAmountInput;
2836
+ readonly unlock: PriceDefinition;
2837
+ readonly fillFrom: readonly string[];
2838
+ readonly reset?: 'onUnlock' | undefined;
2839
+ }
2840
+
2841
+ export declare const piggyBankFixture: () => Readonly<{
2842
+ bank: PiggyBank;
2843
+ currencies: {
2844
+ id: string;
2845
+ kind: "soft";
2846
+ cap: string;
2847
+ }[];
2848
+ available: "13";
2849
+ acceptedAmount: "13";
2850
+ realMoney: false;
2851
+ }>;
2852
+
2853
+ export declare const piggyBanksFakeLayer: Layer.Layer<EconomyPort | PiggyBanksPort, EconomyError, never>;
2854
+
2855
+ declare class PiggyBanksPort extends PiggyBanksPort_base {
2856
+ }
2857
+
2858
+ declare const PiggyBanksPort_base: Context.ServiceClass<PiggyBanksPort, "@voltro/PiggyBanksPort", PiggyBanksService>;
2859
+
2860
+ export declare const piggyBanksPortSystemFixture: Readonly<{
2861
+ kind: "port";
2862
+ tag: typeof PiggyBanksPort;
2863
+ fakeLayer: Layer.Layer<EconomyPort | PiggyBanksPort, EconomyError, never>;
2864
+ probe: {
2865
+ run: () => Effect.Effect<{
2866
+ same: boolean;
2867
+ observed: "fulfilled" | "not-observed";
2868
+ available: string;
2869
+ cycles: number;
2870
+ balance: string;
2871
+ }, EconomyError, never>;
2872
+ expected: {
2873
+ same: boolean;
2874
+ observed: string;
2875
+ available: string;
2876
+ cycles: number;
2877
+ balance: string;
2878
+ };
2879
+ };
2880
+ }>;
2881
+
2882
+ declare interface PiggyBanksService {
2883
+ readonly list: (subject: Subject) => Effect.Effect<readonly PiggyView[], EconomyError>;
2884
+ readonly fill: (subject: Subject, input: PiggyFillInput) => Effect.Effect<PiggyReceipt, EconomyError>;
2885
+ readonly unlock: (subject: Subject, input: PiggyUnlockInput) => Effect.Effect<PiggyReceipt, EconomyError>;
2886
+ readonly receipt: (subject: Subject, input: {
2887
+ readonly requestId: string;
2888
+ }) => Effect.Effect<PiggyObservation, EconomyError>;
2889
+ readonly rebuild: (subject: Subject, input: {
2890
+ readonly tenantId: string;
2891
+ readonly ownerId: string;
2892
+ readonly bankId: string;
2893
+ }) => Effect.Effect<PiggyProjection, EconomyError>;
2894
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
2895
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
2896
+ }
2897
+
2898
+ declare type PiggyEvent = {
2899
+ readonly id: string;
2900
+ readonly sequence: number;
2901
+ readonly kind: 'fill';
2902
+ readonly at: number;
2903
+ readonly authorityId: string;
2904
+ readonly eventId: string;
2905
+ readonly sourceId: string;
2906
+ readonly acceptedAmount: string;
2907
+ readonly delta: string;
2908
+ } | {
2909
+ readonly id: string;
2910
+ readonly sequence: number;
2911
+ readonly kind: 'unlock';
2912
+ readonly at: number;
2913
+ readonly amount: string;
2914
+ readonly remaining: string;
2915
+ };
2916
+
2917
+ declare interface PiggyFillContext extends PiggyRuleContext {
2918
+ readonly acceptedAmount: EconomyAmountInput;
2919
+ }
2920
+
2921
+ declare interface PiggyFillEvidence {
2922
+ readonly tenantId: string;
2923
+ readonly ownerId: string;
2924
+ readonly authorityId: string;
2925
+ readonly eventId: string;
2926
+ readonly sourceId: string;
2927
+ readonly amount: string;
2928
+ readonly acceptedAt: number;
2929
+ }
2930
+
2931
+ declare interface PiggyFillInput {
2932
+ readonly tenantId: string;
2933
+ readonly ownerId: string;
2934
+ readonly bankId: string;
2935
+ readonly sourceId: string;
2936
+ readonly eventId: string;
2937
+ readonly requestId: string;
2938
+ }
2939
+
2940
+ declare type PiggyFillPolicy = (context: PiggyFillContext) => EconomyAmountInput;
2941
+
2942
+ /** Actual accepted server events only. Canonical authority/event identity must
2943
+ * stay independent of aliases and be scoped/verified by the application's
2944
+ * authoritative event source. No player match result or amount is accepted. */
2945
+ declare interface PiggyFillSource {
2946
+ readonly read: (input: {
2947
+ readonly tenantId: string;
2948
+ readonly ownerId: string;
2949
+ readonly bank: PiggyBank;
2950
+ readonly sourceId: string;
2951
+ readonly eventId: string;
2952
+ }) => Effect.Effect<PiggyFillEvidence, EconomyError>;
2953
+ }
2954
+
2955
+ export declare const piggyFillSystemFixture: Readonly<{
2956
+ kind: "slot";
2957
+ defaultImplementation: PiggyFillPolicy;
2958
+ defaultProbe: {
2959
+ run: () => Effect.Effect<string, never, never>;
2960
+ expected: string;
2961
+ };
2962
+ overrideProbe: {
2963
+ run: () => Effect.Effect<string, never, never>;
2964
+ expected: string;
2965
+ };
2966
+ }>;
2967
+
2968
+ declare type PiggyObservation = {
2969
+ readonly requestId: string;
2970
+ readonly state: 'not-observed';
2971
+ } | {
2972
+ readonly requestId: string;
2973
+ readonly state: 'fulfilled';
2974
+ readonly receipt: PiggyReceipt;
2975
+ };
2976
+
2977
+ /** Fresh installed port using actual wallet writers. Receipt and eligibility
2978
+ * sources are explicitly named fixture seams, never production Match facts. */
2979
+ export declare const piggyPortFixture: Effect.Effect<Readonly<{
2980
+ store: InMemoryDataStore;
2981
+ base: {
2982
+ economy: EconomySqlService;
2983
+ user: {
2984
+ readonly type: "user";
2985
+ readonly id: string;
2986
+ readonly tenantId: string;
2987
+ readonly kind?: "guest" | undefined;
2988
+ readonly scopes?: readonly string[] | undefined;
2989
+ readonly metadata?: {
2990
+ readonly [x: string]: unknown;
2991
+ } | undefined;
2992
+ } | {
2993
+ readonly type: "apiKey";
2994
+ readonly id: string;
2995
+ readonly tenantId: string;
2996
+ readonly scopes?: readonly string[] | undefined;
2997
+ readonly metadata?: {
2998
+ readonly [x: string]: unknown;
2999
+ } | undefined;
3000
+ } | {
3001
+ readonly type: "serviceAccount";
3002
+ readonly id: string;
3003
+ readonly tenantId: string;
3004
+ readonly scopes?: readonly string[] | undefined;
3005
+ readonly metadata?: {
3006
+ readonly [x: string]: unknown;
3007
+ } | undefined;
3008
+ readonly auditActorId?: string | null;
3009
+ } | {
3010
+ readonly type: "anonymous";
3011
+ readonly id: null;
3012
+ readonly tenantId: string | null;
3013
+ readonly credentialRejected?: string | undefined;
3014
+ } | {
3015
+ readonly type: "system";
3016
+ readonly id: string;
3017
+ readonly tenantId: null;
3018
+ readonly scopes?: readonly string[] | undefined;
3019
+ readonly metadata?: {
3020
+ readonly [x: string]: unknown;
3021
+ } | undefined;
3022
+ } | {
3023
+ readonly type: "service";
3024
+ readonly id: string;
3025
+ readonly service: string;
3026
+ readonly tenantId: null;
3027
+ readonly scopes?: readonly string[] | undefined;
3028
+ readonly metadata?: {
3029
+ readonly [x: string]: unknown;
3030
+ } | undefined;
3031
+ };
3032
+ options: PiggyStoreOptions;
3033
+ service: PiggyBanksService;
3034
+ fill: (eventId: string, requestId?: string) => {
3035
+ tenantId: string;
3036
+ ownerId: string;
3037
+ bankId: string;
3038
+ sourceId: string;
3039
+ eventId: string;
3040
+ requestId: string;
3041
+ };
3042
+ setAt: (value: number) => void;
3043
+ };
3044
+ plugin: EconomyPlugin;
3045
+ view: PiggyView;
3046
+ unlock: {
3047
+ bankId: string;
3048
+ bankVersion: string;
3049
+ bankDigest: string;
3050
+ requestId: string;
3051
+ currency: string | undefined;
3052
+ };
3053
+ close: Effect.Effect<void, never, never>;
3054
+ }>, EconomyError, never>;
3055
+
3056
+ declare interface PiggyProjection {
3057
+ readonly events: readonly PiggyEvent[];
3058
+ readonly available: string;
3059
+ readonly cycles: number;
3060
+ }
3061
+
3062
+ declare type PiggyReceipt = {
3063
+ readonly operationId: string;
3064
+ readonly requestId: string;
3065
+ readonly bankId: string;
3066
+ readonly event: Extract<PiggyEvent, {
3067
+ kind: 'fill';
3068
+ }>;
3069
+ readonly available: string;
3070
+ } | {
3071
+ readonly operationId: string;
3072
+ readonly requestId: string;
3073
+ readonly bankId: string;
3074
+ readonly event: Extract<PiggyEvent, {
3075
+ kind: 'unlock';
3076
+ }>;
3077
+ readonly available: string;
3078
+ readonly value: ShopOrderReceipt;
3079
+ };
3080
+
3081
+ declare type PiggyResetPolicy = (context: PiggyRuleContext) => EconomyAmountInput;
3082
+
3083
+ export declare const piggyResetSystemFixture: Readonly<{
3084
+ kind: "slot";
3085
+ defaultImplementation: PiggyResetPolicy;
3086
+ defaultProbe: {
3087
+ run: () => Effect.Effect<Readonly<{
3088
+ remaining: string;
3089
+ amount: string;
3090
+ }>, never, never>;
3091
+ expected: {
3092
+ remaining: string;
3093
+ amount: string;
3094
+ };
3095
+ };
3096
+ overrideProbe: {
3097
+ run: () => Effect.Effect<Readonly<{
3098
+ remaining: string;
3099
+ amount: string;
3100
+ }>, never, never>;
3101
+ expected: {
3102
+ remaining: string;
3103
+ amount: string;
3104
+ };
3105
+ };
3106
+ }>;
3107
+
3108
+ declare interface PiggyRuleContext {
3109
+ readonly bank: PiggyBank;
3110
+ readonly currencies: readonly CurrencyDefinition[];
3111
+ readonly realMoney?: boolean;
3112
+ readonly available: EconomyAmountInput;
3113
+ readonly rulesVersion?: string;
3114
+ }
3115
+
3116
+ declare interface PiggyStoreOptions {
3117
+ readonly banks: readonly (PiggyBankDefinition | PiggyBank)[];
3118
+ readonly currencies: readonly CurrencyDefinition[];
3119
+ readonly realMoney?: boolean;
3120
+ readonly economy: Pick<EconomySqlService, 'prepareTransfer'>;
3121
+ readonly fillSource: PiggyFillSource;
3122
+ readonly unlockSource: PiggyUnlockSource;
3123
+ readonly version: string;
3124
+ readonly fill?: PiggyFillPolicy | undefined;
3125
+ readonly reset?: PiggyResetPolicy | undefined;
3126
+ readonly priceResolver?: PriceResolver | undefined;
3127
+ readonly systemAccount?: string | undefined;
3128
+ readonly allowedServices?: readonly string[] | undefined;
3129
+ readonly maxHistoryRows?: number | undefined;
3130
+ readonly now?: (() => number) | undefined;
3131
+ }
3132
+
3133
+ declare interface PiggyUnlockInput {
3134
+ readonly bankId: string;
3135
+ readonly bankVersion: string;
3136
+ readonly bankDigest: string;
3137
+ readonly requestId: string;
3138
+ readonly currency?: string | undefined;
3139
+ }
3140
+
3141
+ declare interface PiggyUnlockSource {
3142
+ readonly read: (input: {
3143
+ readonly subject: Subject;
3144
+ readonly bank: PiggyBank;
3145
+ readonly at: number;
3146
+ }) => Effect.Effect<{
3147
+ readonly eligible: boolean;
3148
+ readonly adjustments: readonly VirtualPriceAdjustment[];
3149
+ }, EconomyError>;
3150
+ }
3151
+
3152
+ declare interface PiggyView {
3153
+ readonly bank: PiggyBank;
3154
+ readonly bankDigest: string;
3155
+ readonly available: string;
3156
+ readonly cycles: number;
3157
+ }
3158
+
3159
+ declare type Price = {
3160
+ readonly kind: 'virtual';
3161
+ readonly options: readonly ResolvedVirtualPriceOption[];
3162
+ } | ({
3163
+ readonly kind: 'real';
3164
+ } & RealPriceDefinition) | {
3165
+ readonly kind: 'mixed';
3166
+ readonly real: RealPriceDefinition;
3167
+ readonly virtual: readonly ResolvedVirtualPriceOption[];
3168
+ } | {
3169
+ readonly kind: 'free';
3170
+ };
3171
+
3172
+ declare type PriceDefinition = {
3173
+ readonly kind: 'virtual';
3174
+ readonly options: readonly VirtualPriceOption[];
3175
+ } | ({
3176
+ readonly kind: 'real';
3177
+ } & RealPriceDefinition) | {
3178
+ readonly kind: 'mixed';
3179
+ readonly real: RealPriceDefinition;
3180
+ readonly virtual: readonly VirtualPriceOption[];
3181
+ } | {
3182
+ readonly kind: 'free';
3183
+ };
3184
+
3185
+ declare type PriceResolver = (context: PriceResolverContext) => Price;
3186
+
3187
+ declare interface PriceResolverContext {
3188
+ readonly price: Price;
3189
+ readonly currencies: readonly ResolvedCurrency[];
3190
+ readonly realMoney: boolean;
3191
+ readonly adjustments: readonly Readonly<{
3192
+ currency: string;
3193
+ discountBps: number;
3194
+ minimum: string;
3195
+ }>[];
3196
+ }
3197
+
3198
+ /** Hypothetical fixture configuration, never a deployment pricing decision. */
3199
+ export declare const priceResolverFixture: () => Readonly<{
3200
+ resolver: PriceResolver;
3201
+ context: PriceResolverContext;
3202
+ }>;
3203
+
3204
+ export declare const priceResolverSystemFixture: Readonly<{
3205
+ kind: "slot";
3206
+ defaultImplementation: PriceResolver;
3207
+ defaultProbe: {
3208
+ run: () => Effect.Effect< Price, never, never>;
3209
+ expected: {
3210
+ kind: string;
3211
+ options: {
3212
+ currency: string;
3213
+ amount: string;
3214
+ }[];
3215
+ };
3216
+ };
3217
+ overrideProbe: {
3218
+ run: () => Effect.Effect< Price, never, never>;
3219
+ expected: {
3220
+ kind: string;
3221
+ options: {
3222
+ currency: string;
3223
+ amount: string;
3224
+ }[];
3225
+ };
3226
+ };
3227
+ }>;
3228
+
3229
+ declare interface PromoCode {
3230
+ readonly id: string;
3231
+ readonly version: string;
3232
+ readonly campaign: string;
3233
+ readonly items: readonly {
3234
+ readonly itemId: string;
3235
+ readonly qty: number;
3236
+ }[];
3237
+ readonly currencies: readonly {
3238
+ readonly currency: string;
3239
+ readonly amount: string;
3240
+ }[];
3241
+ readonly from: number | null;
3242
+ readonly until: number | null;
3243
+ readonly limits: {
3244
+ readonly global: number;
3245
+ readonly perPlayer: number;
3246
+ };
3247
+ }
3248
+
3249
+ declare interface RealPriceDefinition {
3250
+ /** Integer minor-unit references; verified store receipts remain authoritative. */
3251
+ readonly prices: Readonly<Record<string, number>>;
3252
+ readonly storeProductIds: Partial<Readonly<Record<EconomyChannel, string>>>;
3253
+ }
3254
+
3255
+ declare interface RedeemCodeInput {
3256
+ readonly code: string;
3257
+ readonly requestId: string;
3258
+ }
3259
+
3260
+ declare const refundEvent: unique symbol;
3261
+
3262
+ declare interface ResolvedCurrency {
3263
+ readonly id: string;
3264
+ readonly kind: 'soft' | 'hard';
3265
+ readonly cap: string;
3266
+ readonly premium: boolean;
3267
+ readonly display: {
3268
+ readonly nameKey: string;
3269
+ } | null;
3270
+ }
3271
+
3272
+ declare interface ResolvedVirtualPriceOption {
3273
+ readonly currency: string;
3274
+ readonly amount: string;
3275
+ }
3276
+
3277
+ declare interface ShopAudit {
3278
+ readonly matches: boolean;
3279
+ readonly acceptedOrders: number;
3280
+ readonly counters: number;
3281
+ }
3282
+
3283
+ declare interface ShopBuyInput {
3284
+ readonly offerId: string;
3285
+ readonly offerVersion: string;
3286
+ readonly offerDigest: string;
3287
+ readonly requestId: string;
3288
+ readonly currency?: string | undefined;
3289
+ readonly qty?: number | undefined;
3290
+ }
3291
+
3292
+ declare interface ShopContextInput {
3293
+ readonly subject: Subject;
3294
+ readonly offer: ShopOffer;
3295
+ readonly at: number;
3296
+ }
3297
+
3298
+ declare interface ShopContextSnapshot {
3299
+ readonly eligible: boolean;
3300
+ readonly adjustments: readonly VirtualPriceAdjustment[];
3301
+ }
3302
+
3303
+ /** Trusted external profile/segment/flag facts; never inside a replayable SQL callback. */
3304
+ declare interface ShopContextSource {
3305
+ readonly read: (input: ShopContextInput) => Effect.Effect<ShopContextSnapshot, EconomyError>;
3306
+ }
3307
+
3308
+ declare interface ShopCurrencyGrant {
3309
+ readonly currency: string;
3310
+ readonly amount: EconomyAmountInput;
3311
+ }
3312
+
3313
+ export declare const shopFakeLayer: Layer.Layer<ShopPort | EconomyPort, never, never>;
3314
+
3315
+ declare interface ShopItemGrant {
3316
+ readonly itemId: string;
3317
+ readonly qty: number;
3318
+ }
3319
+
3320
+ declare interface ShopOffer {
3321
+ readonly id: string;
3322
+ readonly version: string;
3323
+ readonly titleKey: string;
3324
+ readonly price: Price;
3325
+ readonly items: readonly ShopItemGrant[];
3326
+ readonly currencies: readonly {
3327
+ readonly currency: string;
3328
+ readonly amount: string;
3329
+ }[];
3330
+ readonly from: number | null;
3331
+ readonly until: number | null;
3332
+ readonly segment: string | null;
3333
+ readonly flag: string | null;
3334
+ readonly countries: readonly string[];
3335
+ readonly limits: {
3336
+ readonly perPlayer: number | null;
3337
+ readonly perDay: number | null;
3338
+ readonly global: number | null;
3339
+ };
3340
+ readonly oneTime: boolean;
3341
+ readonly maxQuantity: number;
3342
+ }
3343
+
3344
+ declare interface ShopOfferDefinition {
3345
+ readonly id: string;
3346
+ readonly version?: string | undefined;
3347
+ readonly titleKey: string;
3348
+ readonly price: PriceDefinition;
3349
+ readonly items?: readonly ShopItemGrant[] | undefined;
3350
+ readonly currencies?: readonly ShopCurrencyGrant[] | undefined;
3351
+ readonly from?: number | null | undefined;
3352
+ readonly until?: number | null | undefined;
3353
+ readonly segment?: string | null | undefined;
3354
+ readonly flag?: string | null | undefined;
3355
+ readonly countries?: readonly string[] | undefined;
3356
+ readonly limits?: {
3357
+ readonly perPlayer?: number | null | undefined;
3358
+ readonly perDay?: number | null | undefined;
3359
+ readonly global?: number | null | undefined;
3360
+ } | undefined;
3361
+ readonly oneTime?: boolean | undefined;
3362
+ readonly maxQuantity?: number | undefined;
3363
+ }
3364
+
3365
+ /** Immediate virtual Shop example, not an Owner pricing choice. */
3366
+ export declare const shopOfferFixture: () => ShopOffer;
3367
+
3368
+ declare interface ShopOfferView {
3369
+ readonly offerDigest: string;
3370
+ readonly offer: ShopOffer;
3371
+ readonly price: Price;
3372
+ }
3373
+
3374
+ declare interface ShopOrderReceipt {
3375
+ readonly orderId: string;
3376
+ readonly offerId: string;
3377
+ readonly offerVersion: string;
3378
+ readonly offerDigest: string;
3379
+ readonly quantity: number;
3380
+ readonly at: number;
3381
+ readonly pricePaid: {
3382
+ readonly kind: 'free';
3383
+ } | {
3384
+ readonly kind: 'virtual';
3385
+ readonly currency: string;
3386
+ readonly amount: string;
3387
+ };
3388
+ readonly transfers: readonly {
3389
+ readonly txId: string;
3390
+ readonly currency: string;
3391
+ readonly amount: string;
3392
+ readonly at: number;
3393
+ }[];
3394
+ readonly grants: readonly {
3395
+ readonly operationId: string;
3396
+ readonly kind: 'grant';
3397
+ readonly changes: readonly {
3398
+ readonly instanceId: string;
3399
+ readonly delta: number;
3400
+ }[];
3401
+ readonly at: number;
3402
+ }[];
3403
+ }
3404
+
3405
+ declare class ShopPort extends ShopPort_base {
3406
+ }
3407
+
3408
+ declare const ShopPort_base: Context.ServiceClass<ShopPort, "@voltro/ShopPort", ShopService>;
3409
+
3410
+ /** Actual installed memory-store Shop with the borrowed wallet and inventory
3411
+ * writers of the shop fixture; no payment provider or production catalog. */
3412
+ export declare const shopPortFixture: Effect.Effect<Readonly<{
3413
+ store: InMemoryDataStore;
3414
+ plugin: EconomyPlugin;
3415
+ base: {
3416
+ economy: EconomySqlService;
3417
+ inventory: InventorySqlService;
3418
+ catalog: Readonly<{
3419
+ read: (scope: CatalogScope) => Effect.Effect< CatalogSnapshot, InventoryError>;
3420
+ importSnapshot: (subject: Subject, input: CatalogImport) => Effect.Effect< CatalogSnapshot, InventoryError>;
3421
+ }>;
3422
+ options: ShopStoreOptions;
3423
+ shop: ShopSqlService;
3424
+ user: {
3425
+ readonly type: "user";
3426
+ readonly id: string;
3427
+ readonly tenantId: string;
3428
+ readonly kind?: "guest" | undefined;
3429
+ readonly scopes?: readonly string[] | undefined;
3430
+ readonly metadata?: {
3431
+ readonly [x: string]: unknown;
3432
+ } | undefined;
3433
+ } | {
3434
+ readonly type: "apiKey";
3435
+ readonly id: string;
3436
+ readonly tenantId: string;
3437
+ readonly scopes?: readonly string[] | undefined;
3438
+ readonly metadata?: {
3439
+ readonly [x: string]: unknown;
3440
+ } | undefined;
3441
+ } | {
3442
+ readonly type: "serviceAccount";
3443
+ readonly id: string;
3444
+ readonly tenantId: string;
3445
+ readonly scopes?: readonly string[] | undefined;
3446
+ readonly metadata?: {
3447
+ readonly [x: string]: unknown;
3448
+ } | undefined;
3449
+ readonly auditActorId?: string | null;
3450
+ } | {
3451
+ readonly type: "anonymous";
3452
+ readonly id: null;
3453
+ readonly tenantId: string | null;
3454
+ readonly credentialRejected?: string | undefined;
3455
+ } | {
3456
+ readonly type: "system";
3457
+ readonly id: string;
3458
+ readonly tenantId: null;
3459
+ readonly scopes?: readonly string[] | undefined;
3460
+ readonly metadata?: {
3461
+ readonly [x: string]: unknown;
3462
+ } | undefined;
3463
+ } | {
3464
+ readonly type: "service";
3465
+ readonly id: string;
3466
+ readonly service: string;
3467
+ readonly tenantId: null;
3468
+ readonly scopes?: readonly string[] | undefined;
3469
+ readonly metadata?: {
3470
+ readonly [x: string]: unknown;
3471
+ } | undefined;
3472
+ };
3473
+ buy: ShopBuyInput;
3474
+ };
3475
+ close: Effect.Effect<void, never, never>;
3476
+ }>, never, never>;
3477
+
3478
+ export declare const shopPortSystemFixture: Readonly<{
3479
+ kind: "port";
3480
+ tag: typeof ShopPort;
3481
+ fakeLayer: Layer.Layer<ShopPort | EconomyPort, never, never>;
3482
+ probe: {
3483
+ run: () => Effect.Effect<{
3484
+ same: boolean;
3485
+ quantity: number;
3486
+ balance: string | undefined;
3487
+ }, EconomyError, never>;
3488
+ expected: {
3489
+ same: boolean;
3490
+ quantity: number;
3491
+ balance: string;
3492
+ };
3493
+ };
3494
+ }>;
3495
+
3496
+ /** Nonterminal observations never authorize a new request identity or prove that no purchase can commit. */
3497
+ declare type ShopPurchaseObservation = {
3498
+ readonly requestId: string;
3499
+ readonly state: 'not-observed' | 'pending';
3500
+ } | {
3501
+ readonly requestId: string;
3502
+ readonly state: 'fulfilled';
3503
+ readonly receipt: ShopOrderReceipt;
3504
+ };
3505
+
3506
+ declare interface ShopRules {
3507
+ readonly offers: readonly (ShopOfferDefinition | ShopOffer)[];
3508
+ readonly priceResolver?: PriceResolver | undefined;
3509
+ readonly contextSource?: ShopContextSource | undefined;
3510
+ readonly version?: string | undefined;
3511
+ readonly systemAccount?: string | undefined;
3512
+ readonly maxOrders?: number | undefined;
3513
+ readonly maxAuditRows?: number | undefined;
3514
+ readonly now?: (() => number) | undefined;
3515
+ }
3516
+
3517
+ declare interface ShopService {
3518
+ readonly offers: (subject: Subject) => Effect.Effect<readonly ShopOfferView[], EconomyError>;
3519
+ readonly buy: (subject: Subject, input: ShopBuyInput) => Effect.Effect<ShopOrderReceipt, EconomyError>;
3520
+ readonly receipt: (subject: Subject, input: ShopBuyInput) => Effect.Effect<ShopPurchaseObservation, EconomyError>;
3521
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
3522
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
3523
+ }
3524
+
3525
+ declare interface ShopSqlService extends ShopService {
3526
+ readonly auditShop: (subject: Subject, tenantId: string) => Effect.Effect<ShopAudit, EconomyError>;
3527
+ readonly rebuildShop: (subject: Subject, tenantId: string) => Effect.Effect<ShopAudit, EconomyError>;
3528
+ }
3529
+
3530
+ declare interface ShopStoreOptions extends ShopRules {
3531
+ readonly currencies: readonly CurrencyDefinition[];
3532
+ readonly realMoney?: boolean;
3533
+ readonly economy: Pick<EconomySqlService, 'prepareTransfer'>;
3534
+ readonly inventory?: Pick<InventorySqlService, 'prepareGrant'> | undefined;
3535
+ }
3536
+
3537
+ declare interface SpendBudget {
3538
+ readonly daily: string | null;
3539
+ readonly monthly: string | null;
3540
+ readonly allowed: boolean;
3541
+ }
3542
+
3543
+ declare interface SpendFacts extends SpendFactsInput {
3544
+ readonly allowed: boolean;
3545
+ readonly minor: boolean;
3546
+ readonly observedAt: number;
3547
+ readonly daily?: EconomyAmountInput | null | undefined;
3548
+ readonly monthly?: EconomyAmountInput | null;
3549
+ readonly blockedUntil?: number | null;
3550
+ }
3551
+
3552
+ declare interface SpendFactsInput {
3553
+ readonly tenantId: string;
3554
+ readonly ownerId: string;
3555
+ readonly ownerKind: 'player' | 'guild';
3556
+ readonly currency: string;
3557
+ readonly at: number;
3558
+ }
3559
+
3560
+ declare interface SpendLimit {
3561
+ readonly currency: string;
3562
+ readonly daily: string | null;
3563
+ readonly monthly: string | null;
3564
+ readonly minors: {
3565
+ readonly daily: string | null;
3566
+ readonly monthly: string | null;
3567
+ } | null;
3568
+ }
3569
+
3570
+ declare interface SpendLimitContext {
3571
+ readonly limit: SpendLimit;
3572
+ readonly facts: SpendFacts;
3573
+ readonly version: string;
3574
+ }
3575
+
3576
+ export declare const spendLimitFixture: () => Readonly<{
3577
+ limit: SpendLimit;
3578
+ facts: Readonly<{
3579
+ tenantId: "fixture";
3580
+ ownerId: "a";
3581
+ ownerKind: "player";
3582
+ currency: "test.gold";
3583
+ at: 100;
3584
+ observedAt: 100;
3585
+ allowed: true;
3586
+ minor: true;
3587
+ }>;
3588
+ version: "test.spend.v1";
3589
+ }>;
3590
+
3591
+ declare type SpendLimitPolicy = (input: SpendLimitContext) => SpendBudget;
3592
+
3593
+ export declare const spendLimitPolicySystemFixture: Readonly<{
3594
+ kind: "slot";
3595
+ defaultImplementation: SpendLimitPolicy;
3596
+ defaultProbe: {
3597
+ run: () => Effect.Effect< SpendBudget, never, never>;
3598
+ expected: {
3599
+ daily: string;
3600
+ monthly: string;
3601
+ allowed: boolean;
3602
+ };
3603
+ };
3604
+ overrideProbe: {
3605
+ run: () => Effect.Effect< SpendBudget, never, never>;
3606
+ expected: {
3607
+ daily: string;
3608
+ monthly: string;
3609
+ allowed: boolean;
3610
+ };
3611
+ };
3612
+ }>;
3613
+
3614
+ export declare const spendLimitsFakeLayer: Layer.Layer<EconomyPort, EconomyError, never>;
3615
+
3616
+ /** Actual fresh installed wallet port. The age/parent source is a named test
3617
+ * authority seam; a fake Layer cannot verify a production identity provider. */
3618
+ export declare const spendLimitsPortFixture: Effect.Effect<Readonly<{
3619
+ store: InMemoryDataStore;
3620
+ plugin: EconomyPlugin;
3621
+ currencies: {
3622
+ id: string;
3623
+ kind: "soft";
3624
+ }[];
3625
+ who: {
3626
+ readonly type: "user";
3627
+ readonly id: string;
3628
+ readonly tenantId: string;
3629
+ readonly kind?: "guest" | undefined;
3630
+ readonly scopes?: readonly string[] | undefined;
3631
+ readonly metadata?: {
3632
+ readonly [x: string]: unknown;
3633
+ } | undefined;
3634
+ } | {
3635
+ readonly type: "apiKey";
3636
+ readonly id: string;
3637
+ readonly tenantId: string;
3638
+ readonly scopes?: readonly string[] | undefined;
3639
+ readonly metadata?: {
3640
+ readonly [x: string]: unknown;
3641
+ } | undefined;
3642
+ } | {
3643
+ readonly type: "serviceAccount";
3644
+ readonly id: string;
3645
+ readonly tenantId: string;
3646
+ readonly scopes?: readonly string[] | undefined;
3647
+ readonly metadata?: {
3648
+ readonly [x: string]: unknown;
3649
+ } | undefined;
3650
+ readonly auditActorId?: string | null;
3651
+ } | {
3652
+ readonly type: "anonymous";
3653
+ readonly id: null;
3654
+ readonly tenantId: string | null;
3655
+ readonly credentialRejected?: string | undefined;
3656
+ } | {
3657
+ readonly type: "system";
3658
+ readonly id: string;
3659
+ readonly tenantId: null;
3660
+ readonly scopes?: readonly string[] | undefined;
3661
+ readonly metadata?: {
3662
+ readonly [x: string]: unknown;
3663
+ } | undefined;
3664
+ } | {
3665
+ readonly type: "service";
3666
+ readonly id: string;
3667
+ readonly service: string;
3668
+ readonly tenantId: null;
3669
+ readonly scopes?: readonly string[] | undefined;
3670
+ readonly metadata?: {
3671
+ readonly [x: string]: unknown;
3672
+ } | undefined;
3673
+ };
3674
+ close: Effect.Effect<void, never, never>;
3675
+ }>, EconomyError, never>;
3676
+
3677
+ export declare const spendLimitsPortSystemFixture: Readonly<{
3678
+ kind: "port";
3679
+ tag: typeof EconomyPort;
3680
+ fakeLayer: Layer.Layer<EconomyPort, EconomyError, never>;
3681
+ probe: {
3682
+ run: () => Effect.Effect<{
3683
+ same: boolean;
3684
+ refusal: string;
3685
+ balance: string;
3686
+ }, EconomyError, never>;
3687
+ expected: {
3688
+ same: boolean;
3689
+ refusal: string;
3690
+ balance: string;
3691
+ };
3692
+ };
3693
+ }>;
3694
+
3695
+ declare interface Subscription {
3696
+ readonly id: string;
3697
+ readonly version: string;
3698
+ readonly titleKey: string;
3699
+ readonly price: Price;
3700
+ readonly period: Readonly<{
3701
+ unit: 'day' | 'week' | 'month';
3702
+ count: number;
3703
+ }>;
3704
+ readonly grants: Readonly<{
3705
+ onPurchase: SubscriptionGift;
3706
+ daily: SubscriptionGift;
3707
+ }>;
3708
+ readonly graceDays: number;
3709
+ readonly coolingOff: Readonly<{
3710
+ days: number;
3711
+ overrideReason: string | null;
3712
+ }>;
3713
+ readonly disclosure: Readonly<{
3714
+ durationKey: string;
3715
+ renewalKey: string;
3716
+ cancellationPath: string;
3717
+ }>;
3718
+ }
3719
+
3720
+ /** Consent and eligibility are application authority, never inferred from a
3721
+ * price declaration or a player boolean. No automatic provider acquisition. */
3722
+ declare interface SubscriptionAccessSource {
3723
+ readonly read: (input: {
3724
+ readonly tenantId: string;
3725
+ readonly ownerId: string;
3726
+ readonly subscription: Subscription;
3727
+ readonly at: number;
3728
+ readonly kind: 'start' | 'renew';
3729
+ }) => Effect.Effect<{
3730
+ readonly eligible: boolean;
3731
+ readonly renewalConsent: boolean;
3732
+ readonly adjustments: readonly VirtualPriceAdjustment[];
3733
+ }, EconomyError>;
3734
+ }
3735
+
3736
+ declare interface SubscriptionActiveView {
3737
+ readonly entitlementId: string;
3738
+ readonly subscription: Subscription;
3739
+ readonly subscriptionDigest: string;
3740
+ readonly originAt: number;
3741
+ readonly periodIndex: number;
3742
+ readonly window: SubscriptionEntitlement;
3743
+ readonly currency: string | null;
3744
+ readonly renews: boolean;
3745
+ readonly cancelledAt: number | null;
3746
+ }
3747
+
3748
+ declare interface SubscriptionCancelInput {
3749
+ readonly entitlementId: string;
3750
+ readonly requestId: string;
3751
+ }
3752
+
3753
+ declare interface SubscriptionCancellationReceipt {
3754
+ readonly operationId: string;
3755
+ readonly entitlementId: string;
3756
+ readonly at: number;
3757
+ readonly renews: false;
3758
+ }
3759
+
3760
+ declare interface SubscriptionDailyContext {
3761
+ readonly entitlement: SubscriptionEntitlement;
3762
+ readonly at: number;
3763
+ readonly claimedDays: readonly number[];
3764
+ readonly rulesVersion?: string;
3765
+ }
3766
+
3767
+ declare type SubscriptionDailyGrantPolicy = (input: SubscriptionDailyContext) => boolean;
3768
+
3769
+ declare interface SubscriptionDailyInput {
3770
+ readonly entitlementId: string;
3771
+ readonly requestId: string;
3772
+ }
3773
+
3774
+ export declare const subscriptionDailySystemFixture: Readonly<{
3775
+ kind: "slot";
3776
+ defaultImplementation: SubscriptionDailyGrantPolicy;
3777
+ defaultProbe: {
3778
+ run: () => Effect.Effect<Readonly<{
3779
+ eligible: boolean;
3780
+ day: number;
3781
+ }>, never, never>;
3782
+ expected: {
3783
+ eligible: boolean;
3784
+ day: number;
3785
+ };
3786
+ };
3787
+ overrideProbe: {
3788
+ run: () => Effect.Effect<Readonly<{
3789
+ eligible: boolean;
3790
+ day: number;
3791
+ }>, never, never>;
3792
+ expected: {
3793
+ eligible: boolean;
3794
+ day: number;
3795
+ };
3796
+ };
3797
+ }>;
3798
+
3799
+ declare interface SubscriptionDefinition {
3800
+ readonly id: string;
3801
+ readonly version?: string | undefined;
3802
+ readonly titleKey: string;
3803
+ readonly price: PriceDefinition;
3804
+ readonly period: {
3805
+ readonly unit: 'day' | 'week' | 'month';
3806
+ readonly count: number;
3807
+ };
3808
+ readonly grants: {
3809
+ readonly onPurchase?: SubscriptionGiftDefinition;
3810
+ readonly daily?: SubscriptionGiftDefinition | undefined;
3811
+ };
3812
+ readonly graceDays?: number;
3813
+ readonly coolingOff?: {
3814
+ readonly days: number;
3815
+ readonly overrideReason?: string | null;
3816
+ };
3817
+ readonly disclosure: {
3818
+ readonly durationKey: string;
3819
+ readonly renewalKey: string;
3820
+ readonly cancellationPath: string;
3821
+ };
3822
+ }
3823
+
3824
+ declare interface SubscriptionEntitlement {
3825
+ readonly startsAt: number;
3826
+ readonly expiresAt: number;
3827
+ readonly graceUntil: number;
3828
+ readonly state: 'active' | 'grace' | 'expired';
3829
+ }
3830
+
3831
+ /** Actual pure defaults, not a billing, SQL-entitlement or idempotency fake. */
3832
+ export declare const subscriptionFixture: () => Readonly<{
3833
+ subscription: Subscription;
3834
+ currencies: readonly {
3835
+ id: string;
3836
+ kind: "soft";
3837
+ cap: string;
3838
+ }[];
3839
+ realMoney: false;
3840
+ originAt: number;
3841
+ index: 0;
3842
+ }>;
3843
+
3844
+ declare interface SubscriptionGift {
3845
+ readonly items: ShopOffer['items'];
3846
+ readonly currencies: ShopOffer['currencies'];
3847
+ }
3848
+
3849
+ declare interface SubscriptionGiftDefinition {
3850
+ readonly items?: ShopOfferDefinition['items'] | undefined;
3851
+ readonly currencies?: ShopOfferDefinition['currencies'] | undefined;
3852
+ }
3853
+
3854
+ declare type SubscriptionObservation = {
3855
+ readonly requestId: string;
3856
+ readonly state: 'not-observed';
3857
+ } | {
3858
+ readonly requestId: string;
3859
+ readonly state: 'fulfilled';
3860
+ readonly receipt: SubscriptionValueReceipt | SubscriptionCancellationReceipt;
3861
+ };
3862
+
3863
+ declare interface SubscriptionPeriodContext {
3864
+ readonly subscription: Subscription;
3865
+ readonly currencies: readonly CurrencyDefinition[];
3866
+ readonly realMoney?: boolean;
3867
+ readonly originAt: number;
3868
+ readonly index: number;
3869
+ readonly rulesVersion?: string;
3870
+ }
3871
+
3872
+ declare type SubscriptionPeriodResolver = (input: SubscriptionPeriodContext) => SubscriptionWindow;
3873
+
3874
+ export declare const subscriptionPeriodSystemFixture: Readonly<{
3875
+ kind: "slot";
3876
+ defaultImplementation: SubscriptionPeriodResolver;
3877
+ defaultProbe: {
3878
+ run: () => Effect.Effect< SubscriptionWindow, never, never>;
3879
+ expected: SubscriptionWindow;
3880
+ };
3881
+ overrideProbe: {
3882
+ run: () => Effect.Effect< SubscriptionWindow, never, never>;
3883
+ expected: {
3884
+ expiresAt: number;
3885
+ startsAt: number;
3886
+ };
3887
+ };
3888
+ }>;
3889
+
3890
+ declare type SubscriptionReceiptInput = {
3891
+ readonly kind: 'start';
3892
+ readonly intent: SubscriptionStartInput;
3893
+ } | {
3894
+ readonly kind: 'renew' | 'daily' | 'cancel';
3895
+ readonly intent: SubscriptionDailyInput;
3896
+ };
3897
+
3898
+ declare interface SubscriptionRenewInput {
3899
+ readonly tenantId: string;
3900
+ readonly ownerId: string;
3901
+ readonly entitlementId: string;
3902
+ readonly requestId: string;
3903
+ }
3904
+
3905
+ export declare const subscriptionsFakeLayer: Layer.Layer<EconomyPort | SubscriptionsPort, never, never>;
3906
+
3907
+ declare class SubscriptionsPort extends SubscriptionsPort_base {
3908
+ }
3909
+
3910
+ declare const SubscriptionsPort_base: Context.ServiceClass<SubscriptionsPort, "@voltro/SubscriptionsPort", SubscriptionsService>;
3911
+
3912
+ /** Actual installed memory-store plugin. Consent is an explicit fixture seam,
3913
+ * not a production default or verified consent acquisition/legal policy. */
3914
+ export declare const subscriptionsPortFixture: Effect.Effect<Readonly<{
3915
+ store: InMemoryDataStore;
3916
+ plugin: EconomyPlugin;
3917
+ base: {
3918
+ base: {
3919
+ economy: EconomySqlService;
3920
+ inventory: InventorySqlService;
3921
+ catalog: Readonly<{
3922
+ read: (scope: CatalogScope) => Effect.Effect< CatalogSnapshot, InventoryError>;
3923
+ importSnapshot: (subject: Subject, input: CatalogImport) => Effect.Effect< CatalogSnapshot, InventoryError>;
3924
+ }>;
3925
+ options: ShopStoreOptions;
3926
+ shop: ShopSqlService;
3927
+ user: {
3928
+ readonly type: "user";
3929
+ readonly id: string;
3930
+ readonly tenantId: string;
3931
+ readonly kind?: "guest" | undefined;
3932
+ readonly scopes?: readonly string[] | undefined;
3933
+ readonly metadata?: {
3934
+ readonly [x: string]: unknown;
3935
+ } | undefined;
3936
+ } | {
3937
+ readonly type: "apiKey";
3938
+ readonly id: string;
3939
+ readonly tenantId: string;
3940
+ readonly scopes?: readonly string[] | undefined;
3941
+ readonly metadata?: {
3942
+ readonly [x: string]: unknown;
3943
+ } | undefined;
3944
+ } | {
3945
+ readonly type: "serviceAccount";
3946
+ readonly id: string;
3947
+ readonly tenantId: string;
3948
+ readonly scopes?: readonly string[] | undefined;
3949
+ readonly metadata?: {
3950
+ readonly [x: string]: unknown;
3951
+ } | undefined;
3952
+ readonly auditActorId?: string | null;
3953
+ } | {
3954
+ readonly type: "anonymous";
3955
+ readonly id: null;
3956
+ readonly tenantId: string | null;
3957
+ readonly credentialRejected?: string | undefined;
3958
+ } | {
3959
+ readonly type: "system";
3960
+ readonly id: string;
3961
+ readonly tenantId: null;
3962
+ readonly scopes?: readonly string[] | undefined;
3963
+ readonly metadata?: {
3964
+ readonly [x: string]: unknown;
3965
+ } | undefined;
3966
+ } | {
3967
+ readonly type: "service";
3968
+ readonly id: string;
3969
+ readonly service: string;
3970
+ readonly tenantId: null;
3971
+ readonly scopes?: readonly string[] | undefined;
3972
+ readonly metadata?: {
3973
+ readonly [x: string]: unknown;
3974
+ } | undefined;
3975
+ };
3976
+ buy: ShopBuyInput;
3977
+ };
3978
+ options: SubscriptionsStoreOptions;
3979
+ service: SubscriptionsService;
3980
+ setAt: (value: number) => void;
3981
+ };
3982
+ close: Effect.Effect<void, never, never>;
3983
+ }>, never, never>;
3984
+
3985
+ export declare const subscriptionsPortSystemFixture: Readonly<{
3986
+ kind: "port";
3987
+ tag: typeof SubscriptionsPort;
3988
+ fakeLayer: Layer.Layer<EconomyPort | SubscriptionsPort, never, never>;
3989
+ probe: {
3990
+ run: () => Effect.Effect<{
3991
+ same: boolean;
3992
+ daily: "start" | "daily" | "renew";
3993
+ balance: string | undefined;
3994
+ }, EconomyError, never>;
3995
+ expected: {
3996
+ same: boolean;
3997
+ daily: string;
3998
+ balance: string;
3999
+ };
4000
+ };
4001
+ }>;
4002
+
4003
+ /** Renew is a trusted server intention. Implementations must deny players and
4004
+ * enforce one accepted renewal per entitlement/period independently of nonce.
4005
+ * Calendar/daily strategies alone cannot establish durable idempotency. */
4006
+ declare interface SubscriptionsService {
4007
+ readonly list: (subject: Subject) => Effect.Effect<readonly SubscriptionView[], EconomyError>;
4008
+ readonly active: (subject: Subject) => Effect.Effect<readonly SubscriptionActiveView[], EconomyError>;
4009
+ readonly start: (subject: Subject, input: SubscriptionStartInput) => Effect.Effect<SubscriptionValueReceipt, EconomyError>;
4010
+ readonly renew: (subject: Subject, input: SubscriptionRenewInput) => Effect.Effect<SubscriptionValueReceipt, EconomyError>;
4011
+ readonly claimDaily: (subject: Subject, input: SubscriptionDailyInput) => Effect.Effect<SubscriptionValueReceipt, EconomyError>;
4012
+ readonly cancel: (subject: Subject, input: SubscriptionCancelInput) => Effect.Effect<SubscriptionCancellationReceipt, EconomyError>;
4013
+ readonly receipt: (subject: Subject, input: SubscriptionReceiptInput) => Effect.Effect<SubscriptionObservation, EconomyError>;
4014
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
4015
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
4016
+ }
4017
+
4018
+ declare interface SubscriptionsStoreOptions {
4019
+ readonly subscriptions: readonly (SubscriptionDefinition | Subscription)[];
4020
+ readonly currencies: readonly CurrencyDefinition[];
4021
+ readonly realMoney?: boolean;
4022
+ readonly economy: Pick<EconomySqlService, 'prepareTransfer'>;
4023
+ readonly inventory?: Pick<InventorySqlService, 'prepareGrant'> | undefined;
4024
+ readonly accessSource: SubscriptionAccessSource;
4025
+ readonly version: string;
4026
+ readonly period?: SubscriptionPeriodResolver | undefined;
4027
+ readonly daily?: SubscriptionDailyGrantPolicy | undefined;
4028
+ readonly priceResolver?: PriceResolver | undefined;
4029
+ readonly systemAccount?: string | undefined;
4030
+ readonly maxHistoryRows?: number | undefined;
4031
+ readonly now?: (() => number) | undefined;
4032
+ }
4033
+
4034
+ /** Player intentions contain observed identities, never charge amounts, dates,
4035
+ * provider receipts, claimed days, entitlement state or renewal authority. */
4036
+ declare interface SubscriptionStartInput {
4037
+ readonly subscriptionId: string;
4038
+ readonly subscriptionVersion: string;
4039
+ readonly subscriptionDigest: string;
4040
+ readonly requestId: string;
4041
+ readonly currency?: string | undefined;
4042
+ }
4043
+
4044
+ declare interface SubscriptionValueReceipt {
4045
+ readonly operationId: string;
4046
+ readonly entitlementId: string;
4047
+ readonly kind: 'start' | 'renew' | 'daily';
4048
+ readonly at: number;
4049
+ readonly periodIndex: number;
4050
+ readonly day: number | null;
4051
+ readonly window: SubscriptionEntitlement;
4052
+ readonly value: ShopOrderReceipt;
4053
+ }
4054
+
4055
+ declare interface SubscriptionView {
4056
+ readonly subscription: Subscription;
4057
+ readonly subscriptionDigest: string;
4058
+ }
4059
+
4060
+ declare interface SubscriptionWindow {
4061
+ readonly startsAt: number;
4062
+ readonly expiresAt: number;
4063
+ }
4064
+
4065
+ declare interface TradeAcceptInput {
4066
+ readonly tradeId: string;
4067
+ readonly tradeDigest: string;
4068
+ readonly requestId: string;
4069
+ }
4070
+
4071
+ declare interface TradeAuthorityAdmission {
4072
+ readonly tenantId: string;
4073
+ readonly tradeId: string;
4074
+ readonly phase: 'propose' | 'accept';
4075
+ readonly at: number;
4076
+ readonly expiresAt: number;
4077
+ readonly participants: readonly TradeParticipantAuthority[];
4078
+ }
4079
+
4080
+ /** Required real trust/sanctions facts plus an exact-store SQL revalidation unit.
4081
+ * Read/prepare execute outside replayable SQL. The unit owns admission fences;
4082
+ * no default permission, trust score or revoked-consent behavior is fabricated. */
4083
+ declare interface TradeAuthoritySource {
4084
+ readonly read: (input: {
4085
+ readonly tenantId: string;
4086
+ readonly ownerId: string;
4087
+ readonly tradeId: string;
4088
+ readonly at: number;
4089
+ }) => Effect.Effect<TradeParticipantAuthority, EconomyError>;
4090
+ readonly prepare: (input: TradeAuthorityAdmission) => Effect.Effect<PreparedSqlMutation<void>, EconomyError>;
4091
+ }
4092
+
4093
+ declare interface TradeCurrencyIntention {
4094
+ readonly currency: string;
4095
+ readonly amount: EconomyAmountInput;
4096
+ }
4097
+
4098
+ declare interface TradeEligibilityContext {
4099
+ readonly tenantId: string;
4100
+ readonly proposerId: string;
4101
+ readonly accepterId: string;
4102
+ readonly tradeId: string;
4103
+ readonly proposedToId: string | null;
4104
+ readonly createdAt: number;
4105
+ readonly at: number;
4106
+ readonly expiresAt: number;
4107
+ readonly rules: TradeRules;
4108
+ readonly currencies: readonly CurrencyDefinition[];
4109
+ readonly offered: TradeLeg;
4110
+ readonly requested: TradeLeg;
4111
+ readonly proposer: TradeParticipantAuthority;
4112
+ readonly accepter: TradeParticipantAuthority;
4113
+ }
4114
+
4115
+ declare type TradeEligibilityPolicy = (input: TradeEligibilityContext) => boolean;
4116
+
4117
+ export declare const tradeEligibilitySystemFixture: Readonly<{
4118
+ kind: "slot";
4119
+ defaultImplementation: TradeEligibilityPolicy;
4120
+ defaultProbe: {
4121
+ run: () => Effect.Effect<boolean, never, never>;
4122
+ expected: boolean;
4123
+ };
4124
+ overrideProbe: {
4125
+ run: () => Effect.Effect<boolean, never, never>;
4126
+ expected: boolean;
4127
+ };
4128
+ }>;
4129
+
4130
+ declare interface TradeEscrowExchangeInput extends TradeEscrowTerms {
4131
+ readonly accepterId: string;
4132
+ }
4133
+
4134
+ declare interface TradeEscrowExchangeReceipt {
4135
+ readonly tradeId: string;
4136
+ readonly offered: TradeEscrowLegReceipt;
4137
+ readonly requested: TradeEscrowLegReceipt;
4138
+ }
4139
+
4140
+ declare interface TradeEscrowLegReceipt {
4141
+ readonly inventory: InventoryReservationTransferReceipt | null;
4142
+ readonly currencies: readonly {
4143
+ readonly reserve: EconomyReceipt;
4144
+ readonly settlement: WalletEscrowSettlementReceipt;
4145
+ }[];
4146
+ }
4147
+
4148
+ /** Server composition only. The mandatory exact-store admission belongs to the
4149
+ * durable Trade writer and actual eligibility authority. This does not install
4150
+ * player routes, infer consent/sanctions or replace durable Trade custody. */
4151
+ declare interface TradeEscrowParticipant {
4152
+ readonly prepareReserve: (subject: Subject, input: TradeEscrowTerms, admission: PreparedSqlMutation<void>) => Effect.Effect<PreparedSqlMutation<TradeEscrowReserveReceipt>, EconomyError>;
4153
+ readonly prepareRelease: (subject: Subject, input: TradeEscrowTerms, admission: PreparedSqlMutation<void>) => Effect.Effect<PreparedSqlMutation<TradeEscrowReserveReceipt>, EconomyError>;
4154
+ readonly prepareExchange: (subject: Subject, input: TradeEscrowExchangeInput, admission: PreparedSqlMutation<void>) => Effect.Effect<PreparedSqlMutation<TradeEscrowExchangeReceipt>, EconomyError>;
4155
+ }
4156
+
4157
+ declare interface TradeEscrowReserveReceipt {
4158
+ readonly tradeId: string;
4159
+ readonly inventory: InventoryReceipt | null;
4160
+ readonly currencies: readonly EconomyReceipt[];
4161
+ }
4162
+
4163
+ declare interface TradeEscrowTerms {
4164
+ readonly tenantId: string;
4165
+ readonly tradeId: string;
4166
+ readonly proposerId: string;
4167
+ readonly offered: TradeLegDefinition;
4168
+ readonly requested: TradeLegDefinition;
4169
+ }
4170
+
4171
+ declare interface TradeExpiryInput {
4172
+ readonly tenantId: string;
4173
+ readonly maxTrades?: number | undefined;
4174
+ }
4175
+
4176
+ declare interface TradeExpiryResult {
4177
+ readonly capturedAt: number;
4178
+ readonly scannedRows: number;
4179
+ readonly dueTrades: number;
4180
+ /** Matching original expiry receipts observed by this call, not newly committed closes. */
4181
+ readonly expiredTrades: number;
4182
+ readonly alreadyClosedTrades: number;
4183
+ }
4184
+
4185
+ export declare const tradeFixture: () => Readonly<{
4186
+ currencies: {
4187
+ id: string;
4188
+ kind: "soft";
4189
+ }[];
4190
+ rules: TradeRules;
4191
+ tenantId: "fixture";
4192
+ tradeId: "trade.a";
4193
+ proposerId: "a";
4194
+ accepterId: "b";
4195
+ proposedToId: "b";
4196
+ createdAt: 0;
4197
+ at: 100;
4198
+ expiresAt: 1000;
4199
+ offered: TradeLeg;
4200
+ requested: TradeLeg;
4201
+ proposer: {
4202
+ tenantId: string;
4203
+ ownerId: string;
4204
+ allowed: boolean;
4205
+ trustScore: number;
4206
+ items: {
4207
+ instanceId: string;
4208
+ itemId: string;
4209
+ qty: number;
4210
+ tradable: boolean;
4211
+ locked: boolean;
4212
+ reservationId: string;
4213
+ expiresAt: number;
4214
+ }[];
4215
+ };
4216
+ accepter: {
4217
+ tenantId: string;
4218
+ ownerId: string;
4219
+ allowed: boolean;
4220
+ trustScore: number;
4221
+ items: never[];
4222
+ };
4223
+ }>;
4224
+
4225
+ declare interface TradeItemAuthority {
4226
+ readonly instanceId: string;
4227
+ readonly itemId: string;
4228
+ readonly qty: number;
4229
+ readonly tradable: boolean;
4230
+ readonly locked: boolean;
4231
+ readonly reservationId: string | null;
4232
+ readonly expiresAt: number | null;
4233
+ }
4234
+
4235
+ declare interface TradeItemIntention {
4236
+ readonly instanceId: string;
4237
+ readonly qty: number;
4238
+ }
4239
+
4240
+ declare interface TradeLeg {
4241
+ readonly items: readonly TradeItemIntention[];
4242
+ readonly currencies: readonly {
4243
+ readonly currency: string;
4244
+ readonly amount: string;
4245
+ }[];
4246
+ }
4247
+
4248
+ declare interface TradeLegDefinition {
4249
+ readonly items: readonly TradeItemIntention[];
4250
+ readonly currencies: readonly TradeCurrencyIntention[];
4251
+ }
4252
+
4253
+ declare type TradeObservation = {
4254
+ readonly requestId: string;
4255
+ readonly state: 'not-observed';
4256
+ } | {
4257
+ readonly requestId: string;
4258
+ readonly state: 'fulfilled';
4259
+ readonly receipt: TradeReceipt;
4260
+ };
4261
+
4262
+ declare interface TradeParticipantAuthority {
4263
+ readonly tenantId: string;
4264
+ readonly ownerId: string;
4265
+ readonly allowed: boolean;
4266
+ readonly trustScore: number;
4267
+ readonly items: readonly TradeItemAuthority[];
4268
+ }
4269
+
4270
+ declare interface TradeProposeInput {
4271
+ readonly toId: string | null;
4272
+ readonly offered: TradeLegDefinition;
4273
+ readonly requested: TradeLegDefinition;
4274
+ readonly durationMs: number;
4275
+ readonly requestId: string;
4276
+ }
4277
+
4278
+ declare interface TradeReceipt {
4279
+ readonly operationId: string;
4280
+ readonly requestId: string;
4281
+ readonly trade: TradeView;
4282
+ }
4283
+
4284
+ declare interface TradeRules {
4285
+ readonly version: string;
4286
+ readonly taxBps: number;
4287
+ readonly maxDurationMs: number;
4288
+ readonly minTrustScore: number;
4289
+ }
4290
+
4291
+ export declare const tradesFakeLayer: Layer.Layer<EconomyPort | TradesPort, never, never>;
4292
+
4293
+ declare class TradesPort extends TradesPort_base {
4294
+ }
4295
+
4296
+ declare const TradesPort_base: Context.ServiceClass<TradesPort, "@voltro/TradesPort", TradesService>;
4297
+
4298
+ /** Actual installed SQL writers; allowed/trust remain explicit fixture seams.
4299
+ * This is no production sanctions/consent or scheduler proof. */
4300
+ export declare const tradesPortFixture: Effect.Effect<Readonly<{
4301
+ store: InMemoryDataStore;
4302
+ base: {
4303
+ options: {
4304
+ authority: TradeAuthoritySource;
4305
+ inventory: InventorySqlService;
4306
+ now: () => number;
4307
+ currencies: {
4308
+ id: string;
4309
+ kind: "soft";
4310
+ cap: string;
4311
+ }[];
4312
+ rules: {
4313
+ version: string;
4314
+ taxBps: number;
4315
+ };
4316
+ economy: EconomySqlService;
4317
+ };
4318
+ service: TradesSqlService;
4319
+ proposed: {
4320
+ toId: string;
4321
+ offered: {
4322
+ items: {
4323
+ instanceId: string;
4324
+ qty: number;
4325
+ }[];
4326
+ currencies: {
4327
+ currency: string;
4328
+ amount: string;
4329
+ }[];
4330
+ };
4331
+ requested: {
4332
+ items: {
4333
+ instanceId: string;
4334
+ qty: number;
4335
+ }[];
4336
+ currencies: {
4337
+ currency: string;
4338
+ amount: string;
4339
+ }[];
4340
+ };
4341
+ durationMs: number;
4342
+ requestId: string;
4343
+ };
4344
+ accept: (receipt: TradeReceipt, requestId?: string) => {
4345
+ tradeId: string;
4346
+ tradeDigest: string;
4347
+ requestId: string;
4348
+ };
4349
+ store: DataStore;
4350
+ item: ItemContract;
4351
+ economy: EconomySqlService;
4352
+ inventory: InventorySqlService;
4353
+ participant: TradeEscrowParticipant;
4354
+ terms: {
4355
+ tenantId: string;
4356
+ tradeId: string;
4357
+ proposerId: string;
4358
+ offered: {
4359
+ items: {
4360
+ instanceId: string;
4361
+ qty: number;
4362
+ }[];
4363
+ currencies: {
4364
+ currency: string;
4365
+ amount: string;
4366
+ }[];
4367
+ };
4368
+ requested: {
4369
+ items: {
4370
+ instanceId: string;
4371
+ qty: number;
4372
+ }[];
4373
+ currencies: {
4374
+ currency: string;
4375
+ amount: string;
4376
+ }[];
4377
+ };
4378
+ };
4379
+ user: (id: string) => Subject;
4380
+ a: InventoryInstance;
4381
+ b: InventoryInstance;
4382
+ admission: () => PreparedSqlMutation<void>;
4383
+ };
4384
+ plugin: EconomyPlugin;
4385
+ advance: (milliseconds: number) => void;
4386
+ close: Effect.Effect<void, never, never>;
4387
+ }>, never, never>;
4388
+
4389
+ export declare const tradesPortSystemFixture: Readonly<{
4390
+ kind: "port";
4391
+ tag: typeof TradesPort;
4392
+ fakeLayer: Layer.Layer<EconomyPort | TradesPort, never, never>;
4393
+ probe: {
4394
+ run: () => Effect.Effect<{
4395
+ same: boolean;
4396
+ observed: "fulfilled" | "not-observed";
4397
+ state: "expired" | "cancelled" | "accepted" | "proposed";
4398
+ balance: string;
4399
+ tax: string[];
4400
+ }, EconomyError, never>;
4401
+ expected: {
4402
+ same: boolean;
4403
+ observed: string;
4404
+ state: string;
4405
+ balance: string;
4406
+ tax: string[];
4407
+ };
4408
+ };
4409
+ }>;
4410
+
4411
+ /** Full owning service; no default implementation or escrow installation is
4412
+ * inferred from a Tag. SQL reservation/transfer/expiry/privacy own acceptance. */
4413
+ declare interface TradesService {
4414
+ readonly propose: (subject: Subject, input: TradeProposeInput) => Effect.Effect<TradeReceipt, EconomyError>;
4415
+ readonly accept: (subject: Subject, input: TradeAcceptInput) => Effect.Effect<TradeReceipt, EconomyError>;
4416
+ readonly cancel: (subject: Subject, input: {
4417
+ readonly tradeId: string;
4418
+ readonly requestId: string;
4419
+ }) => Effect.Effect<TradeReceipt, EconomyError>;
4420
+ readonly mine: (subject: Subject) => Effect.Effect<readonly TradeView[], EconomyError>;
4421
+ readonly receipt: (subject: Subject, input: {
4422
+ readonly requestId: string;
4423
+ }) => Effect.Effect<TradeObservation, EconomyError>;
4424
+ readonly rebuild: (subject: Subject, input: {
4425
+ readonly tenantId: string;
4426
+ readonly tradeId: string;
4427
+ }) => Effect.Effect<TradeView, EconomyError>;
4428
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
4429
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
4430
+ }
4431
+
4432
+ declare interface TradesSqlService extends TradesService {
4433
+ readonly expireDue: (subject: Subject, input: TradeExpiryInput) => Effect.Effect<TradeExpiryResult, EconomyError>;
4434
+ readonly expire: (subject: Subject, input: {
4435
+ readonly tenantId: string;
4436
+ readonly tradeId: string;
4437
+ }) => Effect.Effect<TradeReceipt, EconomyError>;
4438
+ }
4439
+
4440
+ declare interface TradeTaxContext {
4441
+ readonly currency: string;
4442
+ readonly amount: EconomyAmountInput;
4443
+ readonly rules: TradeRules;
4444
+ }
4445
+
4446
+ declare type TradeTaxPolicy = (input: TradeTaxContext) => EconomyAmountInput;
4447
+
4448
+ export declare const tradeTaxSystemFixture: Readonly<{
4449
+ kind: "slot";
4450
+ defaultImplementation: TradeTaxPolicy;
4451
+ defaultProbe: {
4452
+ run: () => Effect.Effect<Readonly<{
4453
+ currency: string;
4454
+ gross: string;
4455
+ net: string;
4456
+ tax: string;
4457
+ }>, never, never>;
4458
+ expected: {
4459
+ currency: string;
4460
+ gross: string;
4461
+ net: string;
4462
+ tax: string;
4463
+ };
4464
+ };
4465
+ overrideProbe: {
4466
+ run: () => Effect.Effect<Readonly<{
4467
+ currency: string;
4468
+ gross: string;
4469
+ net: string;
4470
+ tax: string;
4471
+ }>, never, never>;
4472
+ expected: {
4473
+ currency: string;
4474
+ gross: string;
4475
+ net: string;
4476
+ tax: string;
4477
+ };
4478
+ };
4479
+ }>;
4480
+
4481
+ declare interface TradeTaxView {
4482
+ readonly leg: 'offered' | 'requested';
4483
+ readonly currency: string;
4484
+ readonly gross: string;
4485
+ readonly net: string;
4486
+ readonly tax: string;
4487
+ }
4488
+
4489
+ declare interface TradeView {
4490
+ readonly tradeId: string;
4491
+ readonly tradeDigest: string;
4492
+ readonly proposerId: string;
4493
+ readonly toId: string | null;
4494
+ readonly offered: TradeLeg;
4495
+ readonly requested: TradeLeg;
4496
+ readonly createdAt: number;
4497
+ readonly expiresAt: number;
4498
+ readonly state: 'proposed' | 'accepted' | 'cancelled' | 'expired';
4499
+ readonly accepterId: string | null;
4500
+ readonly rulesVersion: string;
4501
+ readonly taxes: readonly TradeTaxView[];
4502
+ }
4503
+
4504
+ declare const verifiedCurrentState: unique symbol;
4505
+
4506
+ /** Fresh provider observation bound to revalidated original SQL. Never copied player refund authority. */
4507
+ declare interface VerifiedIapCurrentState {
4508
+ readonly [verifiedCurrentState]: true;
4509
+ readonly orderId: string;
4510
+ }
4511
+
4512
+ /** Authenticated notice, never current purchase state or grant/refund authority. */
4513
+ declare interface VerifiedIapNotification {
4514
+ readonly [verifiedNotification]: true;
4515
+ readonly noticeId: string;
4516
+ readonly channel: IapRefundChannel;
4517
+ }
4518
+
4519
+ /** Process-local opaque evidence. A serialized object is never a verification capability. */
4520
+ declare interface VerifiedIapPurchase {
4521
+ readonly [verifiedPurchase]: true;
4522
+ readonly scope: IapScope;
4523
+ readonly offerId: string;
4524
+ readonly offerVersion: string;
4525
+ readonly requestId: string;
4526
+ readonly verifiedAt: number;
4527
+ readonly identity: string;
4528
+ readonly facts: IapTransactionFacts;
4529
+ }
4530
+
4531
+ declare const verifiedNotification: unique symbol;
4532
+
4533
+ declare const verifiedPurchase: unique symbol;
4534
+
4535
+ declare interface VirtualPriceAdjustment {
4536
+ readonly currency: string;
4537
+ /** Inclusive integer 0..10000. */
4538
+ readonly discountBps: number;
4539
+ /** A trusted floor, enforced independently of a custom resolver. */
4540
+ readonly minimum: EconomyAmountInput;
4541
+ }
4542
+
4543
+ declare interface VirtualPriceOption {
4544
+ readonly currency: string;
4545
+ readonly amount: EconomyAmountInput;
4546
+ }
4547
+
4548
+ declare interface WalletEscrowFundedSettlementReceipt {
4549
+ readonly reserve: EconomyReceipt;
4550
+ readonly settlement: WalletEscrowSettlementReceipt;
4551
+ }
4552
+
4553
+ declare interface WalletEscrowInput {
4554
+ readonly tenantId: string;
4555
+ readonly owner: EscrowWallet;
4556
+ readonly reservationId: string;
4557
+ readonly currency: string;
4558
+ }
4559
+
4560
+ declare type WalletEscrowObservation = {
4561
+ readonly reservationId: string;
4562
+ readonly currency: string;
4563
+ readonly state: 'not-observed';
4564
+ } | {
4565
+ readonly reservationId: string;
4566
+ readonly currency: string;
4567
+ readonly amount: string;
4568
+ readonly state: 'held' | 'released' | 'settled';
4569
+ readonly receipt: EconomyReceipt;
4570
+ readonly closingReceipt: EconomyReceipt | null;
4571
+ readonly taxReceipt: EconomyReceipt | null;
4572
+ };
4573
+
4574
+ declare interface WalletEscrowParticipant {
4575
+ readonly prepareEscrowReserve: (subject: Subject, input: WalletEscrowReserveInput) => Effect.Effect<PreparedSqlMutation<EconomyReceipt>, EconomyError>;
4576
+ readonly prepareEscrowRelease: (subject: Subject, input: WalletEscrowInput) => Effect.Effect<PreparedSqlMutation<EconomyReceipt>, EconomyError>;
4577
+ /** Whole gross payout; use prepareEscrowTaxSettle for an exact net/sink split. */
4578
+ readonly prepareEscrowSettle: (subject: Subject, input: WalletEscrowSettleInput) => Effect.Effect<PreparedSqlMutation<EconomyReceipt>, EconomyError>;
4579
+ readonly prepareEscrowTaxSettle: (subject: Subject, input: WalletEscrowTaxSettleInput) => Effect.Effect<PreparedSqlMutation<WalletEscrowSettlementReceipt>, EconomyError>;
4580
+ readonly prepareEscrowReserveTaxSettle: (subject: Subject, input: WalletEscrowReserveTaxSettleInput) => Effect.Effect<PreparedSqlMutation<WalletEscrowFundedSettlementReceipt>, EconomyError>;
4581
+ readonly escrow: (subject: Subject, input: WalletEscrowInput) => Effect.Effect<WalletEscrowObservation, EconomyError>;
4582
+ }
4583
+
4584
+ declare interface WalletEscrowReserveInput extends WalletEscrowInput {
4585
+ readonly amount: EconomyAmountInput;
4586
+ }
4587
+
4588
+ declare interface WalletEscrowReserveTaxSettleInput extends WalletEscrowReserveInput {
4589
+ readonly to: EscrowWallet;
4590
+ readonly taxAmount: EconomyAmountInput;
4591
+ readonly taxAccountId?: string | undefined;
4592
+ }
4593
+
4594
+ declare interface WalletEscrowSettleInput extends WalletEscrowInput {
4595
+ readonly to: EscrowWallet;
4596
+ }
4597
+
4598
+ declare interface WalletEscrowSettlementReceipt {
4599
+ readonly reservationId: string;
4600
+ readonly currency: string;
4601
+ readonly gross: string;
4602
+ readonly net: string;
4603
+ readonly tax: string;
4604
+ readonly payout: EconomyReceipt | null;
4605
+ readonly taxReceipt: EconomyReceipt | null;
4606
+ }
4607
+
4608
+ declare interface WalletEscrowTaxSettleInput extends WalletEscrowSettleInput {
4609
+ readonly taxAmount: EconomyAmountInput;
4610
+ readonly taxAccountId?: string | undefined;
4611
+ }
4612
+
4613
+ declare type WalletOwnerKind = 'player' | 'guild';
4614
+
4615
+ export { }