cordova-plugin-purchase
Advanced tools
Comparing version 13.12.0 to 13.12.1
{ | ||
"name": "cordova-plugin-purchase", | ||
"version": "13.12.0", | ||
"version": "13.12.1", | ||
"description": "Cordova Purchase plugin for iOS, Android, Windows (AppStore, Play, UWP)", | ||
@@ -64,3 +64,3 @@ "cordova": { | ||
"/RELEASE_NOTES.md", | ||
"/src/android", | ||
"/src/android/cc/fovea", | ||
"/src/ios", | ||
@@ -67,0 +67,0 @@ "/src/ts", |
@@ -141,2 +141,98 @@ # Cordova Purchase Plugin | ||
### Receipt Validation | ||
The plugin supports two modes of operation for managing product ownership: | ||
1. **With Receipt Validation (Recommended)** | ||
- Products ownership status is determined by validating receipts with a server | ||
- `product.owned` reflects the validated ownership status | ||
- Works reliably across all environments (TestFlight, AppStore) | ||
- Can be setup using [Iaptic's receipt validation service](https://www.iaptic.com) | ||
2. **Without Receipt Validation** | ||
- Relies only on local device data | ||
- `product.owned` will initially be false | ||
- Use `store.owned(productId)` to check ownership status | ||
- Limited functionality in test environments | ||
For proper subscription support, receipt validation is strongly recommended. You can: | ||
- Implement your own validation server | ||
- Use [Iaptic's receipt validation service](https://www.iaptic.com) | ||
See our [receipt validation guide](https://purchase.cordova.fovea.cc/v/v13.0/advanced/receipt-validation) for more details. | ||
### Subscription Example | ||
Here's a complete example showing how to implement subscriptions with the plugin: | ||
```typescript | ||
class SubscriptionService { | ||
constructor(store: CdvPurchase.Store) { | ||
// Setup receipt validation (recommended) | ||
store.validator = "https://validator.iaptic.com/v1/validate?appName=demo&apiKey=12345678"; | ||
// Register products | ||
store.register([{ | ||
id: 'subscription1', | ||
platform: CdvPurchase.Platform.APPLE_APPSTORE, | ||
type: CdvPurchase.ProductType.PAID_SUBSCRIPTION, | ||
}]); | ||
// Setup event handlers | ||
store.when() | ||
.productUpdated(() => { | ||
console.log('Products loaded from the store:', store.products); | ||
updateProductsUI(); // | ||
}) | ||
.approved(transaction => { | ||
console.log('Purchase approved:', transaction); | ||
transaction.verify(); | ||
}) | ||
.verified(receipt => { | ||
console.log('Purchase verified:', receipt); | ||
receipt.finish(); | ||
updateActiveSubscriptionUI(); | ||
}); | ||
// Initialize the store | ||
store.initialize([{ | ||
platform: CdvPurchase.Platform.APPLE_APPSTORE, | ||
options: { | ||
needAppReceipt: true, | ||
} | ||
}]); | ||
} | ||
/** Purchase a subscription */ | ||
subscribe(productId: string) { | ||
const product = store.get(productId); | ||
if (!product) { | ||
console.log('Product not found'); | ||
return; | ||
} | ||
product.getOffer()?.order() | ||
.then(error => { | ||
if (error) { | ||
if (error.code === CdvPurchase.ErrorCode.PAYMENT_CANCELLED) { | ||
console.log('Payment cancelled by user'); | ||
} | ||
else { | ||
console.log('Failed to subscribe:', error); | ||
} | ||
} | ||
}); | ||
} | ||
/** Check if user has an active subscription */ | ||
hasActiveSubscription(): boolean { | ||
return store.owned('subscription1'); | ||
} | ||
} | ||
``` | ||
For a more complete example with a backend integration, check: | ||
- Client: https://github.com/j3k0/cordova-subscription-example | ||
- Server: https://github.com/iaptic/iaptic-example-nodejs-backend | ||
## Extra Resources | ||
@@ -143,0 +239,0 @@ |
@@ -5,2 +5,17 @@ # Release Notes - Cordova Plugin Purchase | ||
### 13.12.1 | ||
#### (googleplay) Fix #1434 - validProducts.find is not a product | ||
Somet user had this occasional error: | ||
Error: TypeError: validProducts.find is not a function | ||
at http://localhost/:9909:64 | ||
at Array.map (<anonymous>) | ||
at iabLoaded (http://localhost/:9908:46) | ||
at Object.callbackFromNative (http://localhost/:1708:52) | ||
at <anonymous>:1:9 | ||
This happened when another call to the native side was made before the list of products was returned (race condition kind of bug). It's fixed in that release. | ||
### 13.12.0 | ||
@@ -7,0 +22,0 @@ |
@@ -204,2 +204,12 @@ /// <reference path="../../receipt.ts" /> | ||
this.log.debug("Loaded: " + JSON.stringify(validProducts)); | ||
// Add type check to handle invalid responses | ||
if (!Array.isArray(validProducts)) { | ||
const message = `Invalid product list received: ${JSON.stringify(validProducts)}, retrying later...`; | ||
this.log.warn(message); | ||
this.retry.retry(go); | ||
this.context.error(playStoreError(ErrorCode.LOAD, message, null)); | ||
return; | ||
} | ||
const ret = products.map(registeredProduct => { | ||
@@ -206,0 +216,0 @@ const validProduct = validProducts.find(vp => vp.productId === registeredProduct.id); |
@@ -77,2 +77,7 @@ namespace CdvPurchase { | ||
* Returns true if the product is owned. | ||
* | ||
* Important: This value will be false when the app starts and will only become | ||
* true after purchase receipts have been loaded and validated. Without receipt validation, | ||
* it might remain false depending on the platform, make sure to store the ownership status | ||
* of non-consumable products in some way. | ||
*/ | ||
@@ -79,0 +84,0 @@ get owned(): boolean { |
@@ -35,3 +35,3 @@ /// <reference path="types.ts" /> | ||
*/ | ||
export const PLUGIN_VERSION = '13.12.0'; | ||
export const PLUGIN_VERSION = '13.12.1'; | ||
@@ -353,9 +353,26 @@ /** | ||
/** | ||
* Setup events listener. | ||
* Register event callbacks. | ||
* | ||
* Events overview: | ||
* - `productUpdated`: Called when product metadata is loaded from the store | ||
* - `receiptUpdated`: Called when local receipt information changes (ownership status change, for example) | ||
* - `verified`: Called after successful receipt validation (requires a receipt validator) | ||
* | ||
* @example | ||
* // Monitor ownership with receipt validation | ||
* store.when() | ||
* .productUpdated(product => updateUI(product)) | ||
* .approved(transaction => transaction.verify()) | ||
* .verified(receipt => receipt.finish()); | ||
* .verified(receipt => { | ||
* if (store.owned("my-product")) { | ||
* // Product is owned and verified | ||
* } | ||
* }); | ||
* | ||
* @example | ||
* // Monitor ownership without receipt validation | ||
* store.when().receiptUpdated(receipt => { | ||
* if (store.owned("my-product")) { | ||
* // Product is owned according to local data | ||
* } | ||
* }); | ||
*/ | ||
@@ -495,2 +512,7 @@ when() { | ||
* | ||
* Important: The value will be false when the app starts and will only become | ||
* true after purchase receipts have been loaded and validated. Without receipt validation, | ||
* it might remain false depending on the platform, make sure to store the ownership status | ||
* of non-consumable products in some way. | ||
* | ||
* @param product - The product object or identifier of the product. | ||
@@ -497,0 +519,0 @@ */ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
25494
304
1377587
95