@molecule/app-iap 1.0.0 → 1.0.1

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 (2) hide show
  1. package/README.md +732 -0
  2. package/package.json +7 -6
package/README.md ADDED
@@ -0,0 +1,732 @@
1
+ <!--
2
+ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
+ Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
+ Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
+ To change this document, edit the module-level JSDoc in src/index.ts.
6
+ Generated: 2026-08-04T01:51:02.000Z
7
+ -->
8
+
9
+ # @molecule/app-iap
10
+
11
+ > **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
12
+ > It is written to be read by coding agents as much as by people, and is generated from this
13
+ > package's source — edit `src/index.ts` JSDoc, not this file.
14
+
15
+ In-App Purchases interface for molecule.dev.
16
+
17
+ Provides a unified API for in-app purchases that works across
18
+ different platforms (iOS App Store, Google Play, web Stripe).
19
+
20
+ ## Quick Start
21
+
22
+ ```tsx
23
+ import {
24
+ createNoopIAPProvider,
25
+ initialize,
26
+ order,
27
+ finish,
28
+ register,
29
+ refresh,
30
+ setProvider,
31
+ verify,
32
+ } from '@molecule/app-iap'
33
+
34
+ // Wire the provider at app startup (swap for an iOS/Android bond in production)
35
+ setProvider(createNoopIAPProvider())
36
+ await initialize()
37
+ register([{ id: 'com.example.pro_monthly', alias: 'pro_monthly', type: 'subscription' }])
38
+ await refresh()
39
+
40
+ const result = await order('pro_monthly')
41
+ if (result.success && result.product) {
42
+ await verify(result.product, '/api/iap/verify')
43
+ finish(result.product)
44
+ }
45
+ ```
46
+
47
+ ## Type
48
+
49
+ `feature`
50
+
51
+ ## Installation
52
+
53
+ ```bash
54
+ npm install @molecule/app-iap @molecule/app-bond @molecule/app-i18n
55
+ ```
56
+
57
+ ## API
58
+
59
+ ### Interfaces
60
+
61
+ #### `IAPError`
62
+
63
+ In-app purchase error with error code, product ID, and platform-specific details.
64
+
65
+ ```typescript
66
+ interface IAPError {
67
+ /**
68
+ * Error code.
69
+ */
70
+ code: string | number
71
+
72
+ /**
73
+ * Error message.
74
+ */
75
+ message: string
76
+
77
+ /**
78
+ * Product ID (if applicable).
79
+ */
80
+ productId?: string
81
+
82
+ /**
83
+ * Raw error.
84
+ */
85
+ raw?: unknown
86
+ }
87
+ ```
88
+
89
+ #### `IAPProvider`
90
+
91
+ In-App Purchases provider interface.
92
+
93
+ ```typescript
94
+ interface IAPProvider {
95
+ /**
96
+ * Initializes the IAP system.
97
+ */
98
+ initialize(): Promise<void>
99
+
100
+ /**
101
+ * Registers products for purchase.
102
+ */
103
+ register(products: ProductDefinition[]): void
104
+
105
+ /**
106
+ * Refreshes product information from the store.
107
+ */
108
+ refresh(): Promise<void>
109
+
110
+ /**
111
+ * Gets a product by ID or alias.
112
+ */
113
+ get(idOrAlias: string): Product | undefined
114
+
115
+ /**
116
+ * Gets all registered products.
117
+ */
118
+ getAll(): Product[]
119
+
120
+ /**
121
+ * Checks if a product can be purchased.
122
+ */
123
+ canPurchase(idOrAlias: string): boolean
124
+
125
+ /**
126
+ * Initiates a purchase.
127
+ */
128
+ order(idOrAlias: string): Promise<PurchaseResult>
129
+
130
+ /**
131
+ * Finishes a transaction (must be called after successful verification).
132
+ */
133
+ finish(product: Product): void
134
+
135
+ /**
136
+ * Verifies a purchase with the server.
137
+ */
138
+ verify(
139
+ product: Product,
140
+ verifyUrl: string,
141
+ additionalData?: Record<string, unknown>,
142
+ ): Promise<VerificationResult>
143
+
144
+ /**
145
+ * Restores previous purchases.
146
+ */
147
+ restore(): Promise<Product[]>
148
+
149
+ /**
150
+ * Opens the subscription management page.
151
+ */
152
+ manageSubscriptions(): void
153
+
154
+ /**
155
+ * Subscribes to product events.
156
+ */
157
+ when(idOrAlias: string): {
158
+ updated: (handler: ProductEventHandler) => void
159
+ approved: (handler: ProductEventHandler) => void
160
+ finished: (handler: ProductEventHandler) => void
161
+ cancelled: (handler: ProductEventHandler) => void
162
+ error: (handler: ErrorEventHandler) => void
163
+ }
164
+
165
+ /**
166
+ * Subscribes to global events.
167
+ */
168
+ on(event: IAPEvent, handler: IAPEventHandler): () => void
169
+
170
+ /**
171
+ * Unsubscribes from events.
172
+ */
173
+ off(handler: IAPEventHandler): void
174
+
175
+ /**
176
+ * Gets the platform name.
177
+ */
178
+ getPlatform(): 'ios' | 'android' | 'web' | 'unknown'
179
+
180
+ /**
181
+ * Checks if IAP is available.
182
+ */
183
+ isAvailable(): boolean
184
+
185
+ /**
186
+ * Destroys the IAP system.
187
+ */
188
+ destroy(): void
189
+ }
190
+ ```
191
+
192
+ #### `Product`
193
+
194
+ Full in-app product details (ID, type, state, pricing, ownership, transaction, subscription info).
195
+
196
+ ```typescript
197
+ interface Product {
198
+ /**
199
+ * Product ID (SKU).
200
+ */
201
+ id: string
202
+
203
+ /**
204
+ * Product alias.
205
+ */
206
+ alias: string
207
+
208
+ /**
209
+ * Product type.
210
+ */
211
+ type: ProductType
212
+
213
+ /**
214
+ * Product state.
215
+ */
216
+ state: ProductState
217
+
218
+ /**
219
+ * Product title.
220
+ */
221
+ title: string
222
+
223
+ /**
224
+ * Product description.
225
+ */
226
+ description: string
227
+
228
+ /**
229
+ * Formatted price string.
230
+ */
231
+ price: string
232
+
233
+ /**
234
+ * Price in micros (cents * 10000).
235
+ */
236
+ priceMicros: number
237
+
238
+ /**
239
+ * Currency code (ISO 4217).
240
+ */
241
+ currency: string
242
+
243
+ /**
244
+ * Whether the product can be purchased.
245
+ */
246
+ canPurchase: boolean
247
+
248
+ /**
249
+ * Whether the product is owned.
250
+ */
251
+ owned: boolean
252
+
253
+ /**
254
+ * Transaction information (if applicable).
255
+ */
256
+ transaction?: Transaction
257
+
258
+ /**
259
+ * Subscription period (for subscriptions).
260
+ */
261
+ subscriptionPeriod?: SubscriptionPeriod
262
+
263
+ /**
264
+ * Introductory price (for subscriptions with trial).
265
+ */
266
+ introPrice?: string
267
+
268
+ /**
269
+ * Trial period in days.
270
+ */
271
+ trialPeriodDays?: number
272
+
273
+ /**
274
+ * Product group.
275
+ */
276
+ group?: string
277
+
278
+ /**
279
+ * Raw platform-specific data.
280
+ */
281
+ raw?: unknown
282
+ }
283
+ ```
284
+
285
+ #### `ProductDefinition`
286
+
287
+ Product definition for registration.
288
+
289
+ ```typescript
290
+ interface ProductDefinition {
291
+ /**
292
+ * Product ID (SKU) - platform-specific identifier.
293
+ */
294
+ id: string
295
+
296
+ /**
297
+ * Product alias - cross-platform identifier.
298
+ */
299
+ alias: string
300
+
301
+ /**
302
+ * Product type.
303
+ */
304
+ type: ProductType
305
+
306
+ /**
307
+ * Product group (for subscription grouping).
308
+ */
309
+ group?: string
310
+ }
311
+ ```
312
+
313
+ #### `PurchaseResult`
314
+
315
+ Outcome of a purchase attempt (success flag, product, transaction, or error).
316
+
317
+ ```typescript
318
+ interface PurchaseResult {
319
+ /**
320
+ * Whether the purchase was successful.
321
+ */
322
+ success: boolean
323
+
324
+ /**
325
+ * The purchased product.
326
+ */
327
+ product?: Product
328
+
329
+ /**
330
+ * Transaction information.
331
+ */
332
+ transaction?: Transaction
333
+
334
+ /**
335
+ * Error (if purchase failed).
336
+ */
337
+ error?: IAPError
338
+ }
339
+ ```
340
+
341
+ #### `Transaction`
342
+
343
+ Purchase transaction record (ID, receipt, purchase token, timestamps, validity).
344
+
345
+ ```typescript
346
+ interface Transaction {
347
+ /**
348
+ * Transaction ID.
349
+ */
350
+ id: string
351
+
352
+ /**
353
+ * Platform-specific receipt.
354
+ */
355
+ receipt?: string
356
+
357
+ /**
358
+ * App Store receipt (iOS).
359
+ */
360
+ appStoreReceipt?: string
361
+
362
+ /**
363
+ * Purchase token (Android).
364
+ */
365
+ purchaseToken?: string
366
+
367
+ /**
368
+ * Purchase time.
369
+ */
370
+ purchaseTime?: Date
371
+
372
+ /**
373
+ * Expiration time (for subscriptions).
374
+ */
375
+ expirationTime?: Date
376
+
377
+ /**
378
+ * Whether the purchase is valid.
379
+ */
380
+ isValid?: boolean
381
+
382
+ /**
383
+ * Raw platform-specific data.
384
+ */
385
+ raw?: unknown
386
+ }
387
+ ```
388
+
389
+ #### `VerificationResult`
390
+
391
+ Verification result from server.
392
+
393
+ ```typescript
394
+ interface VerificationResult {
395
+ /**
396
+ * Whether the receipt is valid.
397
+ */
398
+ valid: boolean
399
+
400
+ /**
401
+ * Subscription expiration time.
402
+ */
403
+ expirationTime?: Date
404
+
405
+ /**
406
+ * Whether the subscription is active.
407
+ */
408
+ isActive?: boolean
409
+
410
+ /**
411
+ * Plan/tier information.
412
+ */
413
+ plan?: string
414
+
415
+ /**
416
+ * Server response data.
417
+ */
418
+ data?: unknown
419
+ }
420
+ ```
421
+
422
+ ### Types
423
+
424
+ #### `ErrorEventHandler`
425
+
426
+ Event handler called with an IAPError when a purchase error occurs.
427
+
428
+ ```typescript
429
+ type ErrorEventHandler = (error: IAPError) => void
430
+ ```
431
+
432
+ #### `IAPEvent`
433
+
434
+ IAP lifecycle events: ready, product-updated, approved, finished, cancelled, error, pending, expired, restored.
435
+
436
+ ```typescript
437
+ type IAPEvent =
438
+ | 'ready'
439
+ | 'product-updated'
440
+ | 'approved'
441
+ | 'finished'
442
+ | 'cancelled'
443
+ | 'error'
444
+ | 'pending'
445
+ | 'expired'
446
+ | 'restored'
447
+ ```
448
+
449
+ #### `IAPEventHandler`
450
+
451
+ Generic event handler for IAP events.
452
+
453
+ ```typescript
454
+ type IAPEventHandler<T = unknown> = (data: T) => void
455
+ ```
456
+
457
+ #### `ProductEventHandler`
458
+
459
+ Event handler called with a Product when a product-related event occurs.
460
+
461
+ ```typescript
462
+ type ProductEventHandler = (product: Product) => void
463
+ ```
464
+
465
+ #### `ProductState`
466
+
467
+ Purchase lifecycle state of an in-app product (registered, valid, approved, owned, cancelled, etc.).
468
+
469
+ ```typescript
470
+ type ProductState =
471
+ | 'registered'
472
+ | 'valid'
473
+ | 'invalid'
474
+ | 'requested'
475
+ | 'initiated'
476
+ | 'approved'
477
+ | 'finished'
478
+ | 'owned'
479
+ | 'cancelled'
480
+ | 'downloading'
481
+ ```
482
+
483
+ #### `ProductType`
484
+
485
+ In-app purchase product categories: one-time consumable, permanent non-consumable, or recurring subscription.
486
+
487
+ ```typescript
488
+ type ProductType = 'consumable' | 'non-consumable' | 'subscription'
489
+ ```
490
+
491
+ #### `SubscriptionPeriod`
492
+
493
+ Subscription billing interval: weekly, monthly, yearly, or lifetime.
494
+
495
+ ```typescript
496
+ type SubscriptionPeriod = 'weekly' | 'monthly' | 'yearly' | 'lifetime'
497
+ ```
498
+
499
+ ### Functions
500
+
501
+ #### `createNoopIAPProvider()`
502
+
503
+ Creates a no-op IAP provider for web/testing.
504
+
505
+ ```typescript
506
+ function createNoopIAPProvider(): IAPProvider
507
+ ```
508
+
509
+ **Returns:** A no-op IAP provider that stubs all purchase operations.
510
+
511
+ #### `finish(product)`
512
+
513
+ Finishes a pending transaction, acknowledging delivery to the store.
514
+
515
+ ```typescript
516
+ function finish(product: Product): void
517
+ ```
518
+
519
+ - `product` — The product whose transaction should be finalized.
520
+
521
+ **Returns:** Nothing.
522
+
523
+ #### `get(idOrAlias)`
524
+
525
+ Gets a product by its store ID or registered alias.
526
+
527
+ ```typescript
528
+ function get(idOrAlias: string): Product | undefined
529
+ ```
530
+
531
+ - `idOrAlias` — The product store ID or alias to look up.
532
+
533
+ **Returns:** The matching product, or undefined if not found.
534
+
535
+ #### `getAll()`
536
+
537
+ Gets all registered products.
538
+
539
+ ```typescript
540
+ function getAll(): Product[]
541
+ ```
542
+
543
+ **Returns:** An array of all available products.
544
+
545
+ #### `getErrorMessage(error, t)`
546
+
547
+ Gets a user-friendly error message.
548
+
549
+ ```typescript
550
+ function getErrorMessage(
551
+ error: unknown,
552
+ t?: (
553
+ key: string,
554
+ values?: Record<string, unknown>,
555
+ options?: { defaultValue?: string },
556
+ ) => string,
557
+ ): string
558
+ ```
559
+
560
+ - `error` — The IAP error object or unknown thrown value to translate.
561
+ - `t` — Optional i18n translation function for localized messages.
562
+
563
+ **Returns:** A user-friendly error message string.
564
+
565
+ #### `getProvider()`
566
+
567
+ Gets the current IAP provider. Falls back to a no-op provider if none has been
568
+ bonded — on web this is correct (no store exists), but on iOS/Android a missing
569
+ real provider means every `order()` reports unavailable. The fallback warns when
570
+ it engages so the omission is visible rather than a silent failure.
571
+
572
+ ```typescript
573
+ function getProvider(): IAPProvider
574
+ ```
575
+
576
+ **Returns:** The active IAP provider instance.
577
+
578
+ #### `hasProvider()`
579
+
580
+ Checks if an IAP provider has been bonded.
581
+
582
+ ```typescript
583
+ function hasProvider(): boolean
584
+ ```
585
+
586
+ **Returns:** Whether an IAP provider is currently registered.
587
+
588
+ #### `initialize()`
589
+
590
+ Initializes the IAP system via the active provider.
591
+
592
+ ```typescript
593
+ function initialize(): Promise<void>
594
+ ```
595
+
596
+ **Returns:** A promise that resolves when initialization is complete.
597
+
598
+ #### `isAvailable()`
599
+
600
+ Checks if in-app purchases are available on the current platform.
601
+
602
+ ```typescript
603
+ function isAvailable(): boolean
604
+ ```
605
+
606
+ **Returns:** Whether the IAP system is available and functional.
607
+
608
+ #### `manageSubscriptions()`
609
+
610
+ Opens the platform's subscription management UI.
611
+
612
+ ```typescript
613
+ function manageSubscriptions(): void
614
+ ```
615
+
616
+ **Returns:** Nothing.
617
+
618
+ #### `order(idOrAlias)`
619
+
620
+ Initiates a purchase order for a product.
621
+
622
+ ```typescript
623
+ function order(idOrAlias: string): Promise<PurchaseResult>
624
+ ```
625
+
626
+ - `idOrAlias` — The product store ID or alias to purchase.
627
+
628
+ **Returns:** A promise that resolves with the purchase result.
629
+
630
+ #### `refresh()`
631
+
632
+ Refreshes product information from the store.
633
+
634
+ ```typescript
635
+ function refresh(): Promise<void>
636
+ ```
637
+
638
+ **Returns:** A promise that resolves when product data has been refreshed.
639
+
640
+ #### `register(products)`
641
+
642
+ Registers product definitions with the IAP provider.
643
+
644
+ ```typescript
645
+ function register(products: ProductDefinition[]): void
646
+ ```
647
+
648
+ - `products` — The product definitions to register for purchase availability.
649
+
650
+ **Returns:** Nothing.
651
+
652
+ #### `restore()`
653
+
654
+ Restores previously completed purchases from the store.
655
+
656
+ ```typescript
657
+ function restore(): Promise<Product[]>
658
+ ```
659
+
660
+ **Returns:** A promise that resolves with an array of restored products.
661
+
662
+ #### `setProvider(provider)`
663
+
664
+ Sets the IAP provider.
665
+
666
+ ```typescript
667
+ function setProvider(provider: IAPProvider): void
668
+ ```
669
+
670
+ - `provider` — The IAP provider implementation to bond.
671
+
672
+ #### `verify(product, verifyUrl, additionalData)`
673
+
674
+ Verifies a purchase receipt with a server endpoint.
675
+
676
+ ```typescript
677
+ function verify(
678
+ product: Product,
679
+ verifyUrl: string,
680
+ additionalData?: Record<string, unknown>,
681
+ ): Promise<VerificationResult>
682
+ ```
683
+
684
+ - `product` — The product whose purchase receipt to verify.
685
+ - `verifyUrl` — The server URL to send the verification request to.
686
+ - `additionalData` — Extra data to include in the verification payload.
687
+
688
+ **Returns:** A promise that resolves with the server verification result.
689
+
690
+ ### Constants
691
+
692
+ #### `errorMessages`
693
+
694
+ Error code mapping for user-friendly messages.
695
+ Derived from defaultTranslations to avoid duplicating strings.
696
+
697
+ ```typescript
698
+ const errorMessages: Record<string, string>
699
+ ```
700
+
701
+ ## Injection Notes
702
+
703
+ ### Requirements
704
+
705
+ Peer dependencies:
706
+
707
+ - `@molecule/app-bond` ^1.0.1
708
+ - `@molecule/app-i18n` ^1.0.1
709
+
710
+ ### Runtime Dependencies
711
+
712
+ - `@molecule/app-bond`
713
+ - `@molecule/app-i18n`
714
+
715
+ - NO store provider ships with the fleet yet: the only built-in
716
+ implementation is `createNoopIAPProvider()`, and `getProvider()` silently
717
+ self-bonds it when nothing is wired — so without a real provider
718
+ `order()` ALWAYS fails with `E_NOT_AVAILABLE`, `verify()` returns
719
+ `{ valid: false }`, and `isAvailable()` is `false`. To sell on iOS /
720
+ Android, implement `IAPProvider` yourself (e.g. wrapping
721
+ cordova-plugin-purchase, StoreKit 2, or Play Billing) and wire it with
722
+ `setProvider()` at startup.
723
+ - `verify()` POSTs the purchase to YOUR server: implement the endpoint with
724
+ `@molecule/api-payments-apple` / `@molecule/api-payments-google` (receipt
725
+ validation) and call `finish()` only after the server says the receipt is
726
+ valid — finishing first loses the purchase if validation fails.
727
+ - Error messages route through `t('iap.error.*')` with English fallbacks;
728
+ the `@molecule/app-locales-iap` bond supplies 79 translations.
729
+
730
+ ## Translations
731
+
732
+ Translation strings are provided by `@molecule/app-locales-iap`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/app-iap",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "In-App Purchases interface for molecule.dev",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -17,7 +17,8 @@
17
17
  }
18
18
  },
19
19
  "files": [
20
- "dist"
20
+ "dist",
21
+ "README.md"
21
22
  ],
22
23
  "keywords": [
23
24
  "molecule",
@@ -29,12 +30,12 @@
29
30
  ],
30
31
  "license": "Apache-2.0",
31
32
  "peerDependencies": {
32
- "@molecule/app-bond": "^1.0.0",
33
- "@molecule/app-i18n": "^1.0.0"
33
+ "@molecule/app-bond": "^1.0.1",
34
+ "@molecule/app-i18n": "^1.0.1"
34
35
  },
35
36
  "devDependencies": {
36
- "@molecule/app-bond": "1.0.0",
37
- "@molecule/app-i18n": "1.0.0",
37
+ "@molecule/app-bond": "1.0.1",
38
+ "@molecule/app-i18n": "1.0.1",
38
39
  "@types/node": "26.1.2",
39
40
  "typescript": "6.0.3",
40
41
  "vitest": "4.1.10"