Skip to content

Instantly share code, notes, and snippets.

@robertopc
Forked from j3k0/index.ts
Created October 27, 2023 20:44
Show Gist options
  • Save robertopc/243332fb05fdfb4408a52961470f4efa to your computer and use it in GitHub Desktop.
Save robertopc/243332fb05fdfb4408a52961470f4efa to your computer and use it in GitHub Desktop.
cordova-purchase-plugin v13 - micro example
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