Migrating to v13
Version 13 of the plugin introduces significant breaking changes compared to v11 and earlier. This guide outlines the key differences and how to update your existing code.
Ionic / Capacitor Users: Note that @ionic-native/in-app-purchase-2
is not compatible with cordova-plugin-purchase
v13+. You should remove the Ionic Native wrapper and use the plugin directly via the CdvPurchase
global namespace (e.g., CdvPurchase.store
). Ensure you wait for the platform to be ready (this.platform.ready()
in Ionic) before accessing CdvPurchase.store
.
1. Global Namespace Change
Before: The plugin exposed its API through the global
window.store
object.After (v13+): The API is now under the
window.CdvPurchase
namespace. The main entry point isCdvPurchase.store
.
Migration:
Option A (Recommended): Replace all instances of
store.
withCdvPurchase.store.
.
x // After CdvPurchase.store.register(...); CdvPurchase.store.when(...); ```
Option B (Quick Fix, Less Recommended): Re-assign the global
store
variable afterdeviceready
. This maintains compatibility with old code but pollutes the global scope and might cause type issues if not declared properly.
2. Initialization and Refreshing
Before:
store.refresh()
was used ambiguously for initialization, refreshing prices/status, and restoring purchases.After (v13+): These actions are split into separate, clearer methods:
Migration:
Replace your initial
store.refresh()
call withstore.initialize([...platforms])
. Ensure you pass the platforms you intend to use.If you used
store.refresh()
later to update prices, replace it withstore.update()
.If you used
store.refresh()
specifically for restoring purchases (often via a "Restore Purchases" button), replace it withstore.restorePurchases()
.
3. Product Registration
Before:
store.register({ id, type, alias })
After (v13+):
store.register({ id, type, platform })
. Theplatform
field (e.g.,CdvPurchase.Platform.APPLE_APPSTORE
) is now mandatory to specify which store the product belongs to. Thealias
field has been removed.
Migration:
Add the correct
platform: CdvPurchase.Platform.XXX
field to each product registration object.Remove the
alias
field if present.
4. Product Information Refactoring
Before: The
Product
object mixed metadata (title, price), local purchase state (approved, finished), and validated data (expiryDate, owned).After (v13+): Information is separated into distinct objects for clarity and accuracy:
Migration:
Access metadata like
title
,description
directly from theProduct
object (store.get('id', platform).title
).Access pricing via
product.pricing
(for simple cases:product.offers[0].pricingPhases[0]
) or iterate throughproduct.offers[].pricingPhases[]
for complex offers/subscriptions.Check local transaction state via
store.findInLocalReceipts(product)?.state
.Check validated ownership and expiry via
store.owned(product)
or by inspectingstore.verifiedPurchases
. Do not rely on local receipts for subscription status or permanent non-consumable unlocks.
5. Event Handling (store.when()
)
store.when()
)Before:
store.when("filter").event(callback)
used filters (product id, type, alias) directly in thewhen
call. Events often related directly to aProduct
object that mixed states.After (v13+):
store.when().event(callback)
. Filters are removed fromwhen()
. Events are more specific to the data they relate to (productUpdated
,receiptUpdated
,approved
(for Transaction),verified
(for VerifiedReceipt),finished
(for Transaction)).
Migration:
Remove filters from
when()
calls (e.g., changestore.when('my_product').approved(...)
tostore.when().approved(...)
).Implement filtering logic inside your callback functions if needed (check
transaction.products[0].id
,receipt.collection[0].id
,product.id
).Adjust callbacks based on the new event signatures (e.g.,
approved
receives aTransaction
,verified
receives aVerifiedReceipt
).
updated
Event: Split intoproductUpdated
(for metadata changes) andreceiptUpdated
(for local purchase data changes). Update your listeners accordingly.error
Event (Product): Removed. Handle purchase errors via the promise returned byoffer.order()
orstore.requestPayment()
. General plugin errors are still caught bystore.error()
.
6. Ordering Products
Before:
store.order("productId")
orstore.order(product)
After (v13+):
offer.order(additionalData?)
. You now order a specificOffer
object obtained from aProduct
.
Migration:
Get the
Product
object:const product = CdvPurchase.store.get('productId', platform);
Get the desired
Offer
:const offer = product?.getOffer();
(orproduct?.getOffer('offerId')
if multiple offers exist).Call order on the offer:
if (offer) offer.order(additionalData);
7. Finishing and Verifying
Before:
product.verify()
andproduct.finish()
were called on the mixedProduct
object.After (v13+): Methods are now on
Transaction
,Receipt
, orVerifiedReceipt
objects.transaction.verify()
: Verifies the specific transaction's receipt data. Typically called in theapproved
handler.transaction.finish()
: Finishes/Acknowledges/Consumes the specific transaction.receipt.verify()
: Verifies all transactions within the local receipt (less common).receipt.finish()
: Finishes all transactions within the local receipt.verifiedReceipt.finish()
: Finishes the transaction(s) associated with the verified receipt. Typically called in theverified
handler.
Migration:
Typically, you'll call
transaction.verify()
in theapproved
handler andreceipt.finish()
(orverifiedReceipt.finish()
) in theverified
handler.
8. Enumerations
Before: Constants were attached directly to the
store
object (e.g.,store.CONSUMABLE
,store.ERR_SETUP
,store.DEBUG
).After (v13+): Constants are organized into proper enums within the
CdvPurchase
namespace:CdvPurchase.ProductType.CONSUMABLE
,CdvPurchase.ProductType.PAID_SUBSCRIPTION
, etc.CdvPurchase.Platform.APPLE_APPSTORE
,CdvPurchase.Platform.GOOGLE_PLAY
, etc.CdvPurchase.ErrorCode.SETUP
,CdvPurchase.ErrorCode.PURCHASE
, etc.CdvPurchase.LogLevel.DEBUG
,CdvPurchase.LogLevel.INFO
, etc.CdvPurchase.TransactionState.APPROVED
,CdvPurchase.TransactionState.FINISHED
, etc.
Migration:
Update references to use the new enum syntax (e.g., change
store.CONSUMABLE
toCdvPurchase.ProductType.CONSUMABLE
).Backward Compatibility: For easier migration, the old constants are currently still attached to the
store
object as properties (e.g.,CdvPurchase.store.CONSUMABLE
), but using the enums (CdvPurchase.ProductType.CONSUMABLE
) is strongly recommended for clarity, type safety, and future-proofing.
9. Braintree Integration
Last updated