@voltro/plugin-economy 0.0.0-stage → 0.88.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 +24492 -0
  2. package/LICENSE +61 -0
  3. package/README.md +2719 -2
  4. package/SECURITY.md +56 -0
  5. package/THIRD-PARTY-NOTICES.md +9841 -0
  6. package/dist/amount.js +20 -0
  7. package/dist/appleIap.server.js +262 -0
  8. package/dist/appleIapNotifications.server.js +162 -0
  9. package/dist/appleIapTrust.server.js +30 -0
  10. package/dist/bundleValue.server.js +112 -0
  11. package/dist/canonicalDataJson.js +10 -0
  12. package/dist/capture.js +53 -0
  13. package/dist/codePort.fixture.js +123 -0
  14. package/dist/codeStore.server.js +439 -0
  15. package/dist/codeTables.entity.js +43 -0
  16. package/dist/codes.d.ts +150 -0
  17. package/dist/codes.js +82 -0
  18. package/dist/components.d.ts +397 -0
  19. package/dist/components.js +283 -0
  20. package/dist/consistency.server.js +115 -0
  21. package/dist/containerStore.server.js +687 -0
  22. package/dist/containerStore.testFixtures.js +75 -0
  23. package/dist/containerTables.entity.js +45 -0
  24. package/dist/containers.d.ts +311 -0
  25. package/dist/containers.fixture.js +128 -0
  26. package/dist/containers.js +214 -0
  27. package/dist/containers.server.js +39 -0
  28. package/dist/containersPort.fixture.js +74 -0
  29. package/dist/currency.js +32 -0
  30. package/dist/declarationFixtures.js +61 -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 +124 -0
  34. package/dist/errors.d.ts +14 -0
  35. package/dist/errors.js +21 -0
  36. package/dist/events.d.ts +46 -0
  37. package/dist/events.js +1 -0
  38. package/dist/googleIap.server.js +391 -0
  39. package/dist/googleIapNotifications.server.js +130 -0
  40. package/dist/googleIapSubscription.server.js +183 -0
  41. package/dist/iap.d.ts +309 -0
  42. package/dist/iap.js +303 -0
  43. package/dist/iap.testFixtures.js +144 -0
  44. package/dist/iapAccountTables.entity.js +39 -0
  45. package/dist/iapAccounts.server.js +200 -0
  46. package/dist/iapBinding.server.js +523 -0
  47. package/dist/iapCheckout.server.js +97 -0
  48. package/dist/iapCompletion.server.js +55 -0
  49. package/dist/iapCurrentState.server.js +107 -0
  50. package/dist/iapCurrentState.testFixtures.js +22 -0
  51. package/dist/iapCurrentStatePort.fixture.js +63 -0
  52. package/dist/iapEntitlements.d.ts +79 -0
  53. package/dist/iapEntitlements.js +57 -0
  54. package/dist/iapEntitlements.server.js +48 -0
  55. package/dist/iapEntitlementsBinding.server.js +105 -0
  56. package/dist/iapEntitlementsPort.fixture.js +80 -0
  57. package/dist/iapInstalledEntitlements.testFixtures.js +75 -0
  58. package/dist/iapInstalledNotifications.testFixtures.js +93 -0
  59. package/dist/iapInventoryRefunds.server.js +261 -0
  60. package/dist/iapNotificationCustodyPort.fixture.js +67 -0
  61. package/dist/iapNotificationPort.fixture.js +73 -0
  62. package/dist/iapNotificationProcessor.server.js +134 -0
  63. package/dist/iapNotificationProcessor.testFixtures.js +63 -0
  64. package/dist/iapNotificationProcessorPort.fixture.js +55 -0
  65. package/dist/iapNotificationSchedule.server.js +69 -0
  66. package/dist/iapNotificationStore.server.js +352 -0
  67. package/dist/iapNotificationStore.testFixtures.js +31 -0
  68. package/dist/iapNotificationTables.entity.js +48 -0
  69. package/dist/iapNotificationTarget.server.js +36 -0
  70. package/dist/iapNotificationTarget.testFixtures.js +29 -0
  71. package/dist/iapNotifications.d.ts +644 -0
  72. package/dist/iapNotifications.js +6 -0
  73. package/dist/iapNotifications.server.js +95 -0
  74. package/dist/iapNotificationsBinding.server.js +232 -0
  75. package/dist/iapNotificationsSystemPort.fixture.js +50 -0
  76. package/dist/iapPlugin.testFixtures.js +59 -0
  77. package/dist/iapPort.fixture.js +74 -0
  78. package/dist/iapProviderCapture.server.js +17 -0
  79. package/dist/iapRecovery.d.ts +146 -0
  80. package/dist/iapRecovery.js +89 -0
  81. package/dist/iapRefundEventDelivery.server.js +82 -0
  82. package/dist/iapRefundEvents.js +44 -0
  83. package/dist/iapRefundEventsPort.fixture.js +59 -0
  84. package/dist/iapRefundPort.fixture.js +54 -0
  85. package/dist/iapRefundTables.entity.js +55 -0
  86. package/dist/iapRefunds.d.ts +755 -0
  87. package/dist/iapRefunds.js +6 -0
  88. package/dist/iapRefunds.server.js +553 -0
  89. package/dist/iapRefunds.testFixtures.js +64 -0
  90. package/dist/iapSpendAdmission.server.js +98 -0
  91. package/dist/iapStore.server.js +740 -0
  92. package/dist/iapStore.testFixtures.js +127 -0
  93. package/dist/iapTables.entity.js +80 -0
  94. package/dist/iapVerification.server.js +120 -0
  95. package/dist/index.d.ts +2984 -0
  96. package/dist/index.js +15 -0
  97. package/dist/intentionRecovery.js +133 -0
  98. package/dist/ledger.js +77 -0
  99. package/dist/loyalty.d.ts +274 -0
  100. package/dist/loyalty.fixture.js +92 -0
  101. package/dist/loyalty.js +311 -0
  102. package/dist/loyaltyBinding.server.js +203 -0
  103. package/dist/loyaltyPort.fixture.js +86 -0
  104. package/dist/loyaltyShopSource.server.js +90 -0
  105. package/dist/loyaltyStore.server.js +552 -0
  106. package/dist/loyaltyStore.testFixtures.js +82 -0
  107. package/dist/loyaltyTables.entity.js +49 -0
  108. package/dist/offerMerchandising.fixture.js +114 -0
  109. package/dist/offerMerchandising.js +282 -0
  110. package/dist/offerPolicies.d.ts +441 -0
  111. package/dist/offerPolicies.fixture.js +53 -0
  112. package/dist/offerPolicies.js +267 -0
  113. package/dist/piggyBanks.d.ts +272 -0
  114. package/dist/piggyBanks.fixture.js +70 -0
  115. package/dist/piggyBanks.js +200 -0
  116. package/dist/piggyBinding.server.js +204 -0
  117. package/dist/piggyPort.fixture.js +72 -0
  118. package/dist/piggyStore.server.js +610 -0
  119. package/dist/piggyStore.testFixtures.js +97 -0
  120. package/dist/piggyTables.entity.js +40 -0
  121. package/dist/plugin.js +1047 -0
  122. package/dist/port.js +12 -0
  123. package/dist/price.d.ts +99 -0
  124. package/dist/price.js +143 -0
  125. package/dist/purchaseRecovery.js +55 -0
  126. package/dist/reports.server.js +114 -0
  127. package/dist/rpc.d.ts +3201 -0
  128. package/dist/rpc.js +1200 -0
  129. package/dist/sanctionsTradeAuthority.server.js +79 -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 +195 -0
  134. package/dist/shop.testFixtures.js +113 -0
  135. package/dist/shopPort.fixture.js +71 -0
  136. package/dist/shopPresentation.js +67 -0
  137. package/dist/shopStore.server.js +623 -0
  138. package/dist/shopTables.entity.js +62 -0
  139. package/dist/shopUiCopy.js +56 -0
  140. package/dist/spendLimits.d.ts +140 -0
  141. package/dist/spendLimits.fixture.js +52 -0
  142. package/dist/spendLimits.js +154 -0
  143. package/dist/spendLimitsPort.fixture.js +119 -0
  144. package/dist/subscriptionBinding.server.js +210 -0
  145. package/dist/subscriptionStore.server.js +642 -0
  146. package/dist/subscriptionStore.testFixtures.js +71 -0
  147. package/dist/subscriptionTables.entity.js +36 -0
  148. package/dist/subscriptions.d.ts +394 -0
  149. package/dist/subscriptions.fixture.js +96 -0
  150. package/dist/subscriptions.js +212 -0
  151. package/dist/subscriptionsPort.fixture.js +75 -0
  152. package/dist/systemFixtures.js +88 -0
  153. package/dist/tables.entity.js +82 -0
  154. package/dist/testing.d.ts +4615 -0
  155. package/dist/testing.js +29 -0
  156. package/dist/tradeAuthority.server.js +171 -0
  157. package/dist/tradeBinding.server.js +200 -0
  158. package/dist/tradeComponents.js +341 -0
  159. package/dist/tradeEscrow.server.js +231 -0
  160. package/dist/tradeEscrow.testFixtures.js +132 -0
  161. package/dist/tradeExpiry.server.js +42 -0
  162. package/dist/tradeRecovery.js +47 -0
  163. package/dist/tradeStore.server.js +749 -0
  164. package/dist/tradeStore.testFixtures.js +72 -0
  165. package/dist/tradeTables.entity.js +65 -0
  166. package/dist/tradeUiCopy.js +78 -0
  167. package/dist/trades.d.ts +246 -0
  168. package/dist/trades.fixture.js +102 -0
  169. package/dist/trades.js +288 -0
  170. package/dist/tradesPort.fixture.js +93 -0
  171. package/dist/walletEscrow.js +105 -0
  172. package/dist/walletEscrow.server.js +375 -0
  173. package/dist/walletKeys.server.js +4 -0
  174. package/dist/walletStore.js +570 -0
  175. package/dist/web.d.ts +2257 -0
  176. package/dist/web.js +8 -0
  177. package/package.json +161 -4
