Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
24c0f90
feat: payments v4
triniwiz Aug 12, 2025
084b582
chore(payments): clean up
triniwiz Aug 12, 2025
3687e90
chore(payments): bump
triniwiz Aug 12, 2025
50a75a4
fix(payments): purchase options
triniwiz Aug 12, 2025
3b5f1cd
chore(payments): bump
triniwiz Aug 12, 2025
f9e3ffd
fix(payment): isReady
triniwiz Aug 12, 2025
e57a3ea
fix(payment): clone
triniwiz Aug 12, 2025
fd73943
chore(payments): bump
triniwiz Aug 12, 2025
b0c9a70
feat(payments): Transaction type
triniwiz Aug 12, 2025
4479e73
chore(payment): bump
triniwiz Aug 12, 2025
665eed3
fix(payments): isAcknowledged
triniwiz Aug 12, 2025
5be4a1d
chore(payment): bump
triniwiz Aug 12, 2025
a48b638
feat(payments): isExpired, expirationDate, isRevoked & revocationDate
triniwiz Aug 13, 2025
0f3ad78
fix(payments): purchaseProduct
triniwiz Aug 13, 2025
d4998e5
fix(payments): priceAmountMicros
triniwiz Aug 13, 2025
cc1b646
fix(payments): purchase flow
triniwiz Aug 13, 2025
6b6a57e
fix(payment): purchase update listener
triniwiz Aug 13, 2025
93c6cee
feat(payments): use string for response.code
triniwiz Aug 13, 2025
bcd40ce
chore(payments): bump
triniwiz Aug 13, 2025
b7b48cd
fix(payments): onPurchase error handling
triniwiz Aug 14, 2025
3f97b83
chore(payments): bump
triniwiz Aug 14, 2025
57231da
feat(payments): show storekit version used
triniwiz Aug 14, 2025
df68216
fix(payments): iOS receipt storekit v1
triniwiz Aug 14, 2025
2225b1f
fix(payments): isExpired & isRevoked
triniwiz Aug 14, 2025
f1651a2
feat(payments): showSubscriptionsManagement
triniwiz Aug 15, 2025
f7a783f
fix(payments): purchase & restore state
triniwiz Aug 15, 2025
7e9adfb
chore(payments): bump
triniwiz Aug 15, 2025
3196aff
feat(payments): forceStoreV1Receipt
triniwiz Aug 15, 2025
ca44f0b
fix(payments): v1 store
triniwiz Aug 15, 2025
11c587e
fix(payments): callback execution
triniwiz Aug 15, 2025
9e40efd
fix(payments): callback execute
triniwiz Aug 15, 2025
1d21459
fix(payments): set alwaysStoreV1Receipt visibility as public
triniwiz Aug 18, 2025
092cbac
fix(payments): first purchase not returning v1 receipt
triniwiz Aug 19, 2025
5f87f45
feat(payments): allow optional consume for non subs
triniwiz Aug 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 96 additions & 64 deletions apps/demo/src/plugin-demos/payments.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EventData, ItemEventData, Observable, ObservableArray, Page } from '@nativescript/core';
import { buyItem, BuyItemOptions, canMakePayments, fetchItems, finalizeOrder, init as initPayments, Item, PaymentEvent, paymentEvents, toMainThread } from '@nativescript/payments';
import { Product, Payment } from '@nativescript/payments';
import { ObservableProperty } from '../obs-prop';

