@choochmeque/tauri-plugin-iap-api 0.4.5 → 0.5.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.
- package/README.md +88 -7
- package/dist-js/index.cjs +5 -11
- package/dist-js/index.d.ts +26 -5
- package/dist-js/index.js +6 -12
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
[](https://www.npmjs.com/package/@choochmeque/tauri-plugin-iap-api)
|
|
2
2
|
[](https://crates.io/crates/tauri-plugin-iap)
|
|
3
|
+
[](https://github.com/Choochmeque/tauri-plugin-iap/actions/workflows/tests.yml)
|
|
4
|
+
[](https://codecov.io/gh/Choochmeque/tauri-plugin-iap)
|
|
3
5
|
[](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)
|
|
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**:
|
|
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.
|
|
49
|
+
tauri-plugin-iap = "0.5"
|
|
48
50
|
```
|
|
49
51
|
|
|
50
52
|
Configure the plugin permissions in your `capabilities/default.json`:
|
|
@@ -68,6 +70,39 @@ fn main() {
|
|
|
68
70
|
}
|
|
69
71
|
```
|
|
70
72
|
|
|
73
|
+
## Example App
|
|
74
|
+
|
|
75
|
+
An example application is available in the [`examples/iap-demo`](examples/iap-demo) directory. The example demonstrates all core IAP functionality with a UI:
|
|
76
|
+
|
|
77
|
+
- Initialize IAP connection
|
|
78
|
+
- Fetch and display products with pricing
|
|
79
|
+
- Purchase products and subscriptions
|
|
80
|
+
- Restore previous purchases
|
|
81
|
+
- Check product ownership status
|
|
82
|
+
- Real-time purchase update notifications
|
|
83
|
+
|
|
84
|
+
### Running the Example
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
cd examples/iap-demo
|
|
88
|
+
|
|
89
|
+
# Install dependencies
|
|
90
|
+
pnpm install
|
|
91
|
+
|
|
92
|
+
# Run in development mode
|
|
93
|
+
pnpm tauri dev
|
|
94
|
+
|
|
95
|
+
# Build for production
|
|
96
|
+
pnpm tauri build
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Important:** The app must be properly code-signed to test IAP functionality:
|
|
100
|
+
- **iOS/macOS**: Code signing is required by StoreKit. Configure signing in Xcode or via Tauri's configuration.
|
|
101
|
+
- **Android**: Sign your APK/AAB with a release keystore for production testing.
|
|
102
|
+
- **Windows**: App must be associated with Microsoft Store and signed for Store submission.
|
|
103
|
+
|
|
104
|
+
The example app provides a fully functional demo with inline documentation for each IAP feature, making it easy to understand how to integrate the plugin into your own application.
|
|
105
|
+
|
|
71
106
|
## Usage
|
|
72
107
|
|
|
73
108
|
### JavaScript/TypeScript
|
|
@@ -82,7 +117,7 @@ import {
|
|
|
82
117
|
getProductStatus,
|
|
83
118
|
onPurchaseUpdated,
|
|
84
119
|
PurchaseState
|
|
85
|
-
} from 'tauri-plugin-iap-api';
|
|
120
|
+
} from '@choochmeque/tauri-plugin-iap-api';
|
|
86
121
|
|
|
87
122
|
// Initialize the billing client
|
|
88
123
|
await initialize();
|
|
@@ -126,12 +161,12 @@ const restored = await restorePurchases('subs');
|
|
|
126
161
|
await acknowledgePurchase(purchaseResult.purchaseToken);
|
|
127
162
|
|
|
128
163
|
// Listen for purchase updates
|
|
129
|
-
const
|
|
164
|
+
const listener = await onPurchaseUpdated((purchase) => {
|
|
130
165
|
console.log('Purchase updated:', purchase);
|
|
131
166
|
});
|
|
132
167
|
|
|
133
168
|
// Stop listening
|
|
134
|
-
|
|
169
|
+
await listener.unregister();
|
|
135
170
|
```
|
|
136
171
|
|
|
137
172
|
## Platform Setup
|
|
@@ -161,6 +196,37 @@ unlisten();
|
|
|
161
196
|
3. Associate your app with the Microsoft Store
|
|
162
197
|
4. Test with Windows sandbox environment
|
|
163
198
|
|
|
199
|
+
### macOS Setup
|
|
200
|
+
|
|
201
|
+
1. Configure your app in App Store Connect
|
|
202
|
+
2. Create subscription or in-app purchase products with pricing
|
|
203
|
+
3. Configure code signing in `tauri.conf.json`:
|
|
204
|
+
```json
|
|
205
|
+
{
|
|
206
|
+
"bundle": {
|
|
207
|
+
"macOS": {
|
|
208
|
+
"signingIdentity": "Developer ID Application: Your Name (TEAM_ID)",
|
|
209
|
+
"entitlements": "path/to/entitlements.plist"
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
4. Add required entitlements for StoreKit (create `entitlements.plist`):
|
|
215
|
+
```xml
|
|
216
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
217
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
218
|
+
<plist version="1.0">
|
|
219
|
+
<dict>
|
|
220
|
+
<key>com.apple.security.app-sandbox</key>
|
|
221
|
+
<true/>
|
|
222
|
+
<key>com.apple.security.network.client</key>
|
|
223
|
+
<true/>
|
|
224
|
+
</dict>
|
|
225
|
+
</plist>
|
|
226
|
+
```
|
|
227
|
+
5. Test with sandbox accounts or StoreKit Configuration files
|
|
228
|
+
6. **Important**: App must be code-signed to use StoreKit APIs
|
|
229
|
+
|
|
164
230
|
## API Reference
|
|
165
231
|
|
|
166
232
|
### `initialize()`
|
|
@@ -221,9 +287,11 @@ Checks the ownership and subscription status of a specific product.
|
|
|
221
287
|
- `isAcknowledged`: Whether the purchase has been acknowledged
|
|
222
288
|
- `purchaseToken`: Token for the purchase transaction
|
|
223
289
|
|
|
224
|
-
### `onPurchaseUpdated(callback: (purchase: Purchase) => void)
|
|
290
|
+
### `onPurchaseUpdated(callback: (purchase: Purchase) => void): Promise<PluginListener>`
|
|
225
291
|
Listens for purchase state changes.
|
|
226
292
|
|
|
293
|
+
**Returns:** A `PluginListener` object with an `unregister()` method to stop listening.
|
|
294
|
+
|
|
227
295
|
## Differences Between Platforms
|
|
228
296
|
|
|
229
297
|
### iOS (StoreKit 2)
|
|
@@ -244,6 +312,13 @@ Listens for purchase state changes.
|
|
|
244
312
|
- Supports consumables, durables, and subscriptions
|
|
245
313
|
- Uses SKUs for subscription offer variations
|
|
246
314
|
|
|
315
|
+
### macOS (StoreKit 2)
|
|
316
|
+
- Same StoreKit 2 API as iOS
|
|
317
|
+
- Automatic transaction verification
|
|
318
|
+
- No manual acknowledgment needed
|
|
319
|
+
- Requires macOS 13.0+
|
|
320
|
+
- App must be code-signed (StoreKit requires valid signature)
|
|
321
|
+
|
|
247
322
|
## Testing
|
|
248
323
|
|
|
249
324
|
### iOS
|
|
@@ -262,6 +337,12 @@ Listens for purchase state changes.
|
|
|
262
337
|
3. Test with Windows Dev Center test payment methods
|
|
263
338
|
4. Ensure app is associated with Store listing
|
|
264
339
|
|
|
340
|
+
### macOS
|
|
341
|
+
1. Use sandbox test accounts (same as iOS)
|
|
342
|
+
2. Use StoreKit Configuration files for local testing
|
|
343
|
+
3. App must be code-signed to use StoreKit
|
|
344
|
+
4. Clear purchase history in System Settings > App Store > Sandbox Account
|
|
345
|
+
|
|
265
346
|
## License
|
|
266
347
|
|
|
267
348
|
[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
|
|
175
|
+
* @returns Promise resolving to a PluginListener that can be used to stop listening
|
|
177
176
|
* @example
|
|
178
177
|
* ```typescript
|
|
179
|
-
* const
|
|
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
|
-
*
|
|
186
|
+
* await listener.unregister();
|
|
188
187
|
* ```
|
|
189
188
|
*/
|
|
190
|
-
function onPurchaseUpdated(callback) {
|
|
191
|
-
|
|
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;
|
package/dist-js/index.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
268
|
+
* @returns Promise resolving to a PluginListener that can be used to stop listening
|
|
248
269
|
* @example
|
|
249
270
|
* ```typescript
|
|
250
|
-
* const
|
|
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
|
-
*
|
|
279
|
+
* await listener.unregister();
|
|
259
280
|
* ```
|
|
260
281
|
*/
|
|
261
|
-
export declare function onPurchaseUpdated(callback: (purchase: Purchase) => 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
|
|
173
|
+
* @returns Promise resolving to a PluginListener that can be used to stop listening
|
|
175
174
|
* @example
|
|
176
175
|
* ```typescript
|
|
177
|
-
* const
|
|
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
|
-
*
|
|
184
|
+
* await listener.unregister();
|
|
186
185
|
* ```
|
|
187
186
|
*/
|
|
188
|
-
function onPurchaseUpdated(callback) {
|
|
189
|
-
|
|
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.
|
|
3
|
+
"version": "0.5.1",
|
|
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.7.4",
|
|
40
44
|
"rollup": "^4.9.6",
|
|
41
45
|
"tslib": "^2.6.2",
|
|
42
46
|
"typescript": "^5.3.3",
|
|
43
|
-
"
|
|
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
|
|
57
|
+
"format:check": "prettier --check \"./**/*.{cjs,mjs,js,jsx,mts,ts,tsx,html,css,json}\""
|
|
50
58
|
}
|
|
51
59
|
}
|