@choochmeque/tauri-plugin-iap-api 0.1.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 ADDED
@@ -0,0 +1,168 @@
1
+ # Tauri Plugin IAP
2
+
3
+ A Tauri plugin for In-App Purchases (IAP) with support for subscriptions on both iOS (StoreKit 2) and Android (Google Play Billing).
4
+
5
+ ## Features
6
+
7
+ - Initialize billing/store connection
8
+ - Query products and subscriptions with detailed pricing
9
+ - Purchase subscriptions with platform-specific features
10
+ - Restore previous purchases
11
+ - Get purchase history
12
+ - Real-time purchase state updates via events
13
+ - Automatic transaction verification (iOS)
14
+ - Support for introductory offers and free trials
15
+
16
+ ## Platform Support
17
+
18
+ - **iOS**: StoreKit 2 (requires iOS 15.0+)
19
+ - **Android**: Google Play Billing Library v8.0.0
20
+
21
+ ## Installation
22
+
23
+ Add the plugin to your Tauri project:
24
+
25
+ ```toml
26
+ [dependencies]
27
+ tauri-plugin-iap = { path = "../path-to-plugin" }
28
+ ```
29
+
30
+ Register the plugin in your Tauri app:
31
+
32
+ ```rust
33
+ fn main() {
34
+ tauri::Builder::default()
35
+ .plugin(tauri_plugin_iap::init())
36
+ .run(tauri::generate_context!())
37
+ .expect("error while running tauri application");
38
+ }
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ ### JavaScript/TypeScript
44
+
45
+ ```typescript
46
+ import {
47
+ initialize,
48
+ getProducts,
49
+ purchase,
50
+ restorePurchases,
51
+ acknowledgePurchase,
52
+ onPurchaseUpdated
53
+ } from 'tauri-plugin-iap-api';
54
+
55
+ // Initialize the billing client
56
+ await initialize();
57
+
58
+ // Get available products
59
+ const products = await getProducts(['subscription_id_1', 'subscription_id_2'], 'subs');
60
+
61
+ // Purchase a subscription
62
+ // On Android: use the offer token from subscriptionOfferDetails
63
+ // On iOS: offer token is not used
64
+ const purchaseResult = await purchase('subscription_id_1', offerToken);
65
+
66
+ // Restore purchases
67
+ const restored = await restorePurchases();
68
+
69
+ // Acknowledge a purchase (Android only, iOS auto-acknowledges)
70
+ await acknowledgePurchase(purchaseResult.purchaseToken);
71
+
72
+ // Listen for purchase updates
73
+ const unlisten = onPurchaseUpdated((purchase) => {
74
+ console.log('Purchase updated:', purchase);
75
+ });
76
+
77
+ // Stop listening
78
+ unlisten();
79
+ ```
80
+
81
+ ## Platform Setup
82
+
83
+ ### iOS Setup
84
+
85
+ 1. Configure your app in App Store Connect
86
+ 2. Create subscription products with appropriate pricing
87
+ 3. Add In-App Purchase capability to your app in Xcode:
88
+ - Open your project in Xcode
89
+ - Select your target
90
+ - Go to "Signing & Capabilities"
91
+ - Click "+" and add "In-App Purchase"
92
+ 4. Test with sandbox accounts
93
+
94
+ ### Android Setup
95
+
96
+ 1. Add your app to Google Play Console
97
+ 2. Create subscription products in Google Play Console
98
+ 3. Configure your app's billing permissions (already included in the plugin)
99
+ 4. Test with test accounts or sandbox environment
100
+
101
+ ## API Reference
102
+
103
+ ### `initialize()`
104
+ Initializes the billing client connection (required on Android, no-op on iOS).
105
+
106
+ ### `getProducts(productIds: string[], productType: 'subs' | 'inapp')`
107
+ Fetches product details from the store.
108
+
109
+ **Returns:**
110
+ - `products`: Array of product objects with:
111
+ - `productId`: Product identifier
112
+ - `title`: Display name
113
+ - `description`: Product description
114
+ - `productType`: Type of product
115
+ - `formattedPrice`: Localized price string
116
+ - `subscriptionOfferDetails`: (subscriptions only) Array of offers
117
+
118
+ ### `purchase(productId: string, offerToken?: string)`
119
+ Initiates a purchase flow.
120
+
121
+ **Parameters:**
122
+ - `productId`: The product to purchase
123
+ - `offerToken`: (Android only) The offer token for subscriptions
124
+
125
+ **Returns:** Purchase object with transaction details
126
+
127
+ ### `restorePurchases()`
128
+ Queries and returns all active purchases.
129
+
130
+ ### `getPurchaseHistory()`
131
+ Returns the complete purchase history.
132
+
133
+ ### `acknowledgePurchase(purchaseToken: string)`
134
+ Acknowledges a purchase (required on Android within 3 days, no-op on iOS).
135
+
136
+ ### `onPurchaseUpdated(callback: (purchase: Purchase) => void)`
137
+ Listens for purchase state changes.
138
+
139
+ ## Differences Between Platforms
140
+
141
+ ### iOS (StoreKit 2)
142
+ - Automatic transaction verification
143
+ - No manual acknowledgment needed
144
+ - Supports introductory offers and promotional offers
145
+ - Transaction updates are automatically observed
146
+ - Requires iOS 15.0+
147
+
148
+ ### Android (Google Play Billing)
149
+ - Manual acknowledgment required within 3 days
150
+ - Supports multiple subscription offers per product
151
+ - Offer tokens required for subscription purchases
152
+ - More detailed pricing phase information
153
+
154
+ ## Testing
155
+
156
+ ### iOS
157
+ 1. Use sandbox test accounts
158
+ 2. Test on physical devices (subscriptions don't work well on simulators)
159
+ 3. Clear purchase history in Settings > App Store > Sandbox Account
160
+
161
+ ### Android
162
+ 1. Upload your app to internal testing track
163
+ 2. Add test accounts in Google Play Console
164
+ 3. Test with test payment methods
165
+
166
+ ## License
167
+
168
+ MIT or Apache-2.0
@@ -0,0 +1,54 @@
1
+ 'use strict';
2
+
3
+ var core = require('@tauri-apps/api/core');
4
+ var event = require('@tauri-apps/api/event');
5
+
6
+ async function initialize() {
7
+ return await core.invoke('plugin:iap|initialize');
8
+ }
9
+ async function getProducts(productIds, productType = 'subs') {
10
+ return await core.invoke('plugin:iap|get_products', {
11
+ payload: {
12
+ productIds,
13
+ productType,
14
+ },
15
+ });
16
+ }
17
+ async function purchase(productId, offerToken) {
18
+ return await core.invoke('plugin:iap|purchase', {
19
+ payload: {
20
+ productId,
21
+ offerToken,
22
+ },
23
+ });
24
+ }
25
+ async function restorePurchases() {
26
+ return await core.invoke('plugin:iap|restore_purchases');
27
+ }
28
+ async function getPurchaseHistory() {
29
+ return await core.invoke('plugin:iap|get_purchase_history');
30
+ }
31
+ async function acknowledgePurchase(purchaseToken) {
32
+ return await core.invoke('plugin:iap|acknowledge_purchase', {
33
+ payload: {
34
+ purchaseToken,
35
+ },
36
+ });
37
+ }
38
+ // Event listener for purchase updates
39
+ function onPurchaseUpdated(callback) {
40
+ const unlisten = event.listen('purchaseUpdated', (event) => {
41
+ callback(event.payload);
42
+ });
43
+ return () => {
44
+ unlisten.then((fn) => fn());
45
+ };
46
+ }
47
+
48
+ exports.acknowledgePurchase = acknowledgePurchase;
49
+ exports.getProducts = getProducts;
50
+ exports.getPurchaseHistory = getPurchaseHistory;
51
+ exports.initialize = initialize;
52
+ exports.onPurchaseUpdated = onPurchaseUpdated;
53
+ exports.purchase = purchase;
54
+ exports.restorePurchases = restorePurchases;
@@ -0,0 +1,66 @@
1
+ export interface InitializeResponse {
2
+ success: boolean;
3
+ }
4
+ export interface PricingPhase {
5
+ formattedPrice: string;
6
+ priceCurrencyCode: string;
7
+ priceAmountMicros: number;
8
+ billingPeriod: string;
9
+ billingCycleCount: number;
10
+ recurrenceMode: number;
11
+ }
12
+ export interface SubscriptionOffer {
13
+ offerToken: string;
14
+ basePlanId: string;
15
+ offerId?: string;
16
+ pricingPhases: PricingPhase[];
17
+ }
18
+ export interface Product {
19
+ productId: string;
20
+ title: string;
21
+ description: string;
22
+ productType: string;
23
+ formattedPrice?: string;
24
+ priceCurrencyCode?: string;
25
+ priceAmountMicros?: number;
26
+ subscriptionOfferDetails?: SubscriptionOffer[];
27
+ }
28
+ export interface GetProductsResponse {
29
+ products: Product[];
30
+ }
31
+ export interface Purchase {
32
+ orderId?: string;
33
+ packageName: string;
34
+ productId: string;
35
+ purchaseTime: number;
36
+ purchaseToken: string;
37
+ purchaseState: number;
38
+ isAutoRenewing: boolean;
39
+ isAcknowledged: boolean;
40
+ originalJson: string;
41
+ signature: string;
42
+ }
43
+ export interface RestorePurchasesResponse {
44
+ purchases: Purchase[];
45
+ }
46
+ export interface PurchaseHistoryRecord {
47
+ productId: string;
48
+ purchaseTime: number;
49
+ purchaseToken: string;
50
+ quantity: number;
51
+ originalJson: string;
52
+ signature: string;
53
+ }
54
+ export interface GetPurchaseHistoryResponse {
55
+ history: PurchaseHistoryRecord[];
56
+ }
57
+ export interface AcknowledgePurchaseResponse {
58
+ success: boolean;
59
+ }
60
+ export declare function initialize(): Promise<InitializeResponse>;
61
+ export declare function getProducts(productIds: string[], productType?: 'subs' | 'inapp'): Promise<GetProductsResponse>;
62
+ export declare function purchase(productId: string, offerToken?: string): Promise<Purchase>;
63
+ export declare function restorePurchases(): Promise<RestorePurchasesResponse>;
64
+ export declare function getPurchaseHistory(): Promise<GetPurchaseHistoryResponse>;
65
+ export declare function acknowledgePurchase(purchaseToken: string): Promise<AcknowledgePurchaseResponse>;
66
+ export declare function onPurchaseUpdated(callback: (purchase: Purchase) => void): () => void;
@@ -0,0 +1,46 @@
1
+ import { invoke } from '@tauri-apps/api/core';
2
+ import { listen } from '@tauri-apps/api/event';
3
+
4
+ async function initialize() {
5
+ return await invoke('plugin:iap|initialize');
6
+ }
7
+ async function getProducts(productIds, productType = 'subs') {
8
+ return await invoke('plugin:iap|get_products', {
9
+ payload: {
10
+ productIds,
11
+ productType,
12
+ },
13
+ });
14
+ }
15
+ async function purchase(productId, offerToken) {
16
+ return await invoke('plugin:iap|purchase', {
17
+ payload: {
18
+ productId,
19
+ offerToken,
20
+ },
21
+ });
22
+ }
23
+ async function restorePurchases() {
24
+ return await invoke('plugin:iap|restore_purchases');
25
+ }
26
+ async function getPurchaseHistory() {
27
+ return await invoke('plugin:iap|get_purchase_history');
28
+ }
29
+ async function acknowledgePurchase(purchaseToken) {
30
+ return await invoke('plugin:iap|acknowledge_purchase', {
31
+ payload: {
32
+ purchaseToken,
33
+ },
34
+ });
35
+ }
36
+ // Event listener for purchase updates
37
+ function onPurchaseUpdated(callback) {
38
+ const unlisten = listen('purchaseUpdated', (event) => {
39
+ callback(event.payload);
40
+ });
41
+ return () => {
42
+ unlisten.then((fn) => fn());
43
+ };
44
+ }
45
+
46
+ export { acknowledgePurchase, getProducts, getPurchaseHistory, initialize, onPurchaseUpdated, purchase, restorePurchases };
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@choochmeque/tauri-plugin-iap-api",
3
+ "version": "0.1.0",
4
+ "author": "You",
5
+ "description": "Tauri plugin for in-app purchases",
6
+ "type": "module",
7
+ "types": "./dist-js/index.d.ts",
8
+ "main": "./dist-js/index.cjs",
9
+ "module": "./dist-js/index.js",
10
+ "exports": {
11
+ "types": "./dist-js/index.d.ts",
12
+ "import": "./dist-js/index.js",
13
+ "require": "./dist-js/index.cjs"
14
+ },
15
+ "files": [
16
+ "dist-js",
17
+ "README.md"
18
+ ],
19
+ "directories": {
20
+ "example": "examples"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/Choochmeque/tauri-plugin-iap.git"
25
+ },
26
+ "keywords": [
27
+ "tauri",
28
+ "iap"
29
+ ],
30
+ "license": "MIT",
31
+ "bugs": {
32
+ "url": "https://github.com/Choochmeque/tauri-plugin-iap/issues"
33
+ },
34
+ "homepage": "https://github.com/Choochmeque/tauri-plugin-iap#readme",
35
+ "scripts": {
36
+ "build": "rollup -c",
37
+ "prepublishOnly": "pnpm build",
38
+ "pretest": "pnpm build"
39
+ },
40
+ "dependencies": {
41
+ "@tauri-apps/api": ">=2.0.0-beta.6"
42
+ },
43
+ "devDependencies": {
44
+ "@rollup/plugin-typescript": "^12.1.4",
45
+ "rollup": "^4.9.6",
46
+ "tslib": "^2.6.2",
47
+ "typescript": "^5.3.3"
48
+ },
49
+ "packageManager": "pnpm@10.9.0+sha512.0486e394640d3c1fb3c9d43d49cf92879ff74f8516959c235308f5a8f62e2e19528a65cdc2a3058f587cde71eba3d5b56327c8c33a97e4c4051ca48a10ca2d5f"
50
+ }