Iaptic is integrated with the cordova purchase plugin. Setting it up is as simple as adding a few lines in your code at plugin initialization.
Plugin version >= 13
The latest version of the plugin contains a Iaptic helper class.
Use the Receipt Validator
Instantiate an "iaptic" object and set the store validator to iaptic's.
const iaptic = new CdvPurchase.Iaptic({
appName: "[APP_NAME]",
apiKey: "[PUBLIC_KEY]",
});
CdvPurchase.store.validator = iaptic.validator;
_Note: Replace [PUBLIC_KEY]
; and [APP_NAME]
with values from your account.
Then make sure to perform receipt validation for approved transactions:
CdvPurchase.store.when()
.approved(transaction => transaction.verify())
.verified(receipt => receipt.finish())
.unverified(receipt => console.log('receipt cannot be verified'));
Use to Determine Apple App Store Discount Eligibility
With App Store, a user is only eligible to certain discount offers if they are or were a subscriber to one of the products of the same subscription group. A user is only eligible to introductory offers if they have never been a subscriber.
You can use iaptic to perform eligibility detection for you. When initializing the APPLE_APPSTORE
platform, set the discountEligibilityDeterminer
to iaptic's:
CdvPurchase.store.initialize([
{
platform: CdvPurchase.Platform.APPLE_APPSTORE,
options: {
needAppReceipt: true,
discountEligibilityDeterminer: iaptic.appStoreDiscountEligibilityDeterminer,
}
},
// other platforms...
]);
Security Policy
Depending on how your app is configured, security policy may prevent it from making requests to the validation service (DOM Exception 18 / Error 0). To prevent this, make sure validator.iaptic.com
is listed in your HTML file’s meta "Content-Security-Policy", in the "connect-src"
list.
Here is an example of what that looks like:
<meta http-equiv="Content-Security-Policy"
content="default-src 'self' 'unsafe-eval'; connect-src https://validator.iaptic.com; style-src 'self' 'unsafe-inline'; media-src *; img-src * 'self' data: blob:">
Best Practices
It's recommended to initialize the plugin when your app starts. This ensures:
- All active and pending purchases are processed immediately
- Purchases initiated from the store or using promo codes are captured
- Users don't need to navigate to a specific page to complete pending transactions
Here's the recommended initialization flow:
// Initialize as early as possible in your app
function initPurchasePlugin() {
// Register your products
CdvPurchase.store.register(products);
// Setup your handlers
CdvPurchase.store.when()
.approved(p => p.verify()) // Validate receipt with iaptic
.verified(p => p.finish()) // Acknowledge to the store
.unverified(p => console.error('Receipt validation failed', p));
// Initialize the platforms
CdvPurchase.store.initialize([
CdvPurchase.Platform.APPLE_APPSTORE,
CdvPurchase.Platform.GOOGLE_PLAY
]);
}
// Alternative: Wait for user session if needed
SessionService.addEventListener("ready", initPurchasePlugin);
Purchase Processing Flow
sequenceDiagram
participant App
participant Plugin/SDK
participant Store
participant Iaptic
participant Server
rect rgb(255, 255, 255)
Note over App: Plugin Initialization
App->>Plugin/SDK: Initialize()
Plugin/SDK->>Store: Check purchases
Store-->>Plugin/SDK: Active/pending purchases
Plugin/SDK-->>App: Notify purchases
App->>Iaptic: verify()
Iaptic->>Store: Validate receipt
Store-->>Iaptic: Receipt status
Iaptic-->>App: Validation result
App->>Plugin/SDK: finish()
Plugin/SDK->>Store: Acknowledge
Iaptic->>Server: Webhook (RECEIPT_VALIDATED)
Note over Server: Update user's purchases
end
rect rgb(255, 255, 255)
Note over App: New Purchase Flow
App->>Plugin/SDK: order()
Plugin/SDK->>Store: Process purchase
Store-->>Plugin/SDK: Success
Plugin/SDK-->>App: Notify approved
App->>Iaptic: verify()
Iaptic->>Store: Validate receipt
Store-->>Iaptic: Receipt status
Iaptic-->>App: Validation result
App->>Plugin/SDK: finish()
Plugin/SDK->>Store: Acknowledge
Iaptic->>Server: Webhook (RECEIPT_VALIDATED)
Note over Server: Update user's purchases
end
Important: Unacknowledged transactions may be refunded after 3 days on some platforms.
Error Handling and Logging
To help troubleshoot issues:
- Implement error logging for:
- Plugin errors
- Receipt validation failures
- Uncaught exceptions
- Provide a way to access or submit logs when issues occur
- Consider adding a "Restore Purchases" feature where users can manually trigger receipt validation
Troubleshooting Purchase Issues
Unacknowledged Purchases
When a purchase is not acknowledged (finished):
- Google Play will automatically refund it after 3 days
- No webhook will be sent to iaptic until acknowledgment
- The purchase won't be associated with any user in iaptic
To prevent these issues:
- Always initialize the plugin at app startup
- Ensure proper error handling during receipt validation
- Implement proper logging to track validation failures
Debugging Missing Purchases
If a purchase is missing from iaptic:
- Check if the purchase was acknowledged using the Google Order ID (GPA.xxxx-xxxx) or Apple Transaction ID.
- Verify client-side logs for errors that could have caused the call to verify() to be skipped or failed.
- You might want to log calls to
CdvPurchase.store.error(...)
andCdvPurchase.store.when().unverified(...)
. - Ensure the plugin is properly initialized and handlers are attached at app startup
Common Implementation Issues
-
Late Plugin Initialization:
- While it's possible to initialize the plugin on-demand, this may lead to unprocessed purchases
- Always initialize at app startup to handle background purchases automatically
- If needed, provide a "Restore Purchases" feature as a fallback
-
Missing Error Handling:
- Implement proper error logging for plugin operations
- Track receipt validation failures
- Monitor unhandled exceptions
-
Purchase Flow Issues:
- After successful payment, no additional user action should be needed
- The app should handle verification and acknowledgment automatically
- Always verify purchases before acknowledging them