@virgodev/iap 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/env.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type { CdvPurchase } from "cordova-plugin-purchase/src/ts/store";
2
+
3
+ export {};
4
+
5
+ declare global {
6
+ interface Window {
7
+ CdvPurchase: CdvPurchase;
8
+ }
9
+ }
package/main.ts ADDED
@@ -0,0 +1 @@
1
+ export { Platform, ProductType, useIapStore } from "./stores/iap";
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@virgodev/iap",
3
+ "version": "1.0.0",
4
+ "main": "./main.ts",
5
+ "scripts": {
6
+ "test": "echo \"Error: no test specified\" && exit 1"
7
+ },
8
+ "author": "",
9
+ "license": "ISC",
10
+ "description": "",
11
+ "dependencies": {
12
+ "@capacitor/app": "^7.1.0",
13
+ "@virgodev/bazaar": "^1.2.7",
14
+ "cordova-plugin-purchase": "^13.12.1"
15
+ },
16
+ "peerDependencies": {
17
+ "@capacitor/device": "^7.0.2",
18
+ "pinia": "^3.0.3"
19
+ }
20
+ }
package/stores/iap.ts ADDED
@@ -0,0 +1,213 @@
1
+ import { App, type AppInfo } from "@capacitor/app";
2
+ import { Capacitor } from "@capacitor/core";
3
+ import { Device } from "@capacitor/device";
4
+ import api from "@virgodev/bazaar/functions/api";
5
+ import { createStore } from "@virgodev/vue-models/utils/create_store";
6
+ import { defineStore } from "pinia";
7
+ import { computed, ref } from "vue";
8
+ import { useRouter } from "vue-router";
9
+
10
+ export enum ProductType {
11
+ CONSUMABLE = "consumable",
12
+ NON_CONSUMABLE = "non consumable",
13
+ FREE_SUBSCRIPTION = "free subscription",
14
+ PAID_SUBSCRIPTION = "paid subscription",
15
+ NON_RENEWING_SUBSCRIPTION = "non renewing subscription",
16
+ APPLICATION = "application",
17
+ }
18
+
19
+ export enum Platform {
20
+ APPLE_APPSTORE = "ios-appstore",
21
+ GOOGLE_PLAY = "android-playstore",
22
+ WINDOWS_STORE = "windows-store-transaction",
23
+ BRAINTREE = "braintree",
24
+ TEST = "test",
25
+ }
26
+
27
+ export interface IapProduct {
28
+ id: string;
29
+ type: ProductType;
30
+ platform: Platform;
31
+ }
32
+
33
+ export interface Transaction {
34
+ // "className": "Transaction",
35
+ // "transactionId": "GPA.3336-6862-5718-29728",
36
+ // "state": "approved",
37
+ products: { id: string }[];
38
+ // {
39
+ // "id": "test.item1"
40
+ // }
41
+ // ],
42
+ platform: string; //"android-playstore",
43
+ // "nativePurchase": {
44
+ // "orderId": "GPA.3336-6862-5718-29728",
45
+ // "packageName": "com.brokerboard.app",
46
+ // "productId": "test.item1",
47
+ // "purchaseTime": 1760736653503,
48
+ // "purchaseState": 0,
49
+ // "purchaseToken": "bopjdgkledpifbfplipfacdb.AO-J1Oy53Bdn1Es9imXavMr_mrHDyB_QT8J_Kf4StOr3zlRibNc49eOoFL14-owv9wK7wYA9NpM4VCU19Tf0uF_1rLajHKgMOg",
50
+ // "quantity": 1,
51
+ // "acknowledged": false,
52
+ // "productIds": [
53
+ // "test.item1"
54
+ // ],
55
+ // "getPurchaseState": 1,
56
+ // "developerPayload": "",
57
+ // "autoRenewing": false,
58
+ // "accountId": "",
59
+ // "profileId": "",
60
+ // "signature": "YUTkvFSZGRSBoWsE0XaVCqQvwAxflHWGDmFVI4mAFEHriMeXNhbCt/pyxutsOcq+iZEHGlPDoWbxljbn8dZI6oAeMD2NfkvFgeiTAiyc3A/pOJ2HOynl5f8FwDwn/hT/ueD8w0vVUuc1OgUsvFAKuT3Zmz1G/+E1c3GLv/aXZmDT4OvWuxXCKOWf8NS9Vx6nk1Bw7rmCpMlVvzXwzRiHKPMU1w6GafOQ9zqm3qGHDk8SOuqMpVW9tcAcDvED6H1A2nrQrXStkMR/0gWf8n+yxukNSk/JVyUoOGRFmhO8rHgpeKyDHKsuf1VGfere/MWbEbjqmkWEWRgAMeGuBLQIGg==",
61
+ // "receipt": "{\"orderId\":\"GPA.3336-6862-5718-29728\",\"packageName\":\"com.brokerboard.app\",\"productId\":\"test.item1\",\"purchaseTime\":1760736653503,\"purchaseState\":0,\"purchaseToken\":\"bopjdgkledpifbfplipfacdb.AO-J1Oy53Bdn1Es9imXavMr_mrHDyB_QT8J_Kf4StOr3zlRibNc49eOoFL14-owv9wK7wYA9NpM4VCU19Tf0uF_1rLajHKgMOg\",\"quantity\":1,\"acknowledged\":false}"
62
+ // },
63
+ transactionId: string;
64
+ purchaseId: string; //"bopjdgkledpifbfplipfacdb.AO-J1Oy53Bdn1Es9imXavMr_mrHDyB_QT8J_Kf4StOr3zlRibNc49eOoFL14-owv9wK7wYA9NpM4VCU19Tf0uF_1rLajHKgMOg",
65
+ // "purchaseDate": "2025-10-17T21:30:53.503Z",
66
+ // "isPending": false,
67
+ // "isAcknowledged": false,
68
+ // "renewalIntent": "Lapse"
69
+
70
+ finish: () => void;
71
+ }
72
+
73
+ export interface Purchase {
74
+ id: number;
75
+ user: number;
76
+ txid: string;
77
+ code: string;
78
+ app: AppInfo;
79
+ device: string;
80
+ platform: Platform;
81
+ Transaction: Transaction;
82
+ subscription: boolean;
83
+ next: string;
84
+ created: string;
85
+ completed: string;
86
+ updated: string;
87
+
88
+ result: any;
89
+ error: string;
90
+ }
91
+
92
+ export const usePurchases = createStore<Purchase>("purchases");
93
+
94
+ export const useIapStore = defineStore("iap", () => {
95
+ const router = useRouter();
96
+ const platform = ref<string>(Capacitor.getPlatform());
97
+ const isReady = ref(false);
98
+ const purchases = usePurchases();
99
+ const appInfo = ref<AppInfo>();
100
+ const deviceIndentifier = ref("");
101
+
102
+ const storePlatform = computed(() => {
103
+ if (platform.value === "android") {
104
+ return Platform.GOOGLE_PLAY;
105
+ } else {
106
+ return Platform.APPLE_APPSTORE;
107
+ }
108
+ });
109
+
110
+ async function initialize(products: IapProduct[]) {
111
+ if (window.CdvPurchase) {
112
+ appInfo.value = await App.getInfo();
113
+ deviceIndentifier.value = (await Device.getId()).identifier;
114
+
115
+ const { store, LogLevel } = window.CdvPurchase;
116
+ store.verbosity = LogLevel.INFO;
117
+ products.forEach((product) => {
118
+ const p = store.register(product);
119
+ });
120
+ store.off(purchaseCompleteHook);
121
+ store.when().approved(purchaseCompleteHook);
122
+ store.ready(() => {
123
+ console.info("store is ready", store.products, "product(s)");
124
+ isReady.value = true;
125
+ });
126
+
127
+ store.initialize();
128
+ } else {
129
+ console.warn("CdvPurchase not found, skipping in app purchases");
130
+ }
131
+ }
132
+
133
+ async function purchaseItem(code: string) {
134
+ if (window.CdvPurchase) {
135
+ const { store } = window.CdvPurchase;
136
+ const product = store.get(code, storePlatform.value);
137
+ if (product) {
138
+ const offer = product.getOffer();
139
+ return offer.order();
140
+ } else {
141
+ console.error("Product not found");
142
+ }
143
+ } else {
144
+ throw new Error("CdvPurchase not available");
145
+ }
146
+ }
147
+
148
+ async function findPurchases(codes: string[]) {
149
+ await purchases.sync();
150
+ return purchases.list.filter((p) => !p.error && codes.includes(p.code));
151
+ }
152
+
153
+ async function purchaseCompleteHook(p: Transaction) {
154
+ const { store } = window.CdvPurchase;
155
+ // why is this returning?
156
+ if (p.transactionId === "appstore.application") {
157
+ return;
158
+ }
159
+
160
+ let productId = p.products[0].id;
161
+
162
+ const finishing = localStorage.getItem("iap:finishing");
163
+ if (finishing !== p.transactionId) {
164
+ localStorage.setItem("iap:finishing", p.transactionId);
165
+
166
+ const sub = [
167
+ ProductType.PAID_SUBSCRIPTION,
168
+ ProductType.FREE_SUBSCRIPTION,
169
+ ].includes(store.products.find((p2: any) => p2.id === productId)?.type);
170
+
171
+ const response = await api({
172
+ url: "purchases/verify/",
173
+ method: "POST",
174
+ headers: {
175
+ "Content-Type": "application/json",
176
+ },
177
+ json: {
178
+ txid: p.transactionId,
179
+ code: productId,
180
+ app: appInfo.value,
181
+ device: deviceIndentifier.value,
182
+ transaction: p,
183
+ platform: p.platform,
184
+ subscription: sub,
185
+ },
186
+ });
187
+
188
+ if (!response.body.error) {
189
+ p.finish();
190
+ }
191
+ }
192
+ }
193
+
194
+ function getProducts(): any[] {
195
+ const { store } = window.CdvPurchase;
196
+ return store.products;
197
+ }
198
+
199
+ return {
200
+ platform,
201
+ storePlatform,
202
+ initialize,
203
+ getProducts,
204
+ purchaseItem,
205
+ findPurchases,
206
+ purchases,
207
+ };
208
+ });
209
+
210
+ // const product = store.products.find((p) => p.id === offer.product);
211
+ // if (product) {
212
+ // offer.price = product.pricing.price;
213
+ // }