let subscription;
Expand All @@ -11,92 +11,124 @@ export function navigatedTo(args: EventData) {

export class DemoModel extends Observable {
@ObservableProperty()
items: ObservableArray<Item>;
items: ObservableArray<Product>;

private _isPaymentSystemInitialized = false;
private _inPurchaseFlow = false;

private _payment: Payment;
constructor() {
super();

console.log('is payment supported: ' + Payment.isSupported());

if (this._isPaymentSystemInitialized === true) {
// if already initialized don't run everything again
return;
}

// Subscribe the Handlers

paymentEvents.pipe(toMainThread()).subscribe((event: PaymentEvent.Type) => {
switch (event.context) {
case PaymentEvent.Context.CONNECTING_STORE:
if (event.result === PaymentEvent.Result.SUCCESS) {
if (!this._isPaymentSystemInitialized) {
console.log('🟢 Initialized In App Purchase Payments 🟢');
const canPay = canMakePayments();
console.log(`${canPay ? '🟢 Can Make Payments 🟢' : '🛑 Unable to make payments 🛑'}`);

if (canPay) {
fetchItems(['io.nstudio.iapdemo.coinsfive', 'io.nstudio.iapdemo.coinsone', 'io.nstudio.iapdemo.coinsonethousand']);
// fetchSubscriptions(['io.nstudio.iapdemo.monthly_subscription']);
}
this._isPaymentSystemInitialized = true;
}
}
break;
case PaymentEvent.Context.RETRIEVING_ITEMS:
console.log('RETRIEVING_ITEMS STATUS: ' + event.result);
if (event.result === PaymentEvent.Result.SUCCESS) {
console.log(event);
this.items = new ObservableArray(event.payload);
console.log(`🟢 Got ${this.items.length} In App Purchase Items 🟢`);
}
break;
case PaymentEvent.Context.PROCESSING_ORDER:
// console.log(event);
if (event.result === PaymentEvent.Result.FAILURE) {
console.log(`🛑 Payment Failure - ${event.payload.description} ${event.payload.nativeCode} ${event.payload.type} 🛑`);
this._inPurchaseFlow = false;
} else if (event.result === PaymentEvent.Result.SUCCESS) {
console.log('🟢 Payment Success 🟢');
// console.log(event);
console.log(`Order Id: ${event.payload.orderId}`);
console.log(`Order Date: ${event.payload.orderDate}`);
console.log(`Receipt Token: ${event.payload.receiptToken}`);
finalizeOrder(event.payload);
}
break;
case PaymentEvent.Context.FINALIZING_ORDER:
if (event.result === PaymentEvent.Result.SUCCESS) {
console.log('Order Finalized');
// paymentEvents.pipe(toMainThread()).subscribe((event: PaymentEvent.Type) => {
// switch (event.context) {
// case PaymentEvent.Context.CONNECTING_STORE:
// if (event.result === PaymentEvent.Result.SUCCESS) {
// if (!this._isPaymentSystemInitialized) {
// console.log('🟢 Initialized In App Purchase Payments 🟢');
// const canPay = canMakePayments();
// console.log(`${canPay ? '🟢 Can Make Payments 🟢' : '🛑 Unable to make payments 🛑'}`);

// if (canPay) {
// fetchItems(['io.nstudio.iapdemo.coinsfive', 'io.nstudio.iapdemo.coinsone', 'io.nstudio.iapdemo.coinsonethousand']);
// // fetchSubscriptions(['io.nstudio.iapdemo.monthly_subscription']);
// }
// this._isPaymentSystemInitialized = true;
// }
// }
// break;
// case PaymentEvent.Context.RETRIEVING_ITEMS:
// console.log('RETRIEVING_ITEMS STATUS: ' + event.result);
// if (event.result === PaymentEvent.Result.SUCCESS) {
// console.log(event);
// this.items = new ObservableArray(event.payload);
// console.log(`🟢 Got ${this.items.length} In App Purchase Items 🟢`);
// }
// break;
// case PaymentEvent.Context.PROCESSING_ORDER:
// // console.log(event);
// if (event.result === PaymentEvent.Result.FAILURE) {
// console.log(`🛑 Payment Failure - ${event.payload.description} ${event.payload.nativeCode} ${event.payload.type} 🛑`);
// this._inPurchaseFlow = false;
// } else if (event.result === PaymentEvent.Result.SUCCESS) {
// console.log('🟢 Payment Success 🟢');
// // console.log(event);
// console.log(`Order Id: ${event.payload.orderId}`);
// console.log(`Order Date: ${event.payload.orderDate}`);
// console.log(`Receipt Token: ${event.payload.receiptToken}`);
// finalizeOrder(event.payload);
// }
// break;
// case PaymentEvent.Context.FINALIZING_ORDER:
// if (event.result === PaymentEvent.Result.SUCCESS) {
// console.log('Order Finalized');
// }
// break;
// case PaymentEvent.Context.RESTORING_ORDERS:
// console.log(event);
// break;
// default:
// console.log(`Invalid EventContext: ${event}`);
// break;
// }
// });

// subscription = paymentEvents.connect();

// Here we initialize the payment system
// initPayments();
this._payment = new Payment();
this._payment.onPurchaseUpdate = (purchases, error) => {
if (error) {
console.error(`🛑 Purchase Update Error: ${error} 🛑`);
} else {
console.log(`🟢 Purchase Update: ${purchases.length} items 🟢`);
purchases.forEach((transaction) => {
if (transaction.state === 'pending') {
transaction.finish();
} else if (transaction.state === 'purchased') {
console.log(`🟢 Purchase Update: ${JSON.stringify(transaction.receiptToken)} 🟢`);
}
break;
case PaymentEvent.Context.RESTORING_ORDERS:
console.log(event);
break;
default:
console.log(`Invalid EventContext: ${event}`);
break;
});
}
});
};
this._payment.onReady = async () => {
try {
console.log('🟢 Payment System Ready 🟢');
const purchases = await this._payment.fetchPurchases();
console.log(`🟢 Fetched previous ${purchases.length} In App Purchase Items 🟢`);

subscription = paymentEvents.connect();
const products = await this._payment.fetchProducts(['io.nstudio.iapdemo.coinsfive', 'io.nstudio.iapdemo.coinsone', 'io.nstudio.iapdemo.coinsonethousand'], 'inapp');

// Here we initialize the payment system
initPayments();
this.items = new ObservableArray(products);
} catch (error) {
console.error(`🛑 Error: ${error} 🛑`);
}
};
}

// The event will be raise when an item inside the ListView is tapped.
onItemTap(args: ItemEventData) {
const item = this.items.getItem(args.index);

this._inPurchaseFlow = true;
const opts: BuyItemOptions = {
// accountUserName: 'bradwaynemartin@gmail.com',
ios: {
quantity: 1,
},
};
this._payment.purchaseProduct(item);

// this._inPurchaseFlow = true;
// const opts: BuyItemOptions = {
// // accountUserName: 'bradwaynemartin@gmail.com',
// ios: {
// quantity: 1,
// },
// };

buyItem(item, opts);
// buyItem(item, opts);
}
}
Loading