@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,72 @@
1
+ import { subscriptionTables as e } from "./subscriptionTables.entity.js";
2
+ import { dataStoreSubscriptions as t } from "./subscriptionStore.server.js";
3
+ import { shopAt as n, shopFixture as r } from "./shop.testFixtures.js";
4
+ import { Effect as i } from "effect";
5
+ import { ensureTableRegistered as a } from "@voltro/database";
6
+ //#region src/subscriptionStore.testFixtures.ts
7
+ var o = async (o, s = "tenant", c = {}, l = 4096) => {
8
+ e.forEach(a);
9
+ let u = await r(o, s, {}, l), d = n, f = {
10
+ currencies: [{
11
+ id: "test.gold",
12
+ kind: "soft"
13
+ }],
14
+ economy: u.economy,
15
+ inventory: u.inventory,
16
+ subscriptions: [{
17
+ id: "plus",
18
+ titleKey: "test.plus",
19
+ price: {
20
+ kind: "virtual",
21
+ options: [{
22
+ currency: "test.gold",
23
+ amount: "10"
24
+ }]
25
+ },
26
+ period: {
27
+ unit: "month",
28
+ count: 1
29
+ },
30
+ grants: {
31
+ onPurchase: { currencies: [{
32
+ currency: "test.gold",
33
+ amount: "2"
34
+ }] },
35
+ daily: { currencies: [{
36
+ currency: "test.gold",
37
+ amount: "1"
38
+ }] }
39
+ },
40
+ graceDays: 2,
41
+ disclosure: {
42
+ durationKey: "test.plus.duration",
43
+ renewalKey: "test.plus.renewal",
44
+ cancellationPath: "/account/subscriptions"
45
+ }
46
+ }],
47
+ version: "fixture.subscriptions.v1",
48
+ accessSource: { read: () => i.succeed({
49
+ eligible: !0,
50
+ renewalConsent: !0,
51
+ adjustments: []
52
+ }) },
53
+ now: () => d,
54
+ ...c
55
+ };
56
+ return {
57
+ base: u,
58
+ options: f,
59
+ service: t(o, f),
60
+ setAt: (e) => {
61
+ d = e;
62
+ }
63
+ };
64
+ }, s = (e, t = "start") => ({
65
+ subscriptionId: e.subscription.id,
66
+ subscriptionVersion: e.subscription.version,
67
+ subscriptionDigest: e.subscriptionDigest,
68
+ requestId: t,
69
+ ...e.subscription.price.kind === "virtual" ? { currency: e.subscription.price.options[0].currency } : {}
70
+ });
71
+ //#endregion
72
+ export { s as subscriptionFixtureStart, o as subscriptionStoreFixture };
@@ -0,0 +1,37 @@
1
+ import { boolean as e, id as t, integer as n, json as r, table as i, text as a, timestamp as o } from "@voltro/database";
2
+ //#region src/subscriptionTables.entity.ts
3
+ var s = () => a().maxLength(120).externalId().sensitive("pii"), c = i("_voltro_eco_subscription_tenants", {
4
+ id: t({ prefix: "evt" }),
5
+ tenantId: s(),
6
+ admission: n().serverOnly()
7
+ }).index(["tenantId"]), l = i("_voltro_eco_subscriptions", {
8
+ id: t({ prefix: "evd" }),
9
+ tenantId: s(),
10
+ definition: r().serverOnly(),
11
+ currencies: r().serverOnly(),
12
+ realMoney: e().serverOnly(),
13
+ rulesVersion: a().maxLength(120).serverOnly()
14
+ }).index(["tenantId"]), u = i("_voltro_eco_subscription_subjects", {
15
+ id: t({ prefix: "evs" }),
16
+ tenantId: s(),
17
+ ownerId: a().maxLength(120).nullable().externalId().sensitive("pii"),
18
+ erased: e().serverOnly(),
19
+ admission: n().serverOnly()
20
+ }).index(["tenantId", "ownerId"]), d = i("_voltro_eco_subscription_events", {
21
+ id: t({ prefix: "eve" }),
22
+ tenantId: s(),
23
+ ownerId: a().maxLength(120).nullable().externalId().sensitive("pii"),
24
+ ownerKey: a().maxLength(64).serverOnly(),
25
+ sequence: n().serverOnly(),
26
+ state: a().maxLength(16).oneOf(["fulfilled", "erased"]).serverOnly(),
27
+ digest: a().maxLength(64).nullable().serverOnly(),
28
+ body: r().nullable().serverOnly(),
29
+ createdAt: o().serverOnly()
30
+ }).index(["tenantId", "ownerKey"]), f = [
31
+ c,
32
+ l,
33
+ u,
34
+ d
35
+ ];
36
+ //#endregion
37
+ export { l as subscriptionDefinitionsTable, d as subscriptionEventsTable, u as subscriptionSubjectsTable, f as subscriptionTables, c as subscriptionTenantsTable };
@@ -0,0 +1,394 @@
1
+ import { Context } from 'effect';
2
+ import { Effect } from 'effect';
3
+ import { PluginPrivacyTarget } from '@voltro/protocol';
4
+ import { Schema } from 'effect';
5
+ import { Subject } from '@voltro/protocol';
6
+ import { YieldableError } from 'effect/Cause';
7
+
8
+ export declare const captureSubscriptionReceiptIntent: (input: SubscriptionReceiptInput) => SubscriptionReceiptInput;
9
+
10
+ export declare const captureSubscriptionsService: (input: SubscriptionsService) => SubscriptionsService;
11
+
12
+ declare interface CurrencyDefinition {
13
+ readonly id: string;
14
+ readonly kind: 'soft' | 'hard';
15
+ readonly cap?: EconomyAmountInput;
16
+ readonly premium?: boolean;
17
+ readonly display?: {
18
+ readonly nameKey: string;
19
+ } | null;
20
+ }
21
+
22
+ /** Pure decision only. claimedDays must come from complete authoritative SQL
23
+ * grant receipts; a player cannot submit this context to a value-write route. */
24
+ export declare const defaultSubscriptionDailyGrant: SubscriptionDailyGrantPolicy;
25
+
26
+ /** UTC calendar windows anchored to original purchase, never the previously
27
+ * clamped renewal date. Thus Jan31 -> Feb28/29 -> Mar31 without monthly drift. */
28
+ export declare const defaultSubscriptionPeriod: SubscriptionPeriodResolver;
29
+
30
+ /** Content only. No entitlement, charge, automatic renewal or consent follows
31
+ * from declaring a price. Required UI copy/path must be rendered by a consumer. */
32
+ export declare const defineSubscription: (input: SubscriptionDefinition | Subscription, declarations: readonly CurrencyDefinition[], realMoney?: boolean) => Subscription;
33
+
34
+ declare type EconomyAmountInput = string | number | bigint;
35
+
36
+ declare type EconomyChannel = 'web' | 'ios' | 'android' | 'steam';
37
+
38
+ /** Redacted codes; never carries owners, amounts or provider payloads. */
39
+ declare class EconomyError extends EconomyError_base {
40
+ }
41
+
42
+ declare const EconomyError_base: Schema.Class<EconomyError, Schema.TaggedStruct<"EconomyError", {
43
+ 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"]>;
44
+ }>, YieldableError>;
45
+
46
+ declare type Price = {
47
+ readonly kind: 'virtual';
48
+ readonly options: readonly ResolvedVirtualPriceOption[];
49
+ } | ({
50
+ readonly kind: 'real';
51
+ } & RealPriceDefinition) | {
52
+ readonly kind: 'mixed';
53
+ readonly real: RealPriceDefinition;
54
+ readonly virtual: readonly ResolvedVirtualPriceOption[];
55
+ } | {
56
+ readonly kind: 'free';
57
+ };
58
+
59
+ declare type PriceDefinition = {
60
+ readonly kind: 'virtual';
61
+ readonly options: readonly VirtualPriceOption[];
62
+ } | ({
63
+ readonly kind: 'real';
64
+ } & RealPriceDefinition) | {
65
+ readonly kind: 'mixed';
66
+ readonly real: RealPriceDefinition;
67
+ readonly virtual: readonly VirtualPriceOption[];
68
+ } | {
69
+ readonly kind: 'free';
70
+ };
71
+
72
+ declare interface RealPriceDefinition {
73
+ /** Integer minor-unit references; verified store receipts remain authoritative. */
74
+ readonly prices: Readonly<Record<string, number>>;
75
+ readonly storeProductIds: Partial<Readonly<Record<EconomyChannel, string>>>;
76
+ }
77
+
78
+ declare interface ResolvedVirtualPriceOption {
79
+ readonly currency: string;
80
+ readonly amount: string;
81
+ }
82
+
83
+ export declare const resolveSubscriptionDailyGrant: (input: SubscriptionDailyContext, slot?: SubscriptionDailyGrantPolicy) => Readonly<{
84
+ eligible: boolean;
85
+ day: number;
86
+ }>;
87
+
88
+ /** A custom window may narrow the canonical period, never overlap/extend it.
89
+ * Actual accepted entitlement/renewal identity remains the SQL owner's duty. */
90
+ export declare const resolveSubscriptionPeriod: (input: SubscriptionPeriodContext, slot?: SubscriptionPeriodResolver) => SubscriptionWindow;
91
+
92
+ declare interface ShopCurrencyGrant {
93
+ readonly currency: string;
94
+ readonly amount: EconomyAmountInput;
95
+ }
96
+
97
+ declare interface ShopItemGrant {
98
+ readonly itemId: string;
99
+ readonly qty: number;
100
+ }
101
+
102
+ declare interface ShopOffer {
103
+ readonly id: string;
104
+ readonly version: string;
105
+ readonly titleKey: string;
106
+ readonly price: Price;
107
+ readonly items: readonly ShopItemGrant[];
108
+ readonly currencies: readonly {
109
+ readonly currency: string;
110
+ readonly amount: string;
111
+ }[];
112
+ readonly from: number | null;
113
+ readonly until: number | null;
114
+ readonly segment: string | null;
115
+ readonly flag: string | null;
116
+ readonly countries: readonly string[];
117
+ readonly limits: {
118
+ readonly perPlayer: number | null;
119
+ readonly perDay: number | null;
120
+ readonly global: number | null;
121
+ };
122
+ readonly oneTime: boolean;
123
+ readonly maxQuantity: number;
124
+ }
125
+
126
+ declare interface ShopOfferDefinition {
127
+ readonly id: string;
128
+ readonly version?: string | undefined;
129
+ readonly titleKey: string;
130
+ readonly price: PriceDefinition;
131
+ readonly items?: readonly ShopItemGrant[] | undefined;
132
+ readonly currencies?: readonly ShopCurrencyGrant[] | undefined;
133
+ readonly from?: number | null | undefined;
134
+ readonly until?: number | null | undefined;
135
+ readonly segment?: string | null | undefined;
136
+ readonly flag?: string | null | undefined;
137
+ readonly countries?: readonly string[] | undefined;
138
+ readonly limits?: {
139
+ readonly perPlayer?: number | null | undefined;
140
+ readonly perDay?: number | null | undefined;
141
+ readonly global?: number | null | undefined;
142
+ } | undefined;
143
+ readonly oneTime?: boolean | undefined;
144
+ readonly maxQuantity?: number | undefined;
145
+ }
146
+
147
+ declare interface ShopOrderReceipt {
148
+ readonly orderId: string;
149
+ readonly offerId: string;
150
+ readonly offerVersion: string;
151
+ readonly offerDigest: string;
152
+ readonly quantity: number;
153
+ readonly at: number;
154
+ readonly pricePaid: {
155
+ readonly kind: 'free';
156
+ } | {
157
+ readonly kind: 'virtual';
158
+ readonly currency: string;
159
+ readonly amount: string;
160
+ };
161
+ readonly transfers: readonly {
162
+ readonly txId: string;
163
+ readonly currency: string;
164
+ readonly amount: string;
165
+ readonly at: number;
166
+ }[];
167
+ readonly grants: readonly {
168
+ readonly operationId: string;
169
+ readonly kind: 'grant';
170
+ readonly changes: readonly {
171
+ readonly instanceId: string;
172
+ readonly delta: number;
173
+ }[];
174
+ readonly at: number;
175
+ }[];
176
+ }
177
+
178
+ export declare interface Subscription {
179
+ readonly id: string;
180
+ readonly version: string;
181
+ readonly titleKey: string;
182
+ readonly price: Price;
183
+ readonly period: Readonly<{
184
+ unit: 'day' | 'week' | 'month';
185
+ count: number;
186
+ }>;
187
+ readonly grants: Readonly<{
188
+ onPurchase: SubscriptionGift;
189
+ daily: SubscriptionGift;
190
+ }>;
191
+ readonly graceDays: number;
192
+ readonly coolingOff: Readonly<{
193
+ days: number;
194
+ overrideReason: string | null;
195
+ }>;
196
+ readonly disclosure: Readonly<{
197
+ durationKey: string;
198
+ renewalKey: string;
199
+ cancellationPath: string;
200
+ }>;
201
+ }
202
+
203
+ export declare interface SubscriptionActiveView {
204
+ readonly entitlementId: string;
205
+ readonly subscription: Subscription;
206
+ readonly subscriptionDigest: string;
207
+ readonly originAt: number;
208
+ readonly periodIndex: number;
209
+ readonly window: SubscriptionEntitlement;
210
+ readonly currency: string | null;
211
+ readonly renews: boolean;
212
+ readonly cancelledAt: number | null;
213
+ }
214
+
215
+ export declare interface SubscriptionCancelInput {
216
+ readonly entitlementId: string;
217
+ readonly requestId: string;
218
+ }
219
+
220
+ export declare interface SubscriptionCancellationReceipt {
221
+ readonly operationId: string;
222
+ readonly entitlementId: string;
223
+ readonly at: number;
224
+ readonly renews: false;
225
+ }
226
+
227
+ export declare interface SubscriptionDailyContext {
228
+ readonly entitlement: SubscriptionEntitlement;
229
+ readonly at: number;
230
+ readonly claimedDays: readonly number[];
231
+ readonly rulesVersion?: string;
232
+ }
233
+
234
+ export declare type SubscriptionDailyGrantPolicy = (input: SubscriptionDailyContext) => boolean;
235
+
236
+ export declare interface SubscriptionDailyInput {
237
+ readonly entitlementId: string;
238
+ readonly requestId: string;
239
+ }
240
+
241
+ export declare interface SubscriptionDefinition {
242
+ readonly id: string;
243
+ readonly version?: string | undefined;
244
+ readonly titleKey: string;
245
+ readonly price: PriceDefinition;
246
+ readonly period: {
247
+ readonly unit: 'day' | 'week' | 'month';
248
+ readonly count: number;
249
+ };
250
+ readonly grants: {
251
+ readonly onPurchase?: SubscriptionGiftDefinition;
252
+ readonly daily?: SubscriptionGiftDefinition | undefined;
253
+ };
254
+ readonly graceDays?: number;
255
+ readonly coolingOff?: {
256
+ readonly days: number;
257
+ readonly overrideReason?: string | null;
258
+ };
259
+ readonly disclosure: {
260
+ readonly durationKey: string;
261
+ readonly renewalKey: string;
262
+ readonly cancellationPath: string;
263
+ };
264
+ }
265
+
266
+ export declare interface SubscriptionEntitlement {
267
+ readonly startsAt: number;
268
+ readonly expiresAt: number;
269
+ readonly graceUntil: number;
270
+ readonly state: 'active' | 'grace' | 'expired';
271
+ }
272
+
273
+ export declare const subscriptionEntitlementIntent: (input: SubscriptionDailyInput | SubscriptionCancelInput) => Readonly<{
274
+ entitlementId: string;
275
+ requestId: string;
276
+ }>;
277
+
278
+ export declare interface SubscriptionGift {
279
+ readonly items: ShopOffer['items'];
280
+ readonly currencies: ShopOffer['currencies'];
281
+ }
282
+
283
+ export declare interface SubscriptionGiftDefinition {
284
+ readonly items?: ShopOfferDefinition['items'] | undefined;
285
+ readonly currencies?: ShopOfferDefinition['currencies'] | undefined;
286
+ }
287
+
288
+ export declare type SubscriptionObservation = {
289
+ readonly requestId: string;
290
+ readonly state: 'not-observed';
291
+ } | {
292
+ readonly requestId: string;
293
+ readonly state: 'fulfilled';
294
+ readonly receipt: SubscriptionValueReceipt | SubscriptionCancellationReceipt;
295
+ };
296
+
297
+ export declare interface SubscriptionPeriodContext {
298
+ readonly subscription: Subscription;
299
+ readonly currencies: readonly CurrencyDefinition[];
300
+ readonly realMoney?: boolean;
301
+ readonly originAt: number;
302
+ readonly index: number;
303
+ readonly rulesVersion?: string;
304
+ }
305
+
306
+ export declare type SubscriptionPeriodResolver = (input: SubscriptionPeriodContext) => SubscriptionWindow;
307
+
308
+ export declare type SubscriptionReceiptInput = {
309
+ readonly kind: 'start';
310
+ readonly intent: SubscriptionStartInput;
311
+ } | {
312
+ readonly kind: 'renew' | 'daily' | 'cancel';
313
+ readonly intent: SubscriptionDailyInput;
314
+ };
315
+
316
+ export declare interface SubscriptionRenewInput {
317
+ readonly tenantId: string;
318
+ readonly ownerId: string;
319
+ readonly entitlementId: string;
320
+ readonly requestId: string;
321
+ }
322
+
323
+ export declare const subscriptionRenewIntent: (input: SubscriptionRenewInput) => Readonly<{
324
+ tenantId: string;
325
+ ownerId: string;
326
+ entitlementId: string;
327
+ requestId: string;
328
+ }>;
329
+
330
+ export declare class SubscriptionsPort extends SubscriptionsPort_base {
331
+ }
332
+
333
+ declare const SubscriptionsPort_base: Context.ServiceClass<SubscriptionsPort, "@voltro/SubscriptionsPort", SubscriptionsService>;
334
+
335
+ /** Renew is a trusted server intention. Implementations must deny players and
336
+ * enforce one accepted renewal per entitlement/period independently of nonce.
337
+ * Calendar/daily strategies alone cannot establish durable idempotency. */
338
+ export declare interface SubscriptionsService {
339
+ readonly list: (subject: Subject) => Effect.Effect<readonly SubscriptionView[], EconomyError>;
340
+ readonly active: (subject: Subject) => Effect.Effect<readonly SubscriptionActiveView[], EconomyError>;
341
+ readonly start: (subject: Subject, input: SubscriptionStartInput) => Effect.Effect<SubscriptionValueReceipt, EconomyError>;
342
+ readonly renew: (subject: Subject, input: SubscriptionRenewInput) => Effect.Effect<SubscriptionValueReceipt, EconomyError>;
343
+ readonly claimDaily: (subject: Subject, input: SubscriptionDailyInput) => Effect.Effect<SubscriptionValueReceipt, EconomyError>;
344
+ readonly cancel: (subject: Subject, input: SubscriptionCancelInput) => Effect.Effect<SubscriptionCancellationReceipt, EconomyError>;
345
+ readonly receipt: (subject: Subject, input: SubscriptionReceiptInput) => Effect.Effect<SubscriptionObservation, EconomyError>;
346
+ readonly exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, EconomyError>;
347
+ readonly eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, EconomyError>;
348
+ }
349
+
350
+ /** Player intentions contain observed identities, never charge amounts, dates,
351
+ * provider receipts, claimed days, entitlement state or renewal authority. */
352
+ export declare interface SubscriptionStartInput {
353
+ readonly subscriptionId: string;
354
+ readonly subscriptionVersion: string;
355
+ readonly subscriptionDigest: string;
356
+ readonly requestId: string;
357
+ readonly currency?: string | undefined;
358
+ }
359
+
360
+ export declare const subscriptionStartIntent: (input: SubscriptionStartInput) => Readonly<{
361
+ subscriptionId: string;
362
+ subscriptionVersion: string;
363
+ subscriptionDigest: string;
364
+ requestId: string;
365
+ currency: string | null;
366
+ }>;
367
+
368
+ export declare interface SubscriptionValueReceipt {
369
+ readonly operationId: string;
370
+ readonly entitlementId: string;
371
+ readonly kind: 'start' | 'renew' | 'daily';
372
+ readonly at: number;
373
+ readonly periodIndex: number;
374
+ readonly day: number | null;
375
+ readonly window: SubscriptionEntitlement;
376
+ readonly value: ShopOrderReceipt;
377
+ }
378
+
379
+ export declare interface SubscriptionView {
380
+ readonly subscription: Subscription;
381
+ readonly subscriptionDigest: string;
382
+ }
383
+
384
+ export declare interface SubscriptionWindow {
385
+ readonly startsAt: number;
386
+ readonly expiresAt: number;
387
+ }
388
+
389
+ declare interface VirtualPriceOption {
390
+ readonly currency: string;
391
+ readonly amount: EconomyAmountInput;
392
+ }
393
+
394
+ export { }
@@ -0,0 +1,97 @@
1
+ import { defaultSubscriptionDailyGrant as e, defaultSubscriptionPeriod as t, defineSubscription as n, resolveSubscriptionDailyGrant as r, resolveSubscriptionPeriod as i } from "./subscriptions.js";
2
+ import { Effect as a } from "effect";
3
+ //#region src/subscriptions.fixture.ts
4
+ var o = () => {
5
+ let e = Object.freeze([{
6
+ id: "test.gold",
7
+ kind: "soft",
8
+ cap: "1000000"
9
+ }]), t = n({
10
+ id: "test.plus",
11
+ titleKey: "test.plus",
12
+ price: {
13
+ kind: "virtual",
14
+ options: [{
15
+ currency: "test.gold",
16
+ amount: "10"
17
+ }]
18
+ },
19
+ period: {
20
+ unit: "month",
21
+ count: 1
22
+ },
23
+ grants: {
24
+ onPurchase: { currencies: [{
25
+ currency: "test.gold",
26
+ amount: "2"
27
+ }] },
28
+ daily: { currencies: [{
29
+ currency: "test.gold",
30
+ amount: "1"
31
+ }] }
32
+ },
33
+ graceDays: 3,
34
+ disclosure: {
35
+ durationKey: "test.plus.duration",
36
+ renewalKey: "test.plus.renewal",
37
+ cancellationPath: "/account/subscriptions"
38
+ }
39
+ }, e);
40
+ return Object.freeze({
41
+ subscription: t,
42
+ currencies: e,
43
+ realMoney: !1,
44
+ originAt: Date.UTC(2024, 0, 31, 12),
45
+ index: 0
46
+ });
47
+ }, s = o(), c = t(s), l = Object.freeze({
48
+ kind: "slot",
49
+ defaultImplementation: t,
50
+ defaultProbe: {
51
+ run: () => a.sync(() => i(s)),
52
+ expected: c
53
+ },
54
+ overrideProbe: {
55
+ run: () => a.sync(() => i({
56
+ ...s,
57
+ rulesVersion: "test.narrow.v1"
58
+ }, (e) => ({
59
+ ...t(e),
60
+ expiresAt: c.expiresAt - 1
61
+ }))),
62
+ expected: {
63
+ ...c,
64
+ expiresAt: c.expiresAt - 1
65
+ }
66
+ }
67
+ }), u = Object.freeze({
68
+ entitlement: Object.freeze({
69
+ ...c,
70
+ graceUntil: c.expiresAt + 2592e5,
71
+ state: "active"
72
+ }),
73
+ at: s.originAt,
74
+ claimedDays: Object.freeze([])
75
+ }), d = Object.freeze({
76
+ kind: "slot",
77
+ defaultImplementation: e,
78
+ defaultProbe: {
79
+ run: () => a.sync(() => r(u)),
80
+ expected: {
81
+ eligible: !0,
82
+ day: Math.floor(s.originAt / 864e5)
83
+ }
84
+ },
85
+ overrideProbe: {
86
+ run: () => a.sync(() => r({
87
+ ...u,
88
+ rulesVersion: "test.deny.v1"
89
+ }, () => !1)),
90
+ expected: {
91
+ eligible: !1,
92
+ day: Math.floor(s.originAt / 864e5)
93
+ }
94
+ }
95
+ });
96
+ //#endregion
97
+ export { d as subscriptionDailySystemFixture, o as subscriptionFixture, l as subscriptionPeriodSystemFixture };