@@ -0,0 +1,29 @@
1
+ import { issueVerifiedIapNotification as e } from "./iapNotifications.server.js";
2
+ import { randomUUID as t } from "node:crypto";
3
+ // src/iapNotificationTarget.testFixtures.ts
4
+ var n = (n, r = {}) => {
5
+ let i = {
6
+ channel: "ios",
7
+ applicationId: n.verificationOptions.applicationId,
8
+ environment: "sandbox",
9
+ noticeId: t(),
10
+ sourceType: "REFUND",
11
+ kind: "voided",
12
+ occurredAt: n.at,
13
+ transactionId: n.transactionId,
14
+ originalTransactionId: n.facts.originalTransactionId,
15
+ storeProductId: n.facts.storeProductId,
16
+ ...r
17
+ };
18
+ return {
19
+ notice: i,
20
+ options: {
21
+ realMoney: !0,
22
+ channel: "ios",
23
+ applicationId: n.verificationOptions.applicationId,
24
+ environment: "sandbox"
25
+ },
26
+ evidence: e(i, { privateReceipt: n.receipt })
27
+ };
28
+ };
29
+ export { n as iapSqlNoticeFixture };
@@ -0,0 +1,644 @@
1
+ import { BrandedScheduleDefinition } from '@voltro/runtime';
2
+ import { ColumnBuilder } from '@voltro/database';
3
+ import { Context } from 'effect';
4
+ import { DataStore } from '@voltro/database';
5
+ import { Effect } from 'effect';
6
+ import { FieldCipher } from '@voltro/database';
7
+ import { FieldDefinitions } from '@voltro/database';
8
+ import { PluginHttpRoute } from '@voltro/protocol';
9
+ import { PluginPrivacyTarget } from '@voltro/protocol';
10
+ import { PreparedSqlMutation } from '@voltro/database/sql';
11
+ import { Schema } from 'effect';
12
+ import { Subject } from '@voltro/protocol';
13
+ import { Table } from '@voltro/database';
14
+ import { YieldableError } from 'effect/Cause';
15
+
16
+ declare const acceptedCompletion: unique symbol;
17
+
18
+ /** SQL-issued capability. A player reference or serialized object cannot authorize provider completion. */
19
+ declare interface AcceptedIapCompletion {
20
+ readonly [acceptedCompletion]: true;
21
+ readonly orderId: string;
22
+ }
23
+
24
+ /** Read-only SQL custody capability, never current provider state or refund authority. */
25
+ declare interface AcceptedIapNotificationTarget {
26
+ readonly [acceptedNotificationTarget]: true;
27
+ readonly orderId: string;
28
+ readonly noticeId: string;
29
+ }
30
+
31
+ declare const acceptedNotificationTarget: unique symbol;
32
+
33
+ export declare const captureIapNotificationCustodyService: (input: IapNotificationCustodyService) => IapNotificationCustodyService;
34
+
35
+ export declare const captureIapNotificationProcessorService: (input: IapNotificationProcessorService) => IapNotificationProcessorService;
36
+
37
+ export declare const captureIapNotificationsSystemService: (input: IapNotificationsSystemService) => IapNotificationsSystemService;
38
+
39
+ declare interface CurrencyDefinition {
40
+ readonly id: string;
41
+ readonly kind: 'soft' | 'hard';
42
+ readonly cap?: EconomyAmountInput;
43
+ readonly premium?: boolean;
44
+ readonly display?: {
45
+ readonly nameKey: string;
46
+ } | null;
47
+ }
48
+
49
+ /** Borrowed SQL with explicit framework cipher, full bounded scans and durable privacy/identity fences. */
50
+ export declare const dataStoreIapNotificationCustody: (store: DataStore, input: IapNotificationCustodyOptions) => IapNotificationCustodyService;
51
+
52
+ declare type EconomyAmountInput = string | number | bigint;
53
+
54
+ declare type EconomyChannel = 'web' | 'ios' | 'android' | 'steam';
55
+
56
+ /** Redacted codes; never carries owners, amounts or provider payloads. */
57
+ declare class EconomyError extends EconomyError_base {
58
+ }
59
+
60
+ declare const EconomyError_base: Schema.Class<EconomyError, Schema.TaggedStruct<"EconomyError", {
61
+ 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"]>;
62
+ }>, YieldableError>;
63
+
64
+ declare interface EconomyReceipt {
65
+ readonly txId: string;
66
+ readonly currency: string;
67
+ readonly amount: string;
68
+ readonly at: number;
69
+ }
70
+
71
+ declare interface IapCurrentPurchaseState {
72
+ readonly orderId: string;
73
+ readonly channel: IapRefundChannel;
74
+ readonly state: 'active' | 'partially-refunded' | 'revoked';
75
+ /** Exact cumulative provider refund share; never floating point or money sent by a player. */
76
+ readonly refundNumerator: number;
77
+ readonly refundDenominator: number;
78
+ readonly observedAt: number;
79
+ readonly revokedAt: number | null;
80
+ }
81
+
82
+ declare interface IapCurrentStateService {
83
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
84
+ readonly read: (target: AcceptedIapNotificationTarget) => Effect.Effect<VerifiedIapCurrentState, EconomyError>;
85
+ }
86
+
87
+ declare type IapDebtPolicy = 'allowNegative' | 'blockSpend';
88
+
89
+ declare type IapEnvironment = 'sandbox' | 'production';
90
+
91
+ export declare interface IapInstalledNotificationScheduleOptions {
92
+ readonly system: IapNotificationsSystemService;
93
+ readonly channel: EconomyChannel;
94
+ readonly name: string;
95
+ readonly cron: string;
96
+ readonly timezone?: string;
97
+ readonly maxNotices?: number;
98
+ }
99
+
100
+ export declare interface IapNotificationCipherSource {
101
+ /** Default is the registered framework FieldCipher. Explicit sources use that same codec/row binding contract. */
102
+ readonly read: () => FieldCipher | undefined;
103
+ }
104
+
105
+ export declare interface IapNotificationCustodyOptions {
106
+ readonly cipher?: IapNotificationCipherSource | undefined;
107
+ readonly namespace: string;
108
+ readonly deployment: IapNotificationTargetOptions;
109
+ readonly orders: Pick<IapSqlService, 'prepareNotification'>;
110
+ /** Explicit operator retention, never a guessed region/privacy policy. Expired identities cannot recreate payloads. */
111
+ readonly bodyRetentionMs: number;
112
+ readonly maxRows?: number;
113
+ readonly maxBodyBytes?: number;
114
+ readonly now?: (() => number) | undefined;
115
+ }
116
+
117
+ export declare class IapNotificationCustodyPort extends IapNotificationCustodyPort_base {
118
+ }
119
+
120
+ declare const IapNotificationCustodyPort_base: Context.ServiceClass<IapNotificationCustodyPort, "@voltro/IapNotificationCustodyPort", IapNotificationCustodyService>;
121
+
122
+ export declare interface IapNotificationCustodyReceipt {
123
+ readonly id: string;
124
+ readonly orderId: string;
125
+ readonly state: 'pending' | 'processed' | 'expired' | 'erased';
126
+ }
127
+
128
+ export declare interface IapNotificationCustodyService {
129
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
130
+ readonly retain: (evidence: VerifiedIapNotification) => Effect.Effect<IapNotificationCustodyReceipt, EconomyError>;
131
+ /** Trusted server worker, not a player RPC. Restores only row-bound authenticated SQL custody. */
132
+ readonly restore: (id: string) => Effect.Effect<VerifiedIapNotification, EconomyError>;
133
+ /** Actual accepted refund capability and same-store journal fence required. */
134
+ readonly markProcessed: (id: string, receipt: IapRefundReceipt) => Effect.Effect<IapNotificationCustodyReceipt, EconomyError>;
135
+ readonly pending: () => Effect.Effect<readonly IapNotificationCustodyReceipt[], EconomyError>;
136
+ readonly prune: () => Effect.Effect<number, EconomyError>;
137
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
138
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
139
+ }
140
+
141
+ export declare interface IapNotificationHttpOptions {
142
+ readonly path: string;
143
+ readonly maxBodyBytes: number;
144
+ readonly processor: IapNotificationProcessorService;
145
+ /** Provider-delivered purchases first; `true` answers the request without the notification pipeline. */
146
+ readonly deliver?: (input: IapNotificationInput) => Effect.Effect<boolean, EconomyError>;
147
+ }
148
+
149
+ /** A real plugin HTTP declaration, mounted by the normal API gateway; cookie/query/body status conveys no authority. */
150
+ export declare const iapNotificationHttpRoute: (input: IapNotificationHttpOptions) => PluginHttpRoute;
151
+
152
+ declare interface IapNotificationInput {
153
+ readonly body: string;
154
+ /** Actual HTTP Authorization header for authenticated Pub/Sub push; never query/body authority. */
155
+ readonly authorization?: string;
156
+ /** The request's other headers, lowercased and bounded, for providers that sign in a header
157
+ * (Stripe: `stripe-signature`). Never authority by themselves. */
158
+ readonly headers?: Readonly<Record<string, string>>;
159
+ }
160
+
161
+ export declare const iapNotificationNamespacesTable: Table<"_voltro_eco_iap_notice_namespaces", FieldDefinitions<{
162
+ readonly id: ColumnBuilder<string, "id", boolean>;
163
+ readonly namespace: ColumnBuilder<string, "text", boolean>;
164
+ readonly configurationDigest: ColumnBuilder<string, "text", boolean>;
165
+ readonly admission: ColumnBuilder<number, "integer", boolean>;
166
+ readonly rowsCount: ColumnBuilder<number, "integer", boolean>;
167
+ }>, true, never>;
168
+
169
+ /** Authenticated delivery -> encrypted SQL custody -> fresh lookup -> actual financial group -> SQL completion fence. */
170
+ export declare const iapNotificationProcessor: (input: IapNotificationProcessorOptions) => IapNotificationProcessorService;
171
+
172
+ export declare interface IapNotificationProcessorOptions {
173
+ readonly deployment: IapNotificationTargetOptions;
174
+ readonly notifications: IapNotificationService;
175
+ readonly current: IapCurrentStateService;
176
+ readonly custody: IapNotificationCustodyService;
177
+ readonly refunds: IapRefundService;
178
+ readonly orders: Pick<IapSqlService, 'prepareNotification'>;
179
+ /** After a notice changed a purchase (see `IapNotificationsPluginOptions.onChange`). */
180
+ readonly changed?: ((change: IapPurchaseChange) => Effect.Effect<void, unknown>) | undefined;
181
+ }
182
+
183
+ export declare class IapNotificationProcessorPort extends IapNotificationProcessorPort_base {
184
+ }
185
+
186
+ declare const IapNotificationProcessorPort_base: Context.ServiceClass<IapNotificationProcessorPort, "@voltro/IapNotificationProcessorPort", IapNotificationProcessorService>;
187
+
188
+ export declare interface IapNotificationProcessorService {
189
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
190
+ /** `null`: an authenticated test notice (a store's TEST message, a provider event with no
191
+ * purchase in it) is acknowledged and not retained; no retry could ever process it. */
192
+ readonly ingest: (input: IapNotificationInput) => Effect.Effect<IapNotificationCustodyReceipt | null, EconomyError>;
193
+ readonly process: (id: string) => Effect.Effect<IapNotificationCustodyReceipt, EconomyError>;
194
+ readonly pending: () => Effect.Effect<readonly IapNotificationCustodyReceipt[], EconomyError>;
195
+ readonly prune: () => Effect.Effect<number, EconomyError>;
196
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
197
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
198
+ }
199
+
200
+ /** Normal discovered schedule descriptor; registration/cadence belong to the app. No timer or background runtime here. */
201
+ export declare const iapNotificationSchedule: (input: IapNotificationScheduleOptions) => BrandedScheduleDefinition;
202
+
203
+ export declare interface IapNotificationScheduleOptions extends IapNotificationWorkOptions {
204
+ readonly name: string;
205
+ readonly cron: string;
206
+ readonly timezone?: string;
207
+ }
208
+
209
+ declare interface IapNotificationService {
210
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
211
+ readonly verify: (input: IapNotificationInput) => Effect.Effect<VerifiedIapNotification, EconomyError>;
212
+ }
213
+
214
+ export declare interface IapNotificationsPluginOptions {
215
+ readonly version: string;
216
+ readonly pathPrefix?: string;
217
+ readonly maxBodyBytes?: number;
218
+ readonly bodyRetentionMs?: number | undefined;
219
+ readonly debtPolicy?: IapDebtPolicy | undefined;
220
+ readonly assets?: IapRefundAssetParticipant | undefined;
221
+ readonly assetsVersion?: string | undefined;
222
+ readonly maxNoticeRows?: number;
223
+ readonly maxRefundRows?: number;
224
+ readonly cipher?: IapNotificationCipherSource | undefined;
225
+ /** Complete replacement owns authentication/current state/financial authority, queue and privacy. */
226
+ readonly service?: IapNotificationsSystemService;
227
+ /** Complete notification replacements may expose their own three-method event queue. */
228
+ readonly refundEvents?: IapRefundEventsService;
229
+ /** Custom borrowed-transaction SQL read. Omit when the custom system exposes no own order disposition. */
230
+ readonly refundDisposition?: IapRefundDispositionSource | undefined;
231
+ /** Runs after a notice changed a purchase (a refund, a revocation), e.g. to end game time at once:
232
+ * `rooms.moderation.recheckAccess({ tenantId, subjectId: ownerId })`. Best effort: a failure is
233
+ * ignored and the notice stays processed. Not with a complete replacement `service`. */
234
+ readonly onChange?: ((change: IapPurchaseChange) => Effect.Effect<void, unknown>) | undefined;
235
+ }
236
+
237
+ export declare class IapNotificationsSystemPort extends IapNotificationsSystemPort_base {
238
+ }
239
+
240
+ declare const IapNotificationsSystemPort_base: Context.ServiceClass<IapNotificationsSystemPort, "@voltro/IapNotificationsSystemPort", IapNotificationsSystemService>;
241
+
242
+ export declare interface IapNotificationsSystemService {
243
+ readonly initialize: () => Effect.Effect<void, EconomyError>;
244
+ readonly processor: (channel: EconomyChannel) => Effect.Effect<IapNotificationProcessorService, EconomyError>;
245
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
246
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
247
+ }
248
+
249
+ export declare const iapNotificationsTable: Table<"_voltro_eco_iap_notices", FieldDefinitions<{
250
+ readonly id: ColumnBuilder<string, "id", boolean>;
251
+ readonly namespace: ColumnBuilder<string, "text", boolean>;
252
+ readonly tenantId: ColumnBuilder<string, "text", boolean>;
253
+ readonly ownerId: ColumnBuilder<string | null, "text", boolean>;
254
+ readonly ownerKey: ColumnBuilder<string, "text", boolean>;
255
+ readonly orderId: ColumnBuilder<string, "text", boolean>;
256
+ readonly identity: ColumnBuilder<string, "text", boolean>;
257
+ readonly fingerprint: ColumnBuilder<string, "text", boolean>;
258
+ readonly state: ColumnBuilder<"erased" | "expired" | "pending" | "processed", "text", boolean>;
259
+ /** Already sealed by the SAME registered row-bound framework FieldCipher, like the connection vault. Never automatic double encryption. */
260
+ readonly sealedBody: ColumnBuilder<string | null, "text", boolean>;
261
+ readonly createdAt: ColumnBuilder<Date, "timestamp", boolean>;
262
+ readonly expiresAt: ColumnBuilder<Date, "timestamp", boolean>;
263
+ }>, true, never>;
264
+
265
+ export declare const iapNotificationSubjectsTable: Table<"_voltro_eco_iap_notice_subjects", FieldDefinitions<{
266
+ readonly id: ColumnBuilder<string, "id", boolean>;
267
+ readonly namespace: ColumnBuilder<string, "text", boolean>;
268
+ readonly tenantId: ColumnBuilder<string, "text", boolean>;
269
+ readonly ownerId: ColumnBuilder<string | null, "text", boolean>;
270
+ readonly erased: ColumnBuilder<boolean, "boolean", boolean>;
271
+ readonly admission: ColumnBuilder<number, "integer", boolean>;
272
+ readonly rowsCount: ColumnBuilder<number, "integer", boolean>;
273
+ }>, true, never>;
274
+
275
+ export declare const iapNotificationTables: readonly [ Table<"_voltro_eco_iap_notice_namespaces", FieldDefinitions<{
276
+ readonly id: ColumnBuilder<string, "id", boolean>;
277
+ readonly namespace: ColumnBuilder<string, "text", boolean>;
278
+ readonly configurationDigest: ColumnBuilder<string, "text", boolean>;
279
+ readonly admission: ColumnBuilder<number, "integer", boolean>;
280
+ readonly rowsCount: ColumnBuilder<number, "integer", boolean>;
281
+ }>, true, never>, Table<"_voltro_eco_iap_notice_subjects", FieldDefinitions<{
282
+ readonly id: ColumnBuilder<string, "id", boolean>;
283
+ readonly namespace: ColumnBuilder<string, "text", boolean>;
284
+ readonly tenantId: ColumnBuilder<string, "text", boolean>;
285
+ readonly ownerId: ColumnBuilder<string | null, "text", boolean>;
286
+ readonly erased: ColumnBuilder<boolean, "boolean", boolean>;
287
+ readonly admission: ColumnBuilder<number, "integer", boolean>;
288
+ readonly rowsCount: ColumnBuilder<number, "integer", boolean>;
289
+ }>, true, never>, Table<"_voltro_eco_iap_notices", FieldDefinitions<{
290
+ readonly id: ColumnBuilder<string, "id", boolean>;
291
+ readonly namespace: ColumnBuilder<string, "text", boolean>;
292
+ readonly tenantId: ColumnBuilder<string, "text", boolean>;
293
+ readonly ownerId: ColumnBuilder<string | null, "text", boolean>;
294
+ readonly ownerKey: ColumnBuilder<string, "text", boolean>;
295
+ readonly orderId: ColumnBuilder<string, "text", boolean>;
296
+ readonly identity: ColumnBuilder<string, "text", boolean>;
297
+ readonly fingerprint: ColumnBuilder<string, "text", boolean>;
298
+ readonly state: ColumnBuilder<"erased" | "expired" | "pending" | "processed", "text", boolean>;
299
+ /** Already sealed by the SAME registered row-bound framework FieldCipher, like the connection vault. Never automatic double encryption. */
300
+ readonly sealedBody: ColumnBuilder<string | null, "text", boolean>;
301
+ readonly createdAt: ColumnBuilder<Date, "timestamp", boolean>;
302
+ readonly expiresAt: ColumnBuilder<Date, "timestamp", boolean>;
303
+ }>, true, never>];
304
+
305
+ declare interface IapNotificationTargetOptions {
306
+ readonly realMoney: boolean;
307
+ readonly channel: IapRefundChannel;
308
+ readonly applicationId: string;
309
+ readonly environment: IapEnvironment;
310
+ }
311
+
312
+ export declare interface IapNotificationWorkOptions {
313
+ readonly processor: IapNotificationProcessorService;
314
+ readonly maxNotices?: number;
315
+ }
316
+
317
+ export declare interface IapNotificationWorkResult {
318
+ readonly prunedBodies: number;
319
+ readonly scannedRows: number;
320
+ readonly processedNotices: number;
321
+ }
322
+
323
+ declare type IapOrderObservation = {
324
+ readonly requestId: string;
325
+ readonly state: 'not-observed' | 'pending';
326
+ } | {
327
+ readonly requestId: string;
328
+ readonly state: 'fulfilled';
329
+ readonly receipt: IapOrderReceipt;
330
+ readonly refund?: IapRefundDisposition;
331
+ } | {
332
+ readonly requestId: string;
333
+ readonly state: 'partially-refunded' | 'refunded';
334
+ readonly receipt: IapOrderReceipt;
335
+ readonly refund: IapRefundDisposition;
336
+ };
337
+
338
+ declare interface IapOrderReceipt {
339
+ readonly orderId: string;
340
+ readonly offerId: string;
341
+ readonly offerVersion: string;
342
+ readonly quantity: number;
343
+ readonly at: number;
344
+ readonly pricePaid: IapPaidAmount | null;
345
+ readonly transfers: readonly {
346
+ readonly txId: string;
347
+ readonly currency: string;
348
+ readonly amount: string;
349
+ readonly at: number;
350
+ }[];
351
+ readonly grants: readonly {
352
+ readonly operationId: string;
353
+ readonly kind: 'grant';
354
+ readonly changes: readonly {
355
+ readonly instanceId: string;
356
+ readonly delta: number;
357
+ }[];
358
+ readonly at: number;
359
+ }[];
360
+ }
361
+
362
+ /** Provider units retain their own decimal scale. Reference prices are never substituted. */
363
+ declare interface IapPaidAmount {
364
+ readonly currency: string;
365
+ readonly units: string;
366
+ readonly scale: number;
367
+ }
368
+
369
+ declare interface IapProduct {
370
+ readonly offer: ShopOffer;
371
+ readonly channel: EconomyChannel;
372
+ readonly kind: IapProductKind;
373
+ readonly storeProductId: string;
374
+ }
375
+
376
+ declare type IapProductKind = 'consumable' | 'non-consumable' | 'subscription';
377
+
378
+ /** A store or provider notice changed a purchase: a refund, a revocation. `ownerId` is the player. */
379
+ export declare interface IapPurchaseChange {
380
+ readonly tenantId: string;
381
+ readonly ownerId: string;
382
+ readonly orderId: string;
383
+ readonly offerId: string;
384
+ readonly offerVersion: string;
385
+ readonly state: IapRefundReceipt['state'];
386
+ }
387
+
388
+ declare interface IapReceiptIntent {
389
+ readonly channel: EconomyChannel;
390
+ readonly offerId: string;
391
+ readonly offerVersion: string;
392
+ readonly requestId: string;
393
+ /** Opaque store evidence. Never log or persist this in player receipts. */
394
+ readonly receipt: string;
395
+ }
396
+
397
+ /** Read-only original-intention reference. Never send raw receipts in query parameters. */
398
+ declare interface IapReceiptReference {
399
+ readonly channel: EconomyChannel;
400
+ readonly offerId: string;
401
+ readonly offerVersion: string;
402
+ readonly requestId: string;
403
+ readonly receiptDigest: string;
404
+ }
405
+
406
+ declare interface IapRefundAssetContext {
407
+ readonly original: IapSqlOriginalPurchase;
408
+ readonly state: IapCurrentPurchaseState;
409
+ readonly previous: IapRefundReceipt | null;
410
+ readonly sequence: number;
411
+ }
412
+
413
+ /** Mandatory for orders with item grants. It must use the same real store and original grant custody. */
414
+ declare interface IapRefundAssetParticipant {
415
+ readonly prepare: (input: IapRefundAssetContext) => Effect.Effect<PreparedSqlMutation<unknown>, EconomyError>;
416
+ readonly validate: (input: IapRefundAssetContext, receipt: unknown) => unknown;
417
+ /** SQL-only audit of accepted movement receipts; no provider/application callbacks. */
418
+ readonly audit: (tx: DataStore, input: IapRefundAssetContext, receipt: unknown) => Promise<void>;
419
+ }
420
+
421
+ /** Channels whose providers report refunds and revocations to the economy. */
422
+ declare type IapRefundChannel = 'ios' | 'android' | 'web';
423
+
424
+ declare interface IapRefundCurrency {
425
+ readonly currency: string;
426
+ readonly original: string;
427
+ readonly refunded: string;
428
+ }
429
+
430
+ /** Accepted SQL refund history only. This projection never authorizes a grant or completion. */
431
+ declare interface IapRefundDisposition {
432
+ readonly sequence: number;
433
+ readonly state: 'active' | 'partially-refunded' | 'revoked';
434
+ readonly refundNumerator: number;
435
+ readonly refundDenominator: number;
436
+ readonly observedAt: number;
437
+ }
438
+
439
+ /** Borrowed transaction read only: no provider, nested transaction, seed, clock or mutation. */
440
+ declare interface IapRefundDispositionSource {
441
+ readonly read: (tx: DataStore, original: IapSqlOriginalPurchase) => Promise<IapRefundDisposition | null>;
442
+ }
443
+
444
+ declare interface IapRefundEventContext {
445
+ readonly namespace: string;
446
+ readonly tenantId: string;
447
+ readonly subjectId: string;
448
+ readonly payload: IapRefundEventPayload;
449
+ /** Complete bounded accepted history; these are observations, never a fraud verdict. */
450
+ readonly refundedOrders: number;
451
+ readonly observedOrders: number;
452
+ readonly refundAdjustments: number;
453
+ }
454
+
455
+ declare type IapRefundEventPayload = typeof IapRefundEventPayloadSchema.Type;
456
+
457
+ declare const IapRefundEventPayloadSchema: Schema.Struct<{
458
+ readonly eventId: Schema.String;
459
+ readonly orderId: Schema.String;
460
+ readonly sequence: Schema.Number;
461
+ readonly state: Schema.Literals<readonly ["active", "partially-refunded", "revoked"]>;
462
+ readonly refundNumerator: Schema.Number;
463
+ readonly refundDenominator: Schema.Number;
464
+ readonly observedAt: Schema.Number;
465
+ }>;
466
+
467
+ declare interface IapRefundEventsService {
468
+ readonly pending: (subject: Subject) => Effect.Effect<readonly PendingIapRefundEvent[], EconomyError>;
469
+ readonly read: (subject: Subject, event: PendingIapRefundEvent) => Effect.Effect<IapRefundEventContext, EconomyError>;
470
+ readonly acknowledge: (subject: Subject, event: PendingIapRefundEvent, deliveryVersion: string) => Effect.Effect<void, EconomyError>;
471
+ }
472
+
473
+ declare interface IapRefundReceipt {
474
+ readonly orderId: string;
475
+ readonly sequence: number;
476
+ readonly state: IapCurrentPurchaseState['state'];
477
+ readonly refundNumerator: number;
478
+ readonly refundDenominator: number;
479
+ readonly observedAt: number;
480
+ readonly noticeId: string;
481
+ readonly fingerprint: string;
482
+ readonly debtPolicy: IapDebtPolicy;
483
+ readonly currencies: readonly IapRefundCurrency[];
484
+ readonly transfers: readonly EconomyReceipt[];
485
+ /** Opaque actual asset participant receipt, validated by its captured participant. */
486
+ readonly assets: unknown;
487
+ }
488
+
489
+ declare interface IapRefundService {
490
+ readonly reconcile: (proof: VerifiedIapCurrentState) => Effect.Effect<IapRefundReceipt, EconomyError>;
491
+ readonly rebuild: (proof: VerifiedIapCurrentState) => Effect.Effect<IapRefundReceipt | null, EconomyError>;
492
+ readonly observe: (scope: IapScope, orderId: string) => Effect.Effect<IapRefundReceipt | null, EconomyError>;
493
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
494
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
495
+ }
496
+
497
+ declare interface IapScope {
498
+ readonly tenantId: string;
499
+ readonly ownerId: string;
500
+ }
501
+
502
+ declare interface IapService {
503
+ readonly redeem: (subject: Subject, input: IapReceiptIntent) => Effect.Effect<IapOrderReceipt, EconomyError>;
504
+ readonly receipt: (subject: Subject, input: IapReceiptReference) => Effect.Effect<IapOrderObservation, EconomyError>;
505
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
506
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
507
+ }
508
+
509
+ /** SQL original after the owning adapter has validated its immutable custody and receipt. */
510
+ declare interface IapSqlOriginalPurchase {
511
+ readonly orderId: string;
512
+ readonly scope: IapScope;
513
+ readonly receipt: IapOrderReceipt;
514
+ readonly product: IapProduct;
515
+ readonly facts: IapTransactionFacts;
516
+ readonly currencies: readonly CurrencyDefinition[];
517
+ }
518
+
519
+ declare interface IapSqlService extends IapService {
520
+ /** Complete owned accepted subscription periods. SQL reads only; never current provider status. */
521
+ readonly subscriptionPeriods: (subject: Subject) => Effect.Effect<readonly IapSubscriptionPeriod[], EconomyError>;
522
+ /** Server only: the provider's original transaction IDs (a Stripe subscription) of the owner's
523
+ * accepted subscription orders on `channel`, newest period first. Never a player projection. */
524
+ readonly subscriptionOriginals: (subject: Subject, channel: EconomyChannel) => Effect.Effect<readonly string[], EconomyError>;
525
+ /** Actual original IAP/account/retained-notice SQL fences; joins the same-store financial mutation group. */
526
+ readonly prepareCurrentState: (evidence: VerifiedIapCurrentState) => Effect.Effect<PreparedSqlMutation<IapCurrentPurchaseState>, EconomyError>;
527
+ /** Trusted server declaration + authenticated notice; read accepted SQL, never seed or fulfill. */
528
+ readonly prepareNotification: (options: IapNotificationTargetOptions, notice: VerifiedIapNotification) => Effect.Effect<AcceptedIapNotificationTarget, EconomyError>;
529
+ /** Read accepted SQL original and issue a revalidated capability; no grant, clock or provider call. */
530
+ readonly prepareCompletion: (subject: Subject, intent: Parameters<IapService['redeem']>[1]) => Effect.Effect<AcceptedIapCompletion, EconomyError>;
531
+ }
532
+
533
+ /** An accepted store period, not a fresh provider observation or financial capability. */
534
+ declare interface IapSubscriptionPeriod {
535
+ readonly orderId: string;
536
+ readonly offerId: string;
537
+ readonly offerVersion: string;
538
+ readonly channel: EconomyChannel;
539
+ readonly startsAt: number;
540
+ readonly expiresAt: number;
541
+ readonly refund: IapRefundDisposition | null;
542
+ }
543
+
544
+ declare interface IapTransactionFacts {
545
+ readonly channel: EconomyChannel;
546
+ readonly environment: IapEnvironment;
547
+ readonly applicationId: string;
548
+ readonly transactionId: string;
549
+ readonly originalTransactionId: string | null;
550
+ readonly storeProductId: string;
551
+ readonly kind: IapProductKind;
552
+ readonly quantity: number;
553
+ readonly purchasedAt: number;
554
+ readonly expiresAt: number | null;
555
+ readonly paid: IapPaidAmount | null;
556
+ readonly acknowledgement: 'none' | 'pending' | 'acknowledged';
557
+ readonly consumption: 'none' | 'available' | 'consumed';
558
+ }
559
+
560
+ /** Discovery may capture the installed facade before activation; resolution occurs only on a firing. */
561
+ export declare const installedIapNotificationSchedule: (input: IapInstalledNotificationScheduleOptions) => BrandedScheduleDefinition;
562
+
563
+ /** Actual pending SQL event authority. Copies and serialized payloads cannot authorize delivery acknowledgement. */
564
+ declare interface PendingIapRefundEvent {
565
+ readonly [refundEvent]: true;
566
+ readonly eventId: string;
567
+ }
568
+
569
+ declare type Price = {
570
+ readonly kind: 'virtual';
571
+ readonly options: readonly ResolvedVirtualPriceOption[];
572
+ } | ({
573
+ readonly kind: 'real';
574
+ } & RealPriceDefinition) | {
575
+ readonly kind: 'mixed';
576
+ readonly real: RealPriceDefinition;
577
+ readonly virtual: readonly ResolvedVirtualPriceOption[];
578
+ } | {
579
+ readonly kind: 'free';
580
+ };
581
+
582
+ declare interface RealPriceDefinition {
583
+ /** Integer minor-unit references; verified store receipts remain authoritative. */
584
+ readonly prices: Readonly<Record<string, number>>;
585
+ readonly storeProductIds: Partial<Readonly<Record<EconomyChannel, string>>>;
586
+ }
587
+
588
+ declare const refundEvent: unique symbol;
589
+
590
+ declare interface ResolvedVirtualPriceOption {
591
+ readonly currency: string;
592
+ readonly amount: string;
593
+ }
594
+
595
+ /** Explicit bounded SQL queue work. Earlier successful notices survive a later firing failure; retry reads SQL again. */
596
+ export declare const runIapNotificationWork: (input: IapNotificationWorkOptions) => Effect.Effect<IapNotificationWorkResult, EconomyError>;
597
+
598
+ declare interface ShopItemGrant {
599
+ readonly itemId: string;
600
+ readonly qty: number;
601
+ }
602
+
603
+ declare interface ShopOffer {
604
+ readonly id: string;
605
+ readonly version: string;
606
+ readonly titleKey: string;
607
+ readonly price: Price;
608
+ readonly items: readonly ShopItemGrant[];
609
+ readonly currencies: readonly {
610
+ readonly currency: string;
611
+ readonly amount: string;
612
+ }[];
613
+ readonly from: number | null;
614
+ readonly until: number | null;
615
+ readonly segment: string | null;
616
+ readonly flag: string | null;
617
+ readonly countries: readonly string[];
618
+ readonly limits: {
619
+ readonly perPlayer: number | null;
620
+ readonly perDay: number | null;
621
+ readonly global: number | null;
622
+ };
623
+ readonly oneTime: boolean;
624
+ readonly maxQuantity: number;
625
+ }
626
+
627
+ declare const verifiedCurrentState: unique symbol;
628
+
629
+ /** Fresh provider observation bound to revalidated original SQL. Never copied player refund authority. */
630
+ declare interface VerifiedIapCurrentState {
631
+ readonly [verifiedCurrentState]: true;
632
+ readonly orderId: string;
633
+ }
634
+
635
+ /** Authenticated notice, never current purchase state or grant/refund authority. */
636
+ declare interface VerifiedIapNotification {
637
+ readonly [verifiedNotification]: true;
638
+ readonly noticeId: string;
639
+ readonly channel: IapRefundChannel;
640
+ }
641
+
642
+ declare const verifiedNotification: unique symbol;
643
+
644
+ export { }
@@ -0,0 +1,6 @@
1
+ import { iapNotificationNamespacesTable as e, iapNotificationSubjectsTable as t, iapNotificationTables as n, iapNotificationsTable as r } from "./iapNotificationTables.entity.js";
2
+ import { IapNotificationCustodyPort as i, captureIapNotificationCustodyService as a, dataStoreIapNotificationCustody as o } from "./iapNotificationStore.server.js";
3
+ import { IapNotificationProcessorPort as s, captureIapNotificationProcessorService as c, iapNotificationHttpRoute as l, iapNotificationProcessor as u } from "./iapNotificationProcessor.server.js";
4
+ import { IapNotificationsSystemPort as d, captureIapNotificationsSystemService as f } from "./iapNotificationsBinding.server.js";
5
+ import { iapNotificationSchedule as p, installedIapNotificationSchedule as m, runIapNotificationWork as h } from "./iapNotificationSchedule.server.js";
6
+ export { i as IapNotificationCustodyPort, s as IapNotificationProcessorPort, d as IapNotificationsSystemPort, a as captureIapNotificationCustodyService, c as captureIapNotificationProcessorService, f as captureIapNotificationsSystemService, o as dataStoreIapNotificationCustody, l as iapNotificationHttpRoute, e as iapNotificationNamespacesTable, u as iapNotificationProcessor, p as iapNotificationSchedule, t as iapNotificationSubjectsTable, n as iapNotificationTables, r as iapNotificationsTable, m as installedIapNotificationSchedule, h as runIapNotificationWork };