document.addEventListener('deviceready', onDeviceReady); function onDeviceReady() { const store = CdvPurchase.store; const { ProductType, Platform, LogLevel, Product, VerifiedReceipt } = CdvPurchase; // shortcuts // We should first register all our products or we cannot use them in the app. store.register([{ id: 'demo_monthly_basic', type: ProductType.PAID_SUBSCRIPTION, platform: Platform.GOOGLE_PLAY, }, { id: 'demo_weekly_basic', type: ProductType.PAID_SUBSCRIPTION, platform: Platform.GOOGLE_PLAY, }]); store.verbosity = LogLevel.DEBUG; store.applicationUsername = () => "my_username_2"; // the plugin will hash this with md5 when needed // For subscriptions and secured transactions, we setup a receipt validator. store.validator = "https://staging.com/v1/validate?appName=XXX&apiKey=YYY"; store.validator_privacy_policy = ['analytics', 'support', 'tracking', 'fraud']; // Show errors on the dedicated Div. store.error(errorHandler); // Define events handler for our subscription products store.when() .updated(object => { // Re-render the interface on updates log.info('Updated: ' + JSON.stringify(object)); renderUI(); }) .approved(transaction => { // verify approved transactions store.verify(transaction); }) .verified(receipt => { // finish transactions from verified receipts store.finish(receipt); renderUI(); }); // Load informations about products and purchases store.initialize([ Platform.APPLE_APPSTORE, Platform.GOOGLE_PLAY, { platform: Platform.BRAINTREE, options: { tokenizationKey: 'sandbox_xyz', nonceProvider: (type, callback) => { callback({ // only 3D secure supported. type: CdvPurchase.Braintree.PaymentMethod.THREE_D_SECURE, value: 'fake-valid-nonce', }); } } } ]); // Updates the user interface to reflect the initial state renderUI(); } // Perform a full render of the user interface function renderUI() { const store = CdvPurchase.store; // When either of our susbscription products is owned, display "Subscribed". // If one of them is being purchased or validated, display "Processing". // In all other cases, display "Not Subscribed". const subscriptions = store.products.filter(p => p.type === CdvPurchase.ProductType.PAID_SUBSCRIPTION); const statusElement = document.getElementById('status'); const productsElement = document.getElementById('products'); if (!statusElement || !productsElement) return; if (isOwned(subscriptions)) statusElement.textContent = 'Subscribed'; else if (isApproved(subscriptions) || isInitiated(subscriptions)) statusElement.textContent = 'Processing...'; else statusElement.textContent = 'Not Subscribed'; const validProducts = store.products.filter(product => product.offers.length > 0); productsElement.innerHTML = validProducts .map(product => `
...
`) .join(''); // Render the products' DOM elements validProducts.forEach(renderProductUI); // Find a verified purchase for one of the provided products that passes the given filter. function findVerifiedPurchase(products: CdvPurchase.Product[], filter: (purchase: CdvPurchase.VerifiedPurchase) => boolean): CdvPurchase.VerifiedPurchase | undefined { for (const product of products) { const purchase = store.findInVerifiedReceipts(product); if (!purchase) continue; if (filter(purchase)) return purchase; } } // Find a local transaction for one of the provided products that passes the given filter. function findLocalTransaction(products: CdvPurchase.Product[], filter: (transaction: CdvPurchase.Transaction) => boolean): CdvPurchase.Transaction | undefined { // find if some of those products are part of a receipt for (const product of products) { const transaction = store.findInLocalReceipts(product); if (!transaction) continue; if (filter(transaction)) return transaction; } } function isOwned(products: CdvPurchase.Product[]): boolean { return !!findVerifiedPurchase(products, p => !p.isExpired); } function isApproved(products: CdvPurchase.Product[]) { return !!findLocalTransaction(products, t => t.state === CdvPurchase.TransactionState.APPROVED); } function isInitiated(products: CdvPurchase.Product[]) { return !!findLocalTransaction(products, t => t.state === CdvPurchase.TransactionState.INITIATED); } /** * Refresh the displayed details about a product in the DOM */ function renderProductUI(product: CdvPurchase.Product) { const productId = product.id; const el = document.getElementById(`${productId}-purchase`); if (!el) { log.error(`HTML element ${productId}-purchase does not exists`); return; } function strikeIf(when: boolean) { return when ? '' : ''; } function strikeEnd(when: boolean) { return when ? '' : ''; } // Create and update the HTML content const id = `id: ${product.id}
`; const info = (`title: ${product.title || ''}
`) + (product.description ? `desc: ${product.description || ''}
` : ''); const offers = product.offers ? 'offers:' : ''; el.innerHTML = id + info + /* discounts + subInfo + */ offers; } } function orderOffer(platform: CdvPurchase.Platform, productId: string, offerId: string) { const store = CdvPurchase.store; const offer = store.get(productId, platform)?.getOffer(offerId); if (offer) store.order(offer); } function formatDuration(iso: string | undefined): string { if (!iso) return ''; const l = iso.length; const n = iso.slice(1, l - 1); if (n === '1') { return ({ 'D': 'Day', 'W': 'Week', 'M': 'Month', 'Y': 'Year', }[iso[l - 1]]) || iso[l - 1]; } else { const u = ({ 'D': 'Days', 'W': 'Weeks', 'M': 'Months', 'Y': 'Years', }[iso[l - 1]]) || iso[l - 1]; return `${n} ${u}`; } } function errorHandler(error: CdvPurchase.IError) { const errorElement = document.getElementById('error'); if (!errorElement) return; errorElement.textContent = `ERROR ${error.code}: ${error.message}`; setTimeout(() => { errorElement.innerHTML = '
'; }, 10000); if (error.code === CdvPurchase.ErrorCode.LOAD_RECEIPTS) { // Cannot load receipt, ask user to refresh purchases. setTimeout(() => { alert('Cannot access purchase information. Use "Refresh" to try again.'); }, 1); } } function restorePurchases() { log.info('restorePurchases()'); CdvPurchase.store.restorePurchases(); } function launchBraintreePayment() { CdvPurchase.store.requestPayment({ platform: CdvPurchase.Platform.BRAINTREE, amountMicros: 1990000, currency: 'USD', description: 'This is the description of the payment request', }).then((result) => { if (result && result.code !== CdvPurchase.ErrorCode.PAYMENT_CANCELLED) { alert(result.message); } }) }