Last active
April 15, 2024 19:26
-
-
Save j3k0/8b7e72b6af43572651328c613ac86d59 to your computer and use it in GitHub Desktop.
cordova-purchase-plugin v13 - micro example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 => `<div id="${product.id}-purchase" style="margin-top: 30px">...</div>`) | |
.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 ? '<strike>' : ''; } | |
function strikeEnd(when: boolean) { return when ? '</strike>' : ''; } | |
// Create and update the HTML content | |
const id = `id: ${product.id}<br/>`; | |
const info = | |
(`title: ${product.title || ''}<br/>`) + | |
(product.description ? `desc: ${product.description || ''}<br/>` : ''); | |
const offers = product.offers ? 'offers:<ul>' + product.offers.map(offer => { | |
return '<li>' + (offer.pricingPhases || []).map(pricingPhase => { | |
const cycles = | |
pricingPhase.recurrenceMode === 'FINITE_RECURRING' | |
? `${pricingPhase.billingCycles}x ` | |
: pricingPhase.recurrenceMode === 'NON_RECURRING' ? '1x ' | |
: 'every '; // INFINITE_RECURRING | |
return `${pricingPhase.price} (${cycles}${formatDuration(pricingPhase.billingPeriod)})`; | |
}).join(' then ') + ` <button onclick="orderOffer('${product.platform}', '${product.id}', '${offer.id}')">Buy</button></li>`; | |
}).join('') + '</ul>' : ''; | |
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 = '<br/>'; | |
}, 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); | |
} | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
And the webhook does not work all the time we see the receipt then no webhook. This Is intermittent. Are you guys having issues with your servers?