@capgo/native-purchases 7.1.30 → 7.1.32
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 +252 -57
- package/android/src/main/java/ee/forgr/nativepurchases/NativePurchasesPlugin.java +4 -5
- package/dist/docs.json +13 -2
- package/dist/esm/definitions.d.ts +6 -0
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Plugin/NativePurchasesPlugin.swift +22 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -81,18 +81,40 @@ For testing in-app purchases on iOS:
|
|
|
81
81
|
Import the plugin in your TypeScript file:
|
|
82
82
|
|
|
83
83
|
```typescript
|
|
84
|
-
import { NativePurchases } from '@capgo/native-purchases';
|
|
84
|
+
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
+
### ⚠️ Important: In-App vs Subscription Purchases
|
|
88
|
+
|
|
89
|
+
There are two types of purchases with different requirements:
|
|
90
|
+
|
|
91
|
+
| Purchase Type | productType | planIdentifier | Use Case |
|
|
92
|
+
|---------------|-------------|----------------|----------|
|
|
93
|
+
| **In-App Purchase** | `PURCHASE_TYPE.INAPP` | ❌ Not needed | One-time purchases (premium features, remove ads, etc.) |
|
|
94
|
+
| **Subscription** | `PURCHASE_TYPE.SUBS` | ✅ **REQUIRED** | Recurring purchases (monthly/yearly subscriptions) |
|
|
95
|
+
|
|
96
|
+
**Key Rules:**
|
|
97
|
+
- ✅ **In-App Products**: Use `productType: PURCHASE_TYPE.INAPP`, no `planIdentifier` needed
|
|
98
|
+
- ✅ **Subscriptions**: Must use `productType: PURCHASE_TYPE.SUBS` AND `planIdentifier: "your-plan-id"`
|
|
99
|
+
- ❌ **Missing planIdentifier** for subscriptions will cause purchase failures
|
|
100
|
+
|
|
87
101
|
### Complete Example: Get Product Info and Purchase
|
|
88
102
|
|
|
89
|
-
Here's a complete example showing how to get product information and make
|
|
103
|
+
Here's a complete example showing how to get product information and make purchases for both in-app products and subscriptions:
|
|
90
104
|
|
|
91
105
|
```typescript
|
|
92
|
-
import { NativePurchases } from '@capgo/native-purchases';
|
|
106
|
+
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
|
|
93
107
|
|
|
94
108
|
class PurchaseManager {
|
|
95
|
-
|
|
109
|
+
// In-app product (one-time purchase)
|
|
110
|
+
private premiumProductId = 'com.yourapp.premium_features';
|
|
111
|
+
|
|
112
|
+
// Subscription products (require planIdentifier)
|
|
113
|
+
private monthlySubId = 'com.yourapp.premium.monthly';
|
|
114
|
+
private monthlyPlanId = 'monthly-plan'; // Base plan ID from store
|
|
115
|
+
|
|
116
|
+
private yearlySubId = 'com.yourapp.premium.yearly';
|
|
117
|
+
private yearlyPlanId = 'yearly-plan'; // Base plan ID from store
|
|
96
118
|
|
|
97
119
|
async initializeStore() {
|
|
98
120
|
try {
|
|
@@ -103,72 +125,130 @@ class PurchaseManager {
|
|
|
103
125
|
}
|
|
104
126
|
|
|
105
127
|
// 2. Get product information (REQUIRED by Apple - no hardcoded prices!)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// 3. Display product with dynamic info from store
|
|
109
|
-
this.displayProduct(product);
|
|
128
|
+
await this.loadProducts();
|
|
110
129
|
|
|
111
130
|
} catch (error) {
|
|
112
131
|
console.error('Store initialization failed:', error);
|
|
113
132
|
}
|
|
114
133
|
}
|
|
115
134
|
|
|
116
|
-
async
|
|
135
|
+
async loadProducts() {
|
|
117
136
|
try {
|
|
118
|
-
|
|
119
|
-
|
|
137
|
+
// Load in-app products
|
|
138
|
+
const { product: premiumProduct } = await NativePurchases.getProduct({
|
|
139
|
+
productIdentifier: this.premiumProductId,
|
|
140
|
+
productType: PURCHASE_TYPE.INAPP
|
|
120
141
|
});
|
|
121
142
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
description: product.description
|
|
143
|
+
// Load subscription products
|
|
144
|
+
const { products: subscriptions } = await NativePurchases.getProducts({
|
|
145
|
+
productIdentifiers: [this.monthlySubId, this.yearlySubId],
|
|
146
|
+
productType: PURCHASE_TYPE.SUBS
|
|
127
147
|
});
|
|
128
148
|
|
|
129
|
-
|
|
149
|
+
console.log('Products loaded:', {
|
|
150
|
+
premium: premiumProduct,
|
|
151
|
+
subscriptions: subscriptions
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Display products with dynamic info from store
|
|
155
|
+
this.displayProducts(premiumProduct, subscriptions);
|
|
156
|
+
|
|
130
157
|
} catch (error) {
|
|
131
|
-
console.error('Failed to
|
|
158
|
+
console.error('Failed to load products:', error);
|
|
132
159
|
throw error;
|
|
133
160
|
}
|
|
134
161
|
}
|
|
135
162
|
|
|
136
|
-
|
|
163
|
+
displayProducts(premiumProduct: any, subscriptions: any[]) {
|
|
137
164
|
// ✅ CORRECT: Use dynamic product info (required by Apple)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
document.getElementById('
|
|
165
|
+
|
|
166
|
+
// Display one-time purchase
|
|
167
|
+
document.getElementById('premium-title')!.textContent = premiumProduct.title;
|
|
168
|
+
document.getElementById('premium-price')!.textContent = premiumProduct.priceString;
|
|
169
|
+
|
|
170
|
+
// Display subscriptions
|
|
171
|
+
subscriptions.forEach(sub => {
|
|
172
|
+
const element = document.getElementById(`sub-${sub.identifier}`);
|
|
173
|
+
if (element) {
|
|
174
|
+
element.textContent = `${sub.title} - ${sub.priceString}`;
|
|
175
|
+
}
|
|
176
|
+
});
|
|
141
177
|
|
|
142
178
|
// ❌ WRONG: Never hardcode prices - Apple will reject your app
|
|
143
|
-
// document.getElementById('
|
|
179
|
+
// document.getElementById('premium-price')!.textContent = '$9.99';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Purchase one-time product (no planIdentifier needed)
|
|
183
|
+
async purchaseInAppProduct() {
|
|
184
|
+
try {
|
|
185
|
+
console.log('Starting in-app purchase...');
|
|
186
|
+
|
|
187
|
+
const result = await NativePurchases.purchaseProduct({
|
|
188
|
+
productIdentifier: this.premiumProductId,
|
|
189
|
+
productType: PURCHASE_TYPE.INAPP,
|
|
190
|
+
quantity: 1
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
console.log('In-app purchase successful!', result.transactionId);
|
|
194
|
+
await this.handleSuccessfulPurchase(result.transactionId, 'premium');
|
|
195
|
+
|
|
196
|
+
} catch (error) {
|
|
197
|
+
console.error('In-app purchase failed:', error);
|
|
198
|
+
this.handlePurchaseError(error);
|
|
199
|
+
}
|
|
144
200
|
}
|
|
145
201
|
|
|
146
|
-
|
|
202
|
+
// Purchase subscription (planIdentifier REQUIRED)
|
|
203
|
+
async purchaseMonthlySubscription() {
|
|
147
204
|
try {
|
|
148
|
-
console.log('Starting purchase...');
|
|
205
|
+
console.log('Starting subscription purchase...');
|
|
149
206
|
|
|
150
207
|
const result = await NativePurchases.purchaseProduct({
|
|
151
|
-
productIdentifier: this.
|
|
208
|
+
productIdentifier: this.monthlySubId,
|
|
209
|
+
planIdentifier: this.monthlyPlanId, // REQUIRED for subscriptions
|
|
210
|
+
productType: PURCHASE_TYPE.SUBS, // REQUIRED for subscriptions
|
|
152
211
|
quantity: 1
|
|
153
212
|
});
|
|
154
213
|
|
|
155
|
-
console.log('
|
|
214
|
+
console.log('Subscription purchase successful!', result.transactionId);
|
|
215
|
+
await this.handleSuccessfulPurchase(result.transactionId, 'monthly');
|
|
156
216
|
|
|
157
|
-
|
|
158
|
-
|
|
217
|
+
} catch (error) {
|
|
218
|
+
console.error('Subscription purchase failed:', error);
|
|
219
|
+
this.handlePurchaseError(error);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Purchase yearly subscription (planIdentifier REQUIRED)
|
|
224
|
+
async purchaseYearlySubscription() {
|
|
225
|
+
try {
|
|
226
|
+
console.log('Starting yearly subscription purchase...');
|
|
227
|
+
|
|
228
|
+
const result = await NativePurchases.purchaseProduct({
|
|
229
|
+
productIdentifier: this.yearlySubId,
|
|
230
|
+
planIdentifier: this.yearlyPlanId, // REQUIRED for subscriptions
|
|
231
|
+
productType: PURCHASE_TYPE.SUBS, // REQUIRED for subscriptions
|
|
232
|
+
quantity: 1
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
console.log('Yearly subscription successful!', result.transactionId);
|
|
236
|
+
await this.handleSuccessfulPurchase(result.transactionId, 'yearly');
|
|
159
237
|
|
|
160
238
|
} catch (error) {
|
|
161
|
-
console.error('
|
|
239
|
+
console.error('Yearly subscription failed:', error);
|
|
162
240
|
this.handlePurchaseError(error);
|
|
163
241
|
}
|
|
164
242
|
}
|
|
165
243
|
|
|
166
|
-
async handleSuccessfulPurchase(transactionId: string) {
|
|
244
|
+
async handleSuccessfulPurchase(transactionId: string, purchaseType: string) {
|
|
167
245
|
// 1. Grant access to premium features
|
|
168
246
|
localStorage.setItem('premium_active', 'true');
|
|
247
|
+
localStorage.setItem('purchase_type', purchaseType);
|
|
169
248
|
|
|
170
249
|
// 2. Update UI
|
|
171
|
-
|
|
250
|
+
const statusText = purchaseType === 'premium' ? 'Premium Unlocked' : `${purchaseType} Subscription Active`;
|
|
251
|
+
document.getElementById('subscription-status')!.textContent = statusText;
|
|
172
252
|
|
|
173
253
|
// 3. Optional: Verify purchase on your server
|
|
174
254
|
await this.verifyPurchaseOnServer(transactionId);
|
|
@@ -223,8 +303,16 @@ const purchaseManager = new PurchaseManager();
|
|
|
223
303
|
purchaseManager.initializeStore();
|
|
224
304
|
|
|
225
305
|
// Attach to UI buttons
|
|
226
|
-
document.getElementById('buy-button')?.addEventListener('click', () => {
|
|
227
|
-
purchaseManager.
|
|
306
|
+
document.getElementById('buy-premium-button')?.addEventListener('click', () => {
|
|
307
|
+
purchaseManager.purchaseInAppProduct();
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
document.getElementById('buy-monthly-button')?.addEventListener('click', () => {
|
|
311
|
+
purchaseManager.purchaseMonthlySubscription();
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
document.getElementById('buy-yearly-button')?.addEventListener('click', () => {
|
|
315
|
+
purchaseManager.purchaseYearlySubscription();
|
|
228
316
|
});
|
|
229
317
|
|
|
230
318
|
document.getElementById('restore-button')?.addEventListener('click', () => {
|
|
@@ -237,15 +325,39 @@ document.getElementById('restore-button')?.addEventListener('click', () => {
|
|
|
237
325
|
#### Get Multiple Products
|
|
238
326
|
|
|
239
327
|
```typescript
|
|
240
|
-
|
|
241
|
-
|
|
328
|
+
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
|
|
329
|
+
|
|
330
|
+
// Get in-app products (one-time purchases)
|
|
331
|
+
const getInAppProducts = async () => {
|
|
332
|
+
try {
|
|
333
|
+
const { products } = await NativePurchases.getProducts({
|
|
334
|
+
productIdentifiers: [
|
|
335
|
+
'com.yourapp.premium_features',
|
|
336
|
+
'com.yourapp.remove_ads',
|
|
337
|
+
'com.yourapp.extra_content'
|
|
338
|
+
],
|
|
339
|
+
productType: PURCHASE_TYPE.INAPP
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
products.forEach(product => {
|
|
343
|
+
console.log(`${product.title}: ${product.priceString}`);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
return products;
|
|
347
|
+
} catch (error) {
|
|
348
|
+
console.error('Error getting in-app products:', error);
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// Get subscription products
|
|
353
|
+
const getSubscriptions = async () => {
|
|
242
354
|
try {
|
|
243
355
|
const { products } = await NativePurchases.getProducts({
|
|
244
356
|
productIdentifiers: [
|
|
245
357
|
'com.yourapp.premium.monthly',
|
|
246
|
-
'com.yourapp.premium.yearly'
|
|
247
|
-
|
|
248
|
-
|
|
358
|
+
'com.yourapp.premium.yearly'
|
|
359
|
+
],
|
|
360
|
+
productType: PURCHASE_TYPE.SUBS
|
|
249
361
|
});
|
|
250
362
|
|
|
251
363
|
products.forEach(product => {
|
|
@@ -254,7 +366,7 @@ const getProducts = async () => {
|
|
|
254
366
|
|
|
255
367
|
return products;
|
|
256
368
|
} catch (error) {
|
|
257
|
-
console.error('Error getting
|
|
369
|
+
console.error('Error getting subscriptions:', error);
|
|
258
370
|
}
|
|
259
371
|
};
|
|
260
372
|
```
|
|
@@ -262,8 +374,10 @@ const getProducts = async () => {
|
|
|
262
374
|
#### Simple Purchase Flow
|
|
263
375
|
|
|
264
376
|
```typescript
|
|
265
|
-
|
|
266
|
-
|
|
377
|
+
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
|
|
378
|
+
|
|
379
|
+
// Simple one-time purchase (in-app product)
|
|
380
|
+
const buyInAppProduct = async () => {
|
|
267
381
|
try {
|
|
268
382
|
// Check billing support
|
|
269
383
|
const { isBillingSupported } = await NativePurchases.isBillingSupported();
|
|
@@ -274,25 +388,76 @@ const buyPremium = async () => {
|
|
|
274
388
|
|
|
275
389
|
// Get product (for price display)
|
|
276
390
|
const { product } = await NativePurchases.getProduct({
|
|
277
|
-
productIdentifier: 'com.yourapp.
|
|
391
|
+
productIdentifier: 'com.yourapp.premium_features',
|
|
392
|
+
productType: PURCHASE_TYPE.INAPP
|
|
278
393
|
});
|
|
279
394
|
|
|
280
395
|
// Confirm with user (showing real price from store)
|
|
281
396
|
const confirmed = confirm(`Purchase ${product.title} for ${product.priceString}?`);
|
|
282
397
|
if (!confirmed) return;
|
|
283
398
|
|
|
284
|
-
// Make purchase
|
|
399
|
+
// Make purchase (no planIdentifier needed for in-app)
|
|
285
400
|
const result = await NativePurchases.purchaseProduct({
|
|
286
|
-
productIdentifier: 'com.yourapp.
|
|
287
|
-
|
|
401
|
+
productIdentifier: 'com.yourapp.premium_features',
|
|
402
|
+
productType: PURCHASE_TYPE.INAPP,
|
|
403
|
+
quantity: 1,
|
|
404
|
+
appAccountToken: userUUID // Optional: iOS only - for linking purchases to user accounts
|
|
288
405
|
});
|
|
289
406
|
|
|
290
407
|
alert('Purchase successful! Transaction ID: ' + result.transactionId);
|
|
291
408
|
|
|
409
|
+
// iOS will also return receipt data for validation
|
|
410
|
+
if (result.receipt) {
|
|
411
|
+
// Send to your backend for validation
|
|
412
|
+
await validateReceipt(result.receipt);
|
|
413
|
+
}
|
|
414
|
+
|
|
292
415
|
} catch (error) {
|
|
293
416
|
alert('Purchase failed: ' + error.message);
|
|
294
417
|
}
|
|
295
418
|
};
|
|
419
|
+
|
|
420
|
+
// Simple subscription purchase (requires planIdentifier)
|
|
421
|
+
const buySubscription = async () => {
|
|
422
|
+
try {
|
|
423
|
+
// Check billing support
|
|
424
|
+
const { isBillingSupported } = await NativePurchases.isBillingSupported();
|
|
425
|
+
if (!isBillingSupported) {
|
|
426
|
+
alert('Purchases not supported on this device');
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Get subscription product (for price display)
|
|
431
|
+
const { product } = await NativePurchases.getProduct({
|
|
432
|
+
productIdentifier: 'com.yourapp.premium.monthly',
|
|
433
|
+
productType: PURCHASE_TYPE.SUBS
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
// Confirm with user (showing real price from store)
|
|
437
|
+
const confirmed = confirm(`Subscribe to ${product.title} for ${product.priceString}?`);
|
|
438
|
+
if (!confirmed) return;
|
|
439
|
+
|
|
440
|
+
// Make subscription purchase (planIdentifier REQUIRED for Android)
|
|
441
|
+
const result = await NativePurchases.purchaseProduct({
|
|
442
|
+
productIdentifier: 'com.yourapp.premium.monthly',
|
|
443
|
+
planIdentifier: 'monthly-plan', // REQUIRED for Android subscriptions
|
|
444
|
+
productType: PURCHASE_TYPE.SUBS, // REQUIRED for subscriptions
|
|
445
|
+
quantity: 1,
|
|
446
|
+
appAccountToken: userUUID // Optional: iOS only - for linking purchases to user accounts
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
alert('Subscription successful! Transaction ID: ' + result.transactionId);
|
|
450
|
+
|
|
451
|
+
// iOS will also return receipt data for validation
|
|
452
|
+
if (result.receipt) {
|
|
453
|
+
// Send to your backend for validation
|
|
454
|
+
await validateReceipt(result.receipt);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
} catch (error) {
|
|
458
|
+
alert('Subscription failed: ' + error.message);
|
|
459
|
+
}
|
|
460
|
+
};
|
|
296
461
|
```
|
|
297
462
|
|
|
298
463
|
### Check if billing is supported
|
|
@@ -366,13 +531,14 @@ import axios from 'axios'; // Make sure to install axios: npm install axios
|
|
|
366
531
|
class Store {
|
|
367
532
|
// ... (previous code remains the same)
|
|
368
533
|
|
|
534
|
+
// Purchase in-app product
|
|
369
535
|
async purchaseProduct(productId: string) {
|
|
370
536
|
try {
|
|
371
537
|
const transaction = await NativePurchases.purchaseProduct({
|
|
372
538
|
productIdentifier: productId,
|
|
373
539
|
productType: PURCHASE_TYPE.INAPP
|
|
374
540
|
});
|
|
375
|
-
console.log('
|
|
541
|
+
console.log('In-app purchase successful:', transaction);
|
|
376
542
|
|
|
377
543
|
// Immediately grant access to the purchased content
|
|
378
544
|
await this.grantAccess(productId);
|
|
@@ -387,6 +553,29 @@ class Store {
|
|
|
387
553
|
}
|
|
388
554
|
}
|
|
389
555
|
|
|
556
|
+
// Purchase subscription (requires planIdentifier)
|
|
557
|
+
async purchaseSubscription(productId: string, planId: string) {
|
|
558
|
+
try {
|
|
559
|
+
const transaction = await NativePurchases.purchaseProduct({
|
|
560
|
+
productIdentifier: productId,
|
|
561
|
+
planIdentifier: planId, // REQUIRED for subscriptions
|
|
562
|
+
productType: PURCHASE_TYPE.SUBS // REQUIRED for subscriptions
|
|
563
|
+
});
|
|
564
|
+
console.log('Subscription purchase successful:', transaction);
|
|
565
|
+
|
|
566
|
+
// Immediately grant access to the subscription content
|
|
567
|
+
await this.grantAccess(productId);
|
|
568
|
+
|
|
569
|
+
// Initiate server-side validation asynchronously
|
|
570
|
+
this.validatePurchaseOnServer(transaction).catch(console.error);
|
|
571
|
+
|
|
572
|
+
return transaction;
|
|
573
|
+
} catch (error) {
|
|
574
|
+
console.error('Subscription purchase failed:', error);
|
|
575
|
+
throw error;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
390
579
|
private async grantAccess(productId: string) {
|
|
391
580
|
// Implement logic to grant immediate access to the purchased content
|
|
392
581
|
console.log(`Granting access to ${productId}`);
|
|
@@ -411,13 +600,18 @@ class Store {
|
|
|
411
600
|
}
|
|
412
601
|
}
|
|
413
602
|
|
|
414
|
-
// Usage
|
|
603
|
+
// Usage examples
|
|
415
604
|
const store = new Store();
|
|
416
605
|
await store.initialize();
|
|
417
606
|
|
|
418
607
|
try {
|
|
419
|
-
|
|
420
|
-
|
|
608
|
+
// Purchase in-app product (one-time purchase)
|
|
609
|
+
await store.purchaseProduct('premium_features');
|
|
610
|
+
console.log('In-app purchase completed successfully');
|
|
611
|
+
|
|
612
|
+
// Purchase subscription (requires planIdentifier)
|
|
613
|
+
await store.purchaseSubscription('premium_monthly', 'monthly-plan');
|
|
614
|
+
console.log('Subscription completed successfully');
|
|
421
615
|
} catch (error) {
|
|
422
616
|
console.error('Purchase failed:', error);
|
|
423
617
|
}
|
|
@@ -554,14 +748,14 @@ Restores a user's previous and links their appUserIDs to any user's also using
|
|
|
554
748
|
### purchaseProduct(...)
|
|
555
749
|
|
|
556
750
|
```typescript
|
|
557
|
-
purchaseProduct(options: { productIdentifier: string; planIdentifier?: string; productType?: PURCHASE_TYPE; quantity?: number; }) => Promise<Transaction>
|
|
751
|
+
purchaseProduct(options: { productIdentifier: string; planIdentifier?: string; productType?: PURCHASE_TYPE; quantity?: number; appAccountToken?: string; }) => Promise<Transaction>
|
|
558
752
|
```
|
|
559
753
|
|
|
560
754
|
Started purchase process for the given product.
|
|
561
755
|
|
|
562
|
-
| Param | Type
|
|
563
|
-
| ------------- |
|
|
564
|
-
| **`options`** | <code>{ productIdentifier: string; planIdentifier?: string; productType?: <a href="#purchase_type">PURCHASE_TYPE</a>; quantity?: number; }</code> | - The product to purchase |
|
|
756
|
+
| Param | Type | Description |
|
|
757
|
+
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
|
|
758
|
+
| **`options`** | <code>{ productIdentifier: string; planIdentifier?: string; productType?: <a href="#purchase_type">PURCHASE_TYPE</a>; quantity?: number; appAccountToken?: string; }</code> | - The product to purchase |
|
|
565
759
|
|
|
566
760
|
**Returns:** <code>Promise<<a href="#transaction">Transaction</a>></code>
|
|
567
761
|
|
|
@@ -633,9 +827,10 @@ Get the native Capacitor plugin version
|
|
|
633
827
|
|
|
634
828
|
#### Transaction
|
|
635
829
|
|
|
636
|
-
| Prop | Type | Description
|
|
637
|
-
| ------------------- | ------------------- |
|
|
638
|
-
| **`transactionId`** | <code>string</code> | RevenueCat Id associated to the transaction.
|
|
830
|
+
| Prop | Type | Description |
|
|
831
|
+
| ------------------- | ------------------- | --------------------------------------------------------------- |
|
|
832
|
+
| **`transactionId`** | <code>string</code> | RevenueCat Id associated to the transaction. |
|
|
833
|
+
| **`receipt`** | <code>string</code> | Receipt data for validation (iOS only - base64 encoded receipt) |
|
|
639
834
|
|
|
640
835
|
|
|
641
836
|
#### Product
|
|
@@ -375,15 +375,14 @@ public class NativePurchasesPlugin extends Plugin {
|
|
|
375
375
|
return;
|
|
376
376
|
}
|
|
377
377
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
Log.d(TAG, "Using product ID for query: " + productId);
|
|
378
|
+
// For subscriptions, always use the productIdentifier (subscription ID) to query
|
|
379
|
+
// The planIdentifier is used later when setting the offer token
|
|
380
|
+
Log.d(TAG, "Using product ID for query: " + productIdentifier);
|
|
382
381
|
|
|
383
382
|
ImmutableList<QueryProductDetailsParams.Product> productList =
|
|
384
383
|
ImmutableList.of(
|
|
385
384
|
QueryProductDetailsParams.Product.newBuilder()
|
|
386
|
-
.setProductId(
|
|
385
|
+
.setProductId(productIdentifier)
|
|
387
386
|
.setProductType(
|
|
388
387
|
productType.equals("inapp")
|
|
389
388
|
? BillingClient.ProductType.INAPP
|
package/dist/docs.json
CHANGED
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"name": "purchaseProduct",
|
|
20
|
-
"signature": "(options: { productIdentifier: string; planIdentifier?: string; productType?: PURCHASE_TYPE; quantity?: number; }) => Promise<Transaction>",
|
|
20
|
+
"signature": "(options: { productIdentifier: string; planIdentifier?: string; productType?: PURCHASE_TYPE; quantity?: number; appAccountToken?: string; }) => Promise<Transaction>",
|
|
21
21
|
"parameters": [
|
|
22
22
|
{
|
|
23
23
|
"name": "options",
|
|
24
24
|
"docs": "- The product to purchase",
|
|
25
|
-
"type": "{ productIdentifier: string; planIdentifier?: string | undefined; productType?: PURCHASE_TYPE | undefined; quantity?: number | undefined; }"
|
|
25
|
+
"type": "{ productIdentifier: string; planIdentifier?: string | undefined; productType?: PURCHASE_TYPE | undefined; quantity?: number | undefined; appAccountToken?: string | undefined; }"
|
|
26
26
|
}
|
|
27
27
|
],
|
|
28
28
|
"returns": "Promise<Transaction>",
|
|
@@ -46,6 +46,10 @@
|
|
|
46
46
|
{
|
|
47
47
|
"name": "param",
|
|
48
48
|
"text": "options.quantity - Only iOS, the number of items you wish to purchase. Will use 1 by default."
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "param",
|
|
52
|
+
"text": "options.appAccountToken - Only iOS, UUID for the user's account. Used to link purchases to the user account for App Store Server Notifications."
|
|
49
53
|
}
|
|
50
54
|
],
|
|
51
55
|
"docs": "Started purchase process for the given product.",
|
|
@@ -173,6 +177,13 @@
|
|
|
173
177
|
"docs": "RevenueCat Id associated to the transaction.",
|
|
174
178
|
"complexTypes": [],
|
|
175
179
|
"type": "string"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"name": "receipt",
|
|
183
|
+
"tags": [],
|
|
184
|
+
"docs": "Receipt data for validation (iOS only - base64 encoded receipt)",
|
|
185
|
+
"complexTypes": [],
|
|
186
|
+
"type": "string | undefined"
|
|
176
187
|
}
|
|
177
188
|
]
|
|
178
189
|
},
|
|
@@ -124,6 +124,10 @@ export interface Transaction {
|
|
|
124
124
|
* RevenueCat Id associated to the transaction.
|
|
125
125
|
*/
|
|
126
126
|
readonly transactionId: string;
|
|
127
|
+
/**
|
|
128
|
+
* Receipt data for validation (iOS only - base64 encoded receipt)
|
|
129
|
+
*/
|
|
130
|
+
readonly receipt?: string;
|
|
127
131
|
}
|
|
128
132
|
export interface SubscriptionPeriod {
|
|
129
133
|
/**
|
|
@@ -236,12 +240,14 @@ export interface NativePurchasesPlugin {
|
|
|
236
240
|
* @param options.productType - Only Android, the type of product, can be inapp or subs. Will use inapp by default.
|
|
237
241
|
* @param options.planIdentifier - Only Android, the identifier of the plan you want to purchase, require for for subs.
|
|
238
242
|
* @param options.quantity - Only iOS, the number of items you wish to purchase. Will use 1 by default.
|
|
243
|
+
* @param options.appAccountToken - Only iOS, UUID for the user's account. Used to link purchases to the user account for App Store Server Notifications.
|
|
239
244
|
*/
|
|
240
245
|
purchaseProduct(options: {
|
|
241
246
|
productIdentifier: string;
|
|
242
247
|
planIdentifier?: string;
|
|
243
248
|
productType?: PURCHASE_TYPE;
|
|
244
249
|
quantity?: number;
|
|
250
|
+
appAccountToken?: string;
|
|
245
251
|
}): Promise<Transaction>;
|
|
246
252
|
/**
|
|
247
253
|
* Gets the product info associated with a list of product identifiers.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,mBAOX;AAPD,WAAY,mBAAmB;IAC7B,qFAAoB,CAAA;IACpB,iEAAU,CAAA;IACV,uEAAa,CAAA;IACb,iEAAU,CAAA;IACV,iEAAU,CAAA;IACV,qEAAY,CAAA;AACd,CAAC,EAPW,mBAAmB,KAAnB,mBAAmB,QAO9B;AAED,MAAM,CAAN,IAAY,aAUX;AAVD,WAAY,aAAa;IACvB;;OAEG;IACH,gCAAe,CAAA;IAEf;;OAEG;IACH,8BAAa,CAAA;AACf,CAAC,EAVW,aAAa,KAAb,aAAa,QAUxB;AAED;;;;GAIG;AACH,MAAM,CAAN,IAAY,eAyBX;AAzBD,WAAY,eAAe;IACzB;;OAEG;IACH,uEAAa,CAAA;IAEb;;OAEG;IACH,qFAAoB,CAAA;IAEpB;;OAEG;IACH,iFAAkB,CAAA;IAElB;;OAEG;IACH,mFAAmB,CAAA;IAEnB;;OAEG;IACH,+FAAyB,CAAA;AAC3B,CAAC,EAzBW,eAAe,KAAf,eAAe,QAyB1B;AACD,MAAM,CAAN,IAAY,cA2BX;AA3BD,WAAY,cAAc;IACxB,qIAAiD,CAAA;IAEjD;;;OAGG;IACH,qGAAiC,CAAA;IAEjC;;;;OAIG;IACH,iHAAuC,CAAA;IAEvC;;;OAGG;IACH,iGAA+B,CAAA;IAE/B;;;OAGG;IACH,2DAAY,CAAA;AACd,CAAC,EA3BW,cAAc,KAAd,cAAc,QA2BzB;AAED,MAAM,CAAN,IAAY,YA6CX;AA7CD,WAAY,YAAY;IACtB;;OAEG;IACH,mCAAmB,CAAA;IAEnB;;OAEG;IACH,iCAAiB,CAAA;IAEjB;;OAEG;IACH,qCAAqB,CAAA;IAErB;;OAEG;IACH,iCAAiB,CAAA;IAEjB;;OAEG;IACH,uCAAuB,CAAA;IAEvB;;OAEG;IACH,2CAA2B,CAAA;IAE3B;;OAEG;IACH,uCAAuB,CAAA;IAEvB;;OAEG;IACH,mCAAmB,CAAA;IAEnB;;OAEG;IACH,iCAAiB,CAAA;AACnB,CAAC,EA7CW,YAAY,KAAZ,YAAY,QA6CvB;AAED,MAAM,CAAN,IAAY,wBAaX;AAbD,WAAY,wBAAwB;IAClC;;OAEG;IACH,+HAAoC,CAAA;IACpC;;OAEG;IACH,qIAAmC,CAAA;IACnC;;OAEG;IACH,iIAAiC,CAAA;AACnC,CAAC,EAbW,wBAAwB,KAAxB,wBAAwB,QAanC","sourcesContent":["export enum ATTRIBUTION_NETWORK {\n APPLE_SEARCH_ADS = 0,\n ADJUST = 1,\n APPSFLYER = 2,\n BRANCH = 3,\n TENJIN = 4,\n FACEBOOK = 5,\n}\n\nexport enum PURCHASE_TYPE {\n /**\n * A type of SKU for in-app products.\n */\n INAPP = \"inapp\",\n\n /**\n * A type of SKU for subscriptions.\n */\n SUBS = \"subs\",\n}\n\n/**\n * Enum for billing features.\n * Currently, these are only relevant for Google Play Android users:\n * https://developer.android.com/reference/com/android/billingclient/api/BillingClient.FeatureType\n */\nexport enum BILLING_FEATURE {\n /**\n * Purchase/query for subscriptions.\n */\n SUBSCRIPTIONS,\n\n /**\n * Subscriptions update/replace.\n */\n SUBSCRIPTIONS_UPDATE,\n\n /**\n * Purchase/query for in-app items on VR.\n */\n IN_APP_ITEMS_ON_VR,\n\n /**\n * Purchase/query for subscriptions on VR.\n */\n SUBSCRIPTIONS_ON_VR,\n\n /**\n * Launch a price change confirmation flow.\n */\n PRICE_CHANGE_CONFIRMATION,\n}\nexport enum PRORATION_MODE {\n UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY = 0,\n\n /**\n * Replacement takes effect immediately, and the remaining time will be\n * prorated and credited to the user. This is the current default behavior.\n */\n IMMEDIATE_WITH_TIME_PRORATION = 1,\n\n /**\n * Replacement takes effect immediately, and the billing cycle remains the\n * same. The price for the remaining period will be charged. This option is\n * only available for subscription upgrade.\n */\n IMMEDIATE_AND_CHARGE_PRORATED_PRICE = 2,\n\n /**\n * Replacement takes effect immediately, and the new price will be charged on\n * next recurrence time. The billing cycle stays the same.\n */\n IMMEDIATE_WITHOUT_PRORATION = 3,\n\n /**\n * Replacement takes effect when the old plan expires, and the new price will\n * be charged at the same time.\n */\n DEFERRED = 4,\n}\n\nexport enum PACKAGE_TYPE {\n /**\n * A package that was defined with a custom identifier.\n */\n UNKNOWN = \"UNKNOWN\",\n\n /**\n * A package that was defined with a custom identifier.\n */\n CUSTOM = \"CUSTOM\",\n\n /**\n * A package configured with the predefined lifetime identifier.\n */\n LIFETIME = \"LIFETIME\",\n\n /**\n * A package configured with the predefined annual identifier.\n */\n ANNUAL = \"ANNUAL\",\n\n /**\n * A package configured with the predefined six month identifier.\n */\n SIX_MONTH = \"SIX_MONTH\",\n\n /**\n * A package configured with the predefined three month identifier.\n */\n THREE_MONTH = \"THREE_MONTH\",\n\n /**\n * A package configured with the predefined two month identifier.\n */\n TWO_MONTH = \"TWO_MONTH\",\n\n /**\n * A package configured with the predefined monthly identifier.\n */\n MONTHLY = \"MONTHLY\",\n\n /**\n * A package configured with the predefined weekly identifier.\n */\n WEEKLY = \"WEEKLY\",\n}\n\nexport enum INTRO_ELIGIBILITY_STATUS {\n /**\n * RevenueCat doesn't have enough information to determine eligibility.\n */\n INTRO_ELIGIBILITY_STATUS_UNKNOWN = 0,\n /**\n * The user is not eligible for a free trial or intro pricing for this product.\n */\n INTRO_ELIGIBILITY_STATUS_INELIGIBLE,\n /**\n * The user is eligible for a free trial or intro pricing for this product.\n */\n INTRO_ELIGIBILITY_STATUS_ELIGIBLE,\n}\n\nexport interface Transaction {\n /**\n * RevenueCat Id associated to the transaction.\n */\n readonly transactionId: string;\n /**\n * Product Id associated with the transaction.\n */\n // readonly productIdentifier: string;\n /**\n * Purchase date of the transaction in ISO 8601 format.\n */\n // readonly purchaseDate: string;\n}\n\nexport interface SubscriptionPeriod {\n /**\n * The Subscription Period number of unit.\n */\n readonly numberOfUnits: number;\n /**\n * The Subscription Period unit.\n */\n readonly unit: number;\n}\nexport interface SKProductDiscount {\n /**\n * The Product discount identifier.\n */\n readonly identifier: string;\n /**\n * The Product discount type.\n */\n readonly type: number;\n /**\n * The Product discount price.\n */\n readonly price: number;\n /**\n * Formatted price of the item, including its currency sign, such as €3.99.\n */\n readonly priceString: string;\n /**\n * The Product discount currency symbol.\n */\n readonly currencySymbol: string;\n /**\n * The Product discount currency code.\n */\n readonly currencyCode: string;\n /**\n * The Product discount paymentMode.\n */\n readonly paymentMode: number;\n /**\n * The Product discount number Of Periods.\n */\n readonly numberOfPeriods: number;\n /**\n * The Product discount subscription period.\n */\n readonly subscriptionPeriod: SubscriptionPeriod;\n}\nexport interface Product {\n /**\n * Product Id.\n */\n readonly identifier: string;\n /**\n * Description of the product.\n */\n readonly description: string;\n /**\n * Title of the product.\n */\n readonly title: string;\n /**\n * Price of the product in the local currency.\n */\n readonly price: number;\n /**\n * Formatted price of the item, including its currency sign, such as €3.99.\n */\n readonly priceString: string;\n /**\n * Currency code for price and original price.\n */\n readonly currencyCode: string;\n /**\n * Currency symbol for price and original price.\n */\n readonly currencySymbol: string;\n /**\n * Boolean indicating if the product is sharable with family\n */\n readonly isFamilyShareable: boolean;\n /**\n * Group identifier for the product.\n */\n readonly subscriptionGroupIdentifier: string;\n /**\n * The Product subcription group identifier.\n */\n readonly subscriptionPeriod: SubscriptionPeriod;\n /**\n * The Product introductory Price.\n */\n readonly introductoryPrice: SKProductDiscount | null;\n /**\n * The Product discounts list.\n */\n readonly discounts: SKProductDiscount[];\n}\n\nexport interface NativePurchasesPlugin {\n /**\n * Restores a user's previous and links their appUserIDs to any user's also using those .\n */\n restorePurchases(): Promise<void>;\n\n /**\n * Started purchase process for the given product.\n *\n * @param options - The product to purchase\n * @param options.productIdentifier - The product identifier of the product you want to purchase.\n * @param options.productType - Only Android, the type of product, can be inapp or subs. Will use inapp by default.\n * @param options.planIdentifier - Only Android, the identifier of the plan you want to purchase, require for for subs.\n * @param options.quantity - Only iOS, the number of items you wish to purchase. Will use 1 by default.\n */\n purchaseProduct(options: {\n productIdentifier: string;\n planIdentifier?: string;\n productType?: PURCHASE_TYPE;\n quantity?: number;\n }): Promise<Transaction>;\n\n /**\n * Gets the product info associated with a list of product identifiers.\n *\n * @param options - The product identifiers you wish to retrieve information for\n * @param options.productIdentifiers - Array of product identifiers\n * @param options.productType - Only Android, the type of product, can be inapp or subs. Will use inapp by default.\n * @returns - The requested product info\n */\n getProducts(options: {\n productIdentifiers: string[];\n productType?: PURCHASE_TYPE;\n }): Promise<{ products: Product[] }>;\n\n /**\n * Gets the product info for a single product identifier.\n *\n * @param options - The product identifier you wish to retrieve information for\n * @param options.productIdentifier - The product identifier\n * @param options.productType - Only Android, the type of product, can be inapp or subs. Will use inapp by default.\n * @returns - The requested product info\n */\n getProduct(options: {\n productIdentifier: string;\n productType?: PURCHASE_TYPE;\n }): Promise<{ product: Product }>;\n\n /**\n * Check if billing is supported for the current device.\n *\n *\n */\n isBillingSupported(): Promise<{ isBillingSupported: boolean }>;\n /**\n * Get the native Capacitor plugin version\n *\n * @returns {Promise<{ id: string }>} an Promise with version for this device\n * @throws An error if the something went wrong\n */\n getPluginVersion(): Promise<{ version: string }>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,mBAOX;AAPD,WAAY,mBAAmB;IAC7B,qFAAoB,CAAA;IACpB,iEAAU,CAAA;IACV,uEAAa,CAAA;IACb,iEAAU,CAAA;IACV,iEAAU,CAAA;IACV,qEAAY,CAAA;AACd,CAAC,EAPW,mBAAmB,KAAnB,mBAAmB,QAO9B;AAED,MAAM,CAAN,IAAY,aAUX;AAVD,WAAY,aAAa;IACvB;;OAEG;IACH,gCAAe,CAAA;IAEf;;OAEG;IACH,8BAAa,CAAA;AACf,CAAC,EAVW,aAAa,KAAb,aAAa,QAUxB;AAED;;;;GAIG;AACH,MAAM,CAAN,IAAY,eAyBX;AAzBD,WAAY,eAAe;IACzB;;OAEG;IACH,uEAAa,CAAA;IAEb;;OAEG;IACH,qFAAoB,CAAA;IAEpB;;OAEG;IACH,iFAAkB,CAAA;IAElB;;OAEG;IACH,mFAAmB,CAAA;IAEnB;;OAEG;IACH,+FAAyB,CAAA;AAC3B,CAAC,EAzBW,eAAe,KAAf,eAAe,QAyB1B;AACD,MAAM,CAAN,IAAY,cA2BX;AA3BD,WAAY,cAAc;IACxB,qIAAiD,CAAA;IAEjD;;;OAGG;IACH,qGAAiC,CAAA;IAEjC;;;;OAIG;IACH,iHAAuC,CAAA;IAEvC;;;OAGG;IACH,iGAA+B,CAAA;IAE/B;;;OAGG;IACH,2DAAY,CAAA;AACd,CAAC,EA3BW,cAAc,KAAd,cAAc,QA2BzB;AAED,MAAM,CAAN,IAAY,YA6CX;AA7CD,WAAY,YAAY;IACtB;;OAEG;IACH,mCAAmB,CAAA;IAEnB;;OAEG;IACH,iCAAiB,CAAA;IAEjB;;OAEG;IACH,qCAAqB,CAAA;IAErB;;OAEG;IACH,iCAAiB,CAAA;IAEjB;;OAEG;IACH,uCAAuB,CAAA;IAEvB;;OAEG;IACH,2CAA2B,CAAA;IAE3B;;OAEG;IACH,uCAAuB,CAAA;IAEvB;;OAEG;IACH,mCAAmB,CAAA;IAEnB;;OAEG;IACH,iCAAiB,CAAA;AACnB,CAAC,EA7CW,YAAY,KAAZ,YAAY,QA6CvB;AAED,MAAM,CAAN,IAAY,wBAaX;AAbD,WAAY,wBAAwB;IAClC;;OAEG;IACH,+HAAoC,CAAA;IACpC;;OAEG;IACH,qIAAmC,CAAA;IACnC;;OAEG;IACH,iIAAiC,CAAA;AACnC,CAAC,EAbW,wBAAwB,KAAxB,wBAAwB,QAanC","sourcesContent":["export enum ATTRIBUTION_NETWORK {\n APPLE_SEARCH_ADS = 0,\n ADJUST = 1,\n APPSFLYER = 2,\n BRANCH = 3,\n TENJIN = 4,\n FACEBOOK = 5,\n}\n\nexport enum PURCHASE_TYPE {\n /**\n * A type of SKU for in-app products.\n */\n INAPP = \"inapp\",\n\n /**\n * A type of SKU for subscriptions.\n */\n SUBS = \"subs\",\n}\n\n/**\n * Enum for billing features.\n * Currently, these are only relevant for Google Play Android users:\n * https://developer.android.com/reference/com/android/billingclient/api/BillingClient.FeatureType\n */\nexport enum BILLING_FEATURE {\n /**\n * Purchase/query for subscriptions.\n */\n SUBSCRIPTIONS,\n\n /**\n * Subscriptions update/replace.\n */\n SUBSCRIPTIONS_UPDATE,\n\n /**\n * Purchase/query for in-app items on VR.\n */\n IN_APP_ITEMS_ON_VR,\n\n /**\n * Purchase/query for subscriptions on VR.\n */\n SUBSCRIPTIONS_ON_VR,\n\n /**\n * Launch a price change confirmation flow.\n */\n PRICE_CHANGE_CONFIRMATION,\n}\nexport enum PRORATION_MODE {\n UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY = 0,\n\n /**\n * Replacement takes effect immediately, and the remaining time will be\n * prorated and credited to the user. This is the current default behavior.\n */\n IMMEDIATE_WITH_TIME_PRORATION = 1,\n\n /**\n * Replacement takes effect immediately, and the billing cycle remains the\n * same. The price for the remaining period will be charged. This option is\n * only available for subscription upgrade.\n */\n IMMEDIATE_AND_CHARGE_PRORATED_PRICE = 2,\n\n /**\n * Replacement takes effect immediately, and the new price will be charged on\n * next recurrence time. The billing cycle stays the same.\n */\n IMMEDIATE_WITHOUT_PRORATION = 3,\n\n /**\n * Replacement takes effect when the old plan expires, and the new price will\n * be charged at the same time.\n */\n DEFERRED = 4,\n}\n\nexport enum PACKAGE_TYPE {\n /**\n * A package that was defined with a custom identifier.\n */\n UNKNOWN = \"UNKNOWN\",\n\n /**\n * A package that was defined with a custom identifier.\n */\n CUSTOM = \"CUSTOM\",\n\n /**\n * A package configured with the predefined lifetime identifier.\n */\n LIFETIME = \"LIFETIME\",\n\n /**\n * A package configured with the predefined annual identifier.\n */\n ANNUAL = \"ANNUAL\",\n\n /**\n * A package configured with the predefined six month identifier.\n */\n SIX_MONTH = \"SIX_MONTH\",\n\n /**\n * A package configured with the predefined three month identifier.\n */\n THREE_MONTH = \"THREE_MONTH\",\n\n /**\n * A package configured with the predefined two month identifier.\n */\n TWO_MONTH = \"TWO_MONTH\",\n\n /**\n * A package configured with the predefined monthly identifier.\n */\n MONTHLY = \"MONTHLY\",\n\n /**\n * A package configured with the predefined weekly identifier.\n */\n WEEKLY = \"WEEKLY\",\n}\n\nexport enum INTRO_ELIGIBILITY_STATUS {\n /**\n * RevenueCat doesn't have enough information to determine eligibility.\n */\n INTRO_ELIGIBILITY_STATUS_UNKNOWN = 0,\n /**\n * The user is not eligible for a free trial or intro pricing for this product.\n */\n INTRO_ELIGIBILITY_STATUS_INELIGIBLE,\n /**\n * The user is eligible for a free trial or intro pricing for this product.\n */\n INTRO_ELIGIBILITY_STATUS_ELIGIBLE,\n}\n\nexport interface Transaction {\n /**\n * RevenueCat Id associated to the transaction.\n */\n readonly transactionId: string;\n /**\n * Receipt data for validation (iOS only - base64 encoded receipt)\n */\n readonly receipt?: string;\n /**\n * Product Id associated with the transaction.\n */\n // readonly productIdentifier: string;\n /**\n * Purchase date of the transaction in ISO 8601 format.\n */\n // readonly purchaseDate: string;\n}\n\nexport interface SubscriptionPeriod {\n /**\n * The Subscription Period number of unit.\n */\n readonly numberOfUnits: number;\n /**\n * The Subscription Period unit.\n */\n readonly unit: number;\n}\nexport interface SKProductDiscount {\n /**\n * The Product discount identifier.\n */\n readonly identifier: string;\n /**\n * The Product discount type.\n */\n readonly type: number;\n /**\n * The Product discount price.\n */\n readonly price: number;\n /**\n * Formatted price of the item, including its currency sign, such as €3.99.\n */\n readonly priceString: string;\n /**\n * The Product discount currency symbol.\n */\n readonly currencySymbol: string;\n /**\n * The Product discount currency code.\n */\n readonly currencyCode: string;\n /**\n * The Product discount paymentMode.\n */\n readonly paymentMode: number;\n /**\n * The Product discount number Of Periods.\n */\n readonly numberOfPeriods: number;\n /**\n * The Product discount subscription period.\n */\n readonly subscriptionPeriod: SubscriptionPeriod;\n}\nexport interface Product {\n /**\n * Product Id.\n */\n readonly identifier: string;\n /**\n * Description of the product.\n */\n readonly description: string;\n /**\n * Title of the product.\n */\n readonly title: string;\n /**\n * Price of the product in the local currency.\n */\n readonly price: number;\n /**\n * Formatted price of the item, including its currency sign, such as €3.99.\n */\n readonly priceString: string;\n /**\n * Currency code for price and original price.\n */\n readonly currencyCode: string;\n /**\n * Currency symbol for price and original price.\n */\n readonly currencySymbol: string;\n /**\n * Boolean indicating if the product is sharable with family\n */\n readonly isFamilyShareable: boolean;\n /**\n * Group identifier for the product.\n */\n readonly subscriptionGroupIdentifier: string;\n /**\n * The Product subcription group identifier.\n */\n readonly subscriptionPeriod: SubscriptionPeriod;\n /**\n * The Product introductory Price.\n */\n readonly introductoryPrice: SKProductDiscount | null;\n /**\n * The Product discounts list.\n */\n readonly discounts: SKProductDiscount[];\n}\n\nexport interface NativePurchasesPlugin {\n /**\n * Restores a user's previous and links their appUserIDs to any user's also using those .\n */\n restorePurchases(): Promise<void>;\n\n /**\n * Started purchase process for the given product.\n *\n * @param options - The product to purchase\n * @param options.productIdentifier - The product identifier of the product you want to purchase.\n * @param options.productType - Only Android, the type of product, can be inapp or subs. Will use inapp by default.\n * @param options.planIdentifier - Only Android, the identifier of the plan you want to purchase, require for for subs.\n * @param options.quantity - Only iOS, the number of items you wish to purchase. Will use 1 by default.\n * @param options.appAccountToken - Only iOS, UUID for the user's account. Used to link purchases to the user account for App Store Server Notifications.\n */\n purchaseProduct(options: {\n productIdentifier: string;\n planIdentifier?: string;\n productType?: PURCHASE_TYPE;\n quantity?: number;\n appAccountToken?: string;\n }): Promise<Transaction>;\n\n /**\n * Gets the product info associated with a list of product identifiers.\n *\n * @param options - The product identifiers you wish to retrieve information for\n * @param options.productIdentifiers - Array of product identifiers\n * @param options.productType - Only Android, the type of product, can be inapp or subs. Will use inapp by default.\n * @returns - The requested product info\n */\n getProducts(options: {\n productIdentifiers: string[];\n productType?: PURCHASE_TYPE;\n }): Promise<{ products: Product[] }>;\n\n /**\n * Gets the product info for a single product identifier.\n *\n * @param options - The product identifier you wish to retrieve information for\n * @param options.productIdentifier - The product identifier\n * @param options.productType - Only Android, the type of product, can be inapp or subs. Will use inapp by default.\n * @returns - The requested product info\n */\n getProduct(options: {\n productIdentifier: string;\n productType?: PURCHASE_TYPE;\n }): Promise<{ product: Product }>;\n\n /**\n * Check if billing is supported for the current device.\n *\n *\n */\n isBillingSupported(): Promise<{ isBillingSupported: boolean }>;\n /**\n * Get the native Capacitor plugin version\n *\n * @returns {Promise<{ id: string }>} an Promise with version for this device\n * @throws An error if the something went wrong\n */\n getPluginVersion(): Promise<{ version: string }>;\n}\n"]}
|
|
@@ -42,6 +42,8 @@ public class NativePurchasesPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
42
42
|
print("purchaseProduct")
|
|
43
43
|
let productIdentifier = call.getString("productIdentifier", "")
|
|
44
44
|
let quantity = call.getInt("quantity", 1)
|
|
45
|
+
let appAccountToken = call.getString("appAccountToken")
|
|
46
|
+
|
|
45
47
|
if productIdentifier.isEmpty {
|
|
46
48
|
call.reject("productIdentifier is Empty, give an id")
|
|
47
49
|
return
|
|
@@ -56,13 +58,31 @@ public class NativePurchasesPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
56
58
|
}
|
|
57
59
|
var purchaseOptions = Set<Product.PurchaseOption>()
|
|
58
60
|
purchaseOptions.insert(Product.PurchaseOption.quantity(quantity))
|
|
61
|
+
|
|
62
|
+
// Add appAccountToken if provided
|
|
63
|
+
if let accountToken = appAccountToken, !accountToken.isEmpty {
|
|
64
|
+
if let tokenData = UUID(uuidString: accountToken) {
|
|
65
|
+
purchaseOptions.insert(Product.PurchaseOption.appAccountToken(tokenData))
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
59
69
|
let result = try await product.purchase(options: purchaseOptions)
|
|
60
70
|
print("purchaseProduct result \(result)")
|
|
61
71
|
switch result {
|
|
62
72
|
case let .success(.verified(transaction)):
|
|
63
|
-
// Successful
|
|
73
|
+
// Successful purchase
|
|
74
|
+
var response: [String: Any] = ["transactionId": transaction.id]
|
|
75
|
+
|
|
76
|
+
// Get receipt data
|
|
77
|
+
if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,
|
|
78
|
+
FileManager.default.fileExists(atPath: appStoreReceiptURL.path),
|
|
79
|
+
let receiptData = try? Data(contentsOf: appStoreReceiptURL) {
|
|
80
|
+
let receiptBase64 = receiptData.base64EncodedString()
|
|
81
|
+
response["receipt"] = receiptBase64
|
|
82
|
+
}
|
|
83
|
+
|
|
64
84
|
await transaction.finish()
|
|
65
|
-
call.resolve(
|
|
85
|
+
call.resolve(response)
|
|
66
86
|
case let .success(.unverified(_, error)):
|
|
67
87
|
// Successful purchase but transaction/receipt can't be verified
|
|
68
88
|
// Could be a jailbroken phone
|