@choochmeque/tauri-plugin-iap-api 0.4.5 → 0.5.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.
package/README.md CHANGED
@@ -1,10 +1,12 @@
1
1
  [![NPM Version](https://img.shields.io/npm/v/@choochmeque%2Ftauri-plugin-iap-api)](https://www.npmjs.com/package/@choochmeque/tauri-plugin-iap-api)
2
2
  [![Crates.io Version](https://img.shields.io/crates/v/tauri-plugin-iap)](https://crates.io/crates/tauri-plugin-iap)
3
+ [![Tests](https://github.com/Choochmeque/tauri-plugin-iap/actions/workflows/tests.yml/badge.svg)](https://github.com/Choochmeque/tauri-plugin-iap/actions/workflows/tests.yml)
4
+ [![codecov](https://codecov.io/gh/Choochmeque/tauri-plugin-iap/branch/main/graph/badge.svg)](https://codecov.io/gh/Choochmeque/tauri-plugin-iap)
3
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
4
6
 
5
7
  # Tauri Plugin IAP
6
8
 
7
- A Tauri plugin for In-App Purchases (IAP) with support for subscriptions on iOS (StoreKit 2), Android (Google Play Billing) and Windows (Microsoft Store).
9
+ A Tauri plugin for In-App Purchases (IAP) with support for subscriptions on iOS (StoreKit 2), Android (Google Play Billing), Windows (Microsoft Store) and macOS (StoreKit 2).
8
10
 
9
11
  ## Features
10
12
 
@@ -26,7 +28,7 @@ A Tauri plugin for In-App Purchases (IAP) with support for subscriptions on iOS
26
28
  - **iOS**: StoreKit 2 (requires iOS 15.0+)
27
29
  - **Android**: Google Play Billing Library v8.0.0
28
30
  - **Windows**: Microsoft Store API (Windows 10/11)
29
- - **macOS**: Experimental support - might not work correctly (still required some work to do)
31
+ - **macOS**: StoreKit 2 (requires macOS 13.0+)
30
32
 
31
33
  ## Installation
32
34
 
@@ -44,7 +46,7 @@ Add the plugin to your Tauri project's `Cargo.toml`:
44
46
 
45
47
  ```toml
46
48
  [dependencies]
47
- tauri-plugin-iap = "0.4"
49
+ tauri-plugin-iap = "0.5"
48
50
  ```
49
51
 
50
52
  Configure the plugin permissions in your `capabilities/default.json`:
@@ -82,7 +84,7 @@ import {
82
84
  getProductStatus,
83
85
  onPurchaseUpdated,
84
86
  PurchaseState
85
- } from 'tauri-plugin-iap-api';
87
+ } from '@choochmeque/tauri-plugin-iap-api';
86
88
 
87
89
  // Initialize the billing client
88
90
  await initialize();
@@ -126,12 +128,12 @@ const restored = await restorePurchases('subs');
126
128
  await acknowledgePurchase(purchaseResult.purchaseToken);
127
129
 
128
130
  // Listen for purchase updates
129
- const unlisten = onPurchaseUpdated((purchase) => {
131
+ const listener = await onPurchaseUpdated((purchase) => {
130
132
  console.log('Purchase updated:', purchase);
131
133
  });
132
134
 
133
135
  // Stop listening
134
- unlisten();
136
+ await listener.unregister();
135
137
  ```
136
138
 
137
139
  ## Platform Setup
@@ -221,9 +223,11 @@ Checks the ownership and subscription status of a specific product.
221
223
  - `isAcknowledged`: Whether the purchase has been acknowledged
222
224
  - `purchaseToken`: Token for the purchase transaction
223
225
 
224
- ### `onPurchaseUpdated(callback: (purchase: Purchase) => void)`
226
+ ### `onPurchaseUpdated(callback: (purchase: Purchase) => void): Promise<PluginListener>`
225
227
  Listens for purchase state changes.
226
228
 
229
+ **Returns:** A `PluginListener` object with an `unregister()` method to stop listening.
230
+
227
231
  ## Differences Between Platforms
228
232
 
229
233
  ### iOS (StoreKit 2)
@@ -244,6 +248,13 @@ Listens for purchase state changes.
244
248
  - Supports consumables, durables, and subscriptions
245
249
  - Uses SKUs for subscription offer variations
246
250
 
251
+ ### macOS (StoreKit 2)
252
+ - Same StoreKit 2 API as iOS
253
+ - Automatic transaction verification
254
+ - No manual acknowledgment needed
255
+ - Requires macOS 13.0+
256
+ - App must be code-signed (StoreKit requires valid signature)
257
+
247
258
  ## Testing
248
259
 
249
260
  ### iOS
@@ -262,6 +273,12 @@ Listens for purchase state changes.
262
273
  3. Test with Windows Dev Center test payment methods
263
274
  4. Ensure app is associated with Store listing
264
275
 
276
+ ### macOS
277
+ 1. Use sandbox test accounts (same as iOS)
278
+ 2. Use StoreKit Configuration files for local testing
279
+ 3. App must be code-signed to use StoreKit
280
+ 4. Clear purchase history in System Settings > App Store > Sandbox Account
281
+
265
282
  ## License
266
283
 
267
284
  [MIT](LICENSE)
package/dist-js/index.cjs CHANGED
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  var core = require('@tauri-apps/api/core');
4
- var event = require('@tauri-apps/api/event');
5
4
 
6
5
  /**
7
6
  * Purchase state enumeration
@@ -173,10 +172,10 @@ async function getProductStatus(productId, productType = "subs") {
173
172
  * This event is triggered when a purchase state changes.
174
173
  *
175
174
  * @param callback - Function to call when a purchase is updated
176
- * @returns Cleanup function to stop listening
175
+ * @returns Promise resolving to a PluginListener that can be used to stop listening
177
176
  * @example
178
177
  * ```typescript
179
- * const unsubscribe = onPurchaseUpdated((purchase) => {
178
+ * const listener = await onPurchaseUpdated((purchase) => {
180
179
  * console.log(`Purchase updated: ${purchase.productId}`);
181
180
  * if (purchase.purchaseState === PurchaseState.PURCHASED) {
182
181
  * // Handle successful purchase
@@ -184,16 +183,11 @@ async function getProductStatus(productId, productType = "subs") {
184
183
  * });
185
184
  *
186
185
  * // Later, stop listening
187
- * unsubscribe();
186
+ * await listener.unregister();
188
187
  * ```
189
188
  */
190
- function onPurchaseUpdated(callback) {
191
- const unlisten = event.listen("purchaseUpdated", (event) => {
192
- callback(event.payload);
193
- });
194
- return () => {
195
- unlisten.then((fn) => fn());
196
- };
189
+ async function onPurchaseUpdated(callback) {
190
+ return await core.addPluginListener("iap", "purchaseUpdated", callback);
197
191
  }
198
192
 
199
193
  exports.acknowledgePurchase = acknowledgePurchase;
@@ -1,3 +1,4 @@
1
+ import { PluginListener } from "@tauri-apps/api/core";
1
2
  /**
2
3
  * Response from IAP initialization
3
4
  */
@@ -28,13 +29,21 @@ export interface SubscriptionOffer {
28
29
  * Product information from the app store
29
30
  */
30
31
  export interface Product {
32
+ /** Unique product identifier as configured in the app store */
31
33
  productId: string;
34
+ /** Localized product title */
32
35
  title: string;
36
+ /** Localized product description */
33
37
  description: string;
38
+ /** Type of product: "subs" for subscriptions, "inapp" for one-time purchases */
34
39
  productType: string;
40
+ /** Localized price string with currency symbol (e.g., "$9.99") */
35
41
  formattedPrice?: string;
42
+ /** ISO 4217 currency code (e.g., "USD", "EUR") */
36
43
  priceCurrencyCode?: string;
44
+ /** Price in micros (price × 1,000,000). For example, $9.99 = 9990000 */
37
45
  priceAmountMicros?: number;
46
+ /** Subscription offer details including pricing phases. (Android only) */
38
47
  subscriptionOfferDetails?: SubscriptionOffer[];
39
48
  }
40
49
  /**
@@ -47,16 +56,28 @@ export interface GetProductsResponse {
47
56
  * Purchase transaction information
48
57
  */
49
58
  export interface Purchase {
59
+ /** Unique order identifier from the store. May be undefined for pending purchases. */
50
60
  orderId?: string;
61
+ /** Application package name (Android) or bundle identifier (iOS/macOS) */
51
62
  packageName: string;
63
+ /** Product identifier that was purchased */
52
64
  productId: string;
65
+ /** Unix timestamp (milliseconds) when the purchase was made */
53
66
  purchaseTime: number;
67
+ /** Token used to identify this purchase for acknowledgment and server-side verification */
54
68
  purchaseToken: string;
55
- purchaseState: number;
69
+ /** Current state of the purchase. */
70
+ purchaseState: PurchaseState;
71
+ /** Whether this subscription is set to auto-renew. Always false for one-time purchases. */
56
72
  isAutoRenewing: boolean;
73
+ /** Whether the purchase has been acknowledged. Unacknowledged purchases are refunded after 3 days. (Android only, always true on iOS/macOS) */
57
74
  isAcknowledged: boolean;
75
+ /** Raw JSON response from the store for server-side verification. (Android only) */
58
76
  originalJson: string;
77
+ /** Cryptographic signature for purchase verification. (Android only) */
59
78
  signature: string;
79
+ /** Original transaction ID. Used to link renewals and restores to the original purchase. (iOS/macOS only) */
80
+ originalId?: string;
60
81
  }
61
82
  /**
62
83
  * Response containing restored purchases
@@ -244,10 +265,10 @@ export declare function getProductStatus(productId: string, productType?: "subs"
244
265
  * This event is triggered when a purchase state changes.
245
266
  *
246
267
  * @param callback - Function to call when a purchase is updated
247
- * @returns Cleanup function to stop listening
268
+ * @returns Promise resolving to a PluginListener that can be used to stop listening
248
269
  * @example
249
270
  * ```typescript
250
- * const unsubscribe = onPurchaseUpdated((purchase) => {
271
+ * const listener = await onPurchaseUpdated((purchase) => {
251
272
  * console.log(`Purchase updated: ${purchase.productId}`);
252
273
  * if (purchase.purchaseState === PurchaseState.PURCHASED) {
253
274
  * // Handle successful purchase
@@ -255,7 +276,7 @@ export declare function getProductStatus(productId: string, productType?: "subs"
255
276
  * });
256
277
  *
257
278
  * // Later, stop listening
258
- * unsubscribe();
279
+ * await listener.unregister();
259
280
  * ```
260
281
  */
261
- export declare function onPurchaseUpdated(callback: (purchase: Purchase) => void): () => void;
282
+ export declare function onPurchaseUpdated(callback: (purchase: Purchase) => void): Promise<PluginListener>;
package/dist-js/index.js CHANGED
@@ -1,5 +1,4 @@
1
- import { invoke } from '@tauri-apps/api/core';
2
- import { listen } from '@tauri-apps/api/event';
1
+ import { invoke, addPluginListener } from '@tauri-apps/api/core';
3
2
 
4
3
  /**
5
4
  * Purchase state enumeration
@@ -171,10 +170,10 @@ async function getProductStatus(productId, productType = "subs") {
171
170
  * This event is triggered when a purchase state changes.
172
171
  *
173
172
  * @param callback - Function to call when a purchase is updated
174
- * @returns Cleanup function to stop listening
173
+ * @returns Promise resolving to a PluginListener that can be used to stop listening
175
174
  * @example
176
175
  * ```typescript
177
- * const unsubscribe = onPurchaseUpdated((purchase) => {
176
+ * const listener = await onPurchaseUpdated((purchase) => {
178
177
  * console.log(`Purchase updated: ${purchase.productId}`);
179
178
  * if (purchase.purchaseState === PurchaseState.PURCHASED) {
180
179
  * // Handle successful purchase
@@ -182,16 +181,11 @@ async function getProductStatus(productId, productType = "subs") {
182
181
  * });
183
182
  *
184
183
  * // Later, stop listening
185
- * unsubscribe();
184
+ * await listener.unregister();
186
185
  * ```
187
186
  */
188
- function onPurchaseUpdated(callback) {
189
- const unlisten = listen("purchaseUpdated", (event) => {
190
- callback(event.payload);
191
- });
192
- return () => {
193
- unlisten.then((fn) => fn());
194
- };
187
+ async function onPurchaseUpdated(callback) {
188
+ return await addPluginListener("iap", "purchaseUpdated", callback);
195
189
  }
196
190
 
197
191
  export { PurchaseState, acknowledgePurchase, getProductStatus, getProducts, getPurchaseHistory, initialize, onPurchaseUpdated, purchase, restorePurchases };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@choochmeque/tauri-plugin-iap-api",
3
- "version": "0.4.5",
3
+ "version": "0.5.0",
4
4
  "license": "MIT",
5
5
  "author": "You",
6
6
  "description": "A Tauri v2 plugin that enables In-App Purchases (IAP)",
@@ -37,15 +37,23 @@
37
37
  },
38
38
  "devDependencies": {
39
39
  "@rollup/plugin-typescript": "^12.1.4",
40
+ "@vitest/coverage-v8": "^4.0.13",
41
+ "@vitest/ui": "^4.0.13",
42
+ "happy-dom": "^20.0.10",
43
+ "prettier": "3.6.2",
40
44
  "rollup": "^4.9.6",
41
45
  "tslib": "^2.6.2",
42
46
  "typescript": "^5.3.3",
43
- "prettier": "3.6.2"
47
+ "vitest": "^4.0.13"
44
48
  },
45
49
  "scripts": {
46
50
  "build": "rollup -c",
47
51
  "pretest": "pnpm build",
52
+ "test": "vitest run",
53
+ "test:watch": "vitest",
54
+ "test:ui": "vitest --ui",
55
+ "test:coverage": "vitest run --coverage",
48
56
  "format": "prettier --write \"./**/*.{cjs,mjs,js,jsx,mts,ts,tsx,html,css,json}\"",
49
- "format-check": "prettier --check \"./**/*.{cjs,mjs,js,jsx,mts,ts,tsx,html,css,json}\""
57
+ "format:check": "prettier --check \"./**/*.{cjs,mjs,js,jsx,mts,ts,tsx,html,css,json}\""
50
58
  }
51
59
  }