@nosslabs/iap 5.0.0 → 7.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/dist/index.d.cts CHANGED
@@ -844,10 +844,6 @@ interface EventMap<TEntitlement extends EntitlementBase = EntitlementBase> {
844
844
  entitlements: TEntitlement[];
845
845
  previous: TEntitlement[];
846
846
  };
847
- 'price-stale': {
848
- productId: string;
849
- lastFetchedAt: number;
850
- };
851
847
  /**
852
848
  * Recovery classified an `unfinished_transactions` entry as permanently
853
849
  * invalid (per `options.permanentErrorCodes`) and removed it from
@@ -866,9 +862,6 @@ interface EventMap<TEntitlement extends EntitlementBase = EntitlementBase> {
866
862
  error: string;
867
863
  message?: string;
868
864
  };
869
- error: {
870
- error: IAPError;
871
- };
872
865
  }
873
866
  type EventName<TEntitlement extends EntitlementBase = EntitlementBase> = keyof EventMap<TEntitlement>;
874
867
  type EventPayload<K extends EventName<TEntitlement>, TEntitlement extends EntitlementBase = EntitlementBase> = EventMap<TEntitlement>[K];
@@ -968,9 +961,7 @@ interface IAP<TEntitlement extends EntitlementBase = EntitlementBase> {
968
961
  */
969
962
  refresh(): Promise<void>;
970
963
  /**
971
- * Tear down. Removes event listeners and disposes the native adapter
972
- * (which clears its `pendingFinish` map and removes the long-lived
973
- * `.approved()` listener on cdv).
964
+ * Tear down. Removes event listeners and disposes the native adapter.
974
965
  *
975
966
  * NOTE 1: persisted entitlement cache is NOT cleared. If you're handling
976
967
  * a logout for a multi-user app, also call your storage adapter's
@@ -980,10 +971,11 @@ interface IAP<TEntitlement extends EntitlementBase = EntitlementBase> {
980
971
  *
981
972
  * NOTE 2: calling `destroy()` while a `purchase()` is in flight may
982
973
  * leave the result in an inconsistent state — the backend may have
983
- * recorded the entitlement but the native `acknowledge()` call will
984
- * be a no-op (because cdv's `pendingFinish` was cleared mid-flow).
985
- * On Android this means Google auto-refunds in 3 days. Avoid by
986
- * awaiting the in-flight `purchase()` before calling `destroy()`.
974
+ * recorded the entitlement but the native `acknowledge()` call may not
975
+ * have run yet. On Android this means Google auto-refunds in 3 days
976
+ * (the unfinished-transaction recovery on the next launch re-acks, but
977
+ * only if it runs within that window). Avoid by awaiting the in-flight
978
+ * `purchase()` before calling `destroy()`.
987
979
  */
988
980
  destroy(): Promise<void>;
989
981
  /**
@@ -1222,4 +1214,4 @@ declare class HttpBackendAdapter<TEntitlement extends EntitlementBase = Entitlem
1222
1214
  listProducts(): Promise<ConfiguredProduct[]>;
1223
1215
  }
1224
1216
 
1225
- export { type AppUserId, type BackendAdapter, type BackendConfig, type BackendConfigInput, type ConfiguredProduct, DEFAULT_PERMANENT_ERROR_CODES, type DefaultEntitlement, type EntitlementBase, type EventMap, type EventName, type EventPayload, HttpBackendAdapter, HttpClient, type HttpRequest, type IAP, type IAPConfig, type IAPConfigInput, IAPError, IAPErrorCode, type LogLevel, type Logger, type NativeTransaction, type OptionsConfig, type Platform, type Product, type ProductType, type PurchaseOptions, type PurchaseResult, type RestoreRequest, type RestoreRequestTransaction, type RestoreResponse, type RestoreResult, type StorageConfig, type Unsubscribe, VERSION, type VerifiedTransaction, type VerifyAppleRequest, type VerifyGoogleRequest, type VerifyResponse, createIAP, errorHint, isIAPError };
1217
+ export { type AppUserId, type AppUserIdFetcherContext, type BackendAdapter, type BackendConfig, type BackendConfigInput, type ConfiguredProduct, DEFAULT_PERMANENT_ERROR_CODES, type DefaultEntitlement, type EntitlementBase, type EventMap, type EventName, type EventPayload, HttpBackendAdapter, HttpClient, type HttpRequest, type IAP, type IAPConfig, type IAPConfigInput, IAPError, IAPErrorCode, type LogLevel, type Logger, type NativeTransaction, type OptionsConfig, type Platform, type Product, type ProductType, type PurchaseOptions, type PurchaseResult, type RestoreRequest, type RestoreRequestTransaction, type RestoreResponse, type RestoreResult, type StorageConfig, type Unsubscribe, VERSION, type VerifiedTransaction, type VerifyAppleRequest, type VerifyGoogleRequest, type VerifyResponse, createIAP, errorHint, isIAPError };
package/dist/index.d.ts CHANGED
@@ -844,10 +844,6 @@ interface EventMap<TEntitlement extends EntitlementBase = EntitlementBase> {
844
844
  entitlements: TEntitlement[];
845
845
  previous: TEntitlement[];
846
846
  };
847
- 'price-stale': {
848
- productId: string;
849
- lastFetchedAt: number;
850
- };
851
847
  /**
852
848
  * Recovery classified an `unfinished_transactions` entry as permanently
853
849
  * invalid (per `options.permanentErrorCodes`) and removed it from
@@ -866,9 +862,6 @@ interface EventMap<TEntitlement extends EntitlementBase = EntitlementBase> {
866
862
  error: string;
867
863
  message?: string;
868
864
  };
869
- error: {
870
- error: IAPError;
871
- };
872
865
  }
873
866
  type EventName<TEntitlement extends EntitlementBase = EntitlementBase> = keyof EventMap<TEntitlement>;
874
867
  type EventPayload<K extends EventName<TEntitlement>, TEntitlement extends EntitlementBase = EntitlementBase> = EventMap<TEntitlement>[K];
@@ -968,9 +961,7 @@ interface IAP<TEntitlement extends EntitlementBase = EntitlementBase> {
968
961
  */
969
962
  refresh(): Promise<void>;
970
963
  /**
971
- * Tear down. Removes event listeners and disposes the native adapter
972
- * (which clears its `pendingFinish` map and removes the long-lived
973
- * `.approved()` listener on cdv).
964
+ * Tear down. Removes event listeners and disposes the native adapter.
974
965
  *
975
966
  * NOTE 1: persisted entitlement cache is NOT cleared. If you're handling
976
967
  * a logout for a multi-user app, also call your storage adapter's
@@ -980,10 +971,11 @@ interface IAP<TEntitlement extends EntitlementBase = EntitlementBase> {
980
971
  *
981
972
  * NOTE 2: calling `destroy()` while a `purchase()` is in flight may
982
973
  * leave the result in an inconsistent state — the backend may have
983
- * recorded the entitlement but the native `acknowledge()` call will
984
- * be a no-op (because cdv's `pendingFinish` was cleared mid-flow).
985
- * On Android this means Google auto-refunds in 3 days. Avoid by
986
- * awaiting the in-flight `purchase()` before calling `destroy()`.
974
+ * recorded the entitlement but the native `acknowledge()` call may not
975
+ * have run yet. On Android this means Google auto-refunds in 3 days
976
+ * (the unfinished-transaction recovery on the next launch re-acks, but
977
+ * only if it runs within that window). Avoid by awaiting the in-flight
978
+ * `purchase()` before calling `destroy()`.
987
979
  */
988
980
  destroy(): Promise<void>;
989
981
  /**
@@ -1222,4 +1214,4 @@ declare class HttpBackendAdapter<TEntitlement extends EntitlementBase = Entitlem
1222
1214
  listProducts(): Promise<ConfiguredProduct[]>;
1223
1215
  }
1224
1216
 
1225
- export { type AppUserId, type BackendAdapter, type BackendConfig, type BackendConfigInput, type ConfiguredProduct, DEFAULT_PERMANENT_ERROR_CODES, type DefaultEntitlement, type EntitlementBase, type EventMap, type EventName, type EventPayload, HttpBackendAdapter, HttpClient, type HttpRequest, type IAP, type IAPConfig, type IAPConfigInput, IAPError, IAPErrorCode, type LogLevel, type Logger, type NativeTransaction, type OptionsConfig, type Platform, type Product, type ProductType, type PurchaseOptions, type PurchaseResult, type RestoreRequest, type RestoreRequestTransaction, type RestoreResponse, type RestoreResult, type StorageConfig, type Unsubscribe, VERSION, type VerifiedTransaction, type VerifyAppleRequest, type VerifyGoogleRequest, type VerifyResponse, createIAP, errorHint, isIAPError };
1217
+ export { type AppUserId, type AppUserIdFetcherContext, type BackendAdapter, type BackendConfig, type BackendConfigInput, type ConfiguredProduct, DEFAULT_PERMANENT_ERROR_CODES, type DefaultEntitlement, type EntitlementBase, type EventMap, type EventName, type EventPayload, HttpBackendAdapter, HttpClient, type HttpRequest, type IAP, type IAPConfig, type IAPConfigInput, IAPError, IAPErrorCode, type LogLevel, type Logger, type NativeTransaction, type OptionsConfig, type Platform, type Product, type ProductType, type PurchaseOptions, type PurchaseResult, type RestoreRequest, type RestoreRequestTransaction, type RestoreResponse, type RestoreResult, type StorageConfig, type Unsubscribe, VERSION, type VerifiedTransaction, type VerifyAppleRequest, type VerifyGoogleRequest, type VerifyResponse, createIAP, errorHint, isIAPError };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Capacitor } from '@capacitor/core';
2
- import 'cordova-plugin-purchase';
2
+ import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
3
3
  import { z } from 'zod';
4
4
  import { Preferences } from '@capacitor/preferences';
5
5
 
@@ -83,7 +83,7 @@ var init_errors = __esm({
83
83
  NOT_INITIALIZED: "Call iap.initialize() before this method, or recreate the instance after destroy().",
84
84
  // Native plugin
85
85
  PLATFORM_NOT_SUPPORTED: "In-app purchases run on iOS/Android only. Web is no-op by design \u2014 guard your purchase UI behind Capacitor.isNativePlatform().",
86
- BILLING_NOT_AVAILABLE: "cordova-plugin-purchase failed to initialize. Confirm the plugin is installed and `npx cap sync` has run; check device sandbox/test account is signed in.",
86
+ BILLING_NOT_AVAILABLE: "The store billing service is unavailable. Confirm @capgo/native-purchases is installed and `npx cap sync` has run; check the device sandbox/test account is signed in.",
87
87
  PRODUCT_NOT_FOUND: "Ensure the productId is registered in App Store Connect / Play Console AND in your createIAP({ products }) config.",
88
88
  USER_CANCELLED: "No action needed \u2014 the user dismissed the native purchase sheet.",
89
89
  PURCHASE_PENDING: "Android only: payment is awaiting external clearance (e.g. cash payment, bank verification). The backend will receive a Google RTDN webhook when it clears; call iap.refresh() afterward.",
@@ -137,295 +137,170 @@ var init_platform = __esm({
137
137
  }
138
138
  });
139
139
 
140
- // src/adapters/native/cdv/native-adapter.ts
140
+ // src/adapters/native/capgo/native-adapter.ts
141
141
  var native_adapter_exports = {};
142
142
  __export(native_adapter_exports, {
143
- CdvNativeAdapter: () => CdvNativeAdapter
143
+ CapgoNativeAdapter: () => CapgoNativeAdapter
144
144
  });
145
- function getCdv() {
146
- const candidate = globalThis.CdvPurchase;
147
- if (!candidate || !candidate.store) {
148
- throw new IAPError({
149
- code: IAPErrorCode.BILLING_NOT_AVAILABLE,
150
- message: "cordova-plugin-purchase is not available. Ensure the plugin is installed and `npx cap sync` has run."
151
- });
152
- }
153
- return candidate;
154
- }
155
- function currentCdvPlatform() {
156
- const cdv = getCdv();
157
- const platform = getPlatform();
158
- if (platform === "android") return cdv.Platform.GOOGLE_PLAY;
159
- return cdv.Platform.APPLE_APPSTORE;
160
- }
161
- function mapProductType(type) {
162
- const cdv = getCdv();
163
- switch (type) {
164
- case "subscription":
165
- return cdv.ProductType.PAID_SUBSCRIPTION;
166
- case "consumable":
167
- return cdv.ProductType.CONSUMABLE;
168
- default:
169
- return cdv.ProductType.NON_CONSUMABLE;
170
- }
171
- }
172
- function inferProductType(tx, configured) {
173
- const id = tx.products[0]?.id;
174
- if (!id) return "product";
175
- const match = configured.find((p) => p.id === id);
176
- return match?.type ?? "product";
177
- }
178
145
  function normalizeProduct(p, type) {
179
- const offer = p.getOffer();
180
- const phase = offer?.pricingPhases?.[0];
181
- const priceMicros = phase?.priceMicros?.toString() ?? "0";
182
- const priceString = phase?.price ?? "";
183
- const currency = phase?.currency ?? "";
146
+ const priceMicros = Math.round(p.price * 1e6).toString();
184
147
  return {
185
- id: p.id,
148
+ id: p.identifier,
186
149
  type,
187
- title: p.title ?? p.id,
188
- description: p.description ?? "",
189
- priceString,
150
+ title: p.title,
151
+ description: p.description,
152
+ priceString: p.priceString,
190
153
  priceMicros,
191
- currency
154
+ currency: p.currencyCode
192
155
  };
193
156
  }
194
- function normalizeTransaction(tx, productType, token) {
195
- const platform = tx.platform === "ios-appstore" ? "apple" : "google";
196
- const productId = tx.products[0]?.id ?? "";
197
- const result = {
157
+ function normalizeTransaction(tx, productType) {
158
+ const platform = inferPlatform(tx);
159
+ const token = platform === "google" ? tx.purchaseToken ?? tx.transactionId : tx.transactionId;
160
+ const native = {
198
161
  platform,
199
- productId,
162
+ productId: tx.productIdentifier,
200
163
  token,
201
164
  productType,
202
165
  raw: tx
203
166
  };
204
- if (platform === "google") {
205
- const packageName = googlePackageName(tx);
206
- if (packageName) result.packageName = packageName;
167
+ return native;
168
+ }
169
+ function inferPlatform(tx) {
170
+ if (tx.purchaseToken !== void 0 || tx.purchaseState !== void 0 || tx.orderId !== void 0) {
171
+ return "google";
207
172
  }
208
- return result;
173
+ if (tx.receipt !== void 0 || tx.jwsRepresentation !== void 0) {
174
+ return "apple";
175
+ }
176
+ return getPlatform() === "android" ? "google" : "apple";
209
177
  }
210
- function googlePackageName(tx) {
211
- const googleTx = tx;
212
- return googleTx.nativePurchase?.packageName;
178
+ function inferProductType(tx) {
179
+ if (tx.productType === "subs") return "subscription";
180
+ return "product";
213
181
  }
214
- function transactionToken(tx) {
215
- if (tx.platform === "ios-appstore") {
216
- return tx.transactionId || null;
217
- }
218
- const googleTx = tx;
219
- return googleTx.nativePurchase?.purchaseToken ?? googleTx.parentReceipt?.purchaseToken ?? tx.transactionId ?? null;
182
+ function mapToPluginPurchaseType(type) {
183
+ return type === "subscription" ? PURCHASE_TYPE.SUBS : PURCHASE_TYPE.INAPP;
220
184
  }
221
- function mapOrderError(err, productId) {
222
- const cdv = getCdv();
223
- const cancelled = cdv.ErrorCode?.PAYMENT_CANCELLED;
224
- if (cancelled !== void 0 && err.code === cancelled) {
185
+ function mapPurchaseError(error, productId) {
186
+ if (error instanceof IAPError) return error;
187
+ const message = error instanceof Error ? error.message : String(error);
188
+ const lower = message.toLowerCase();
189
+ if (lower.includes("cancel")) {
225
190
  return new IAPError({
226
191
  code: IAPErrorCode.USER_CANCELLED,
227
- message: "User cancelled the native purchase sheet.",
228
- cause: err
192
+ message: `Purchase of "${productId}" was cancelled.`,
193
+ cause: error
194
+ });
195
+ }
196
+ if (lower.includes("pending")) {
197
+ return new IAPError({
198
+ code: IAPErrorCode.PURCHASE_PENDING,
199
+ message: `Purchase of "${productId}" is pending external clearance.`,
200
+ cause: error
201
+ });
202
+ }
203
+ if (lower.includes("product not found") || lower.includes("cannot find product")) {
204
+ return new IAPError({
205
+ code: IAPErrorCode.PRODUCT_NOT_FOUND,
206
+ message: `Product "${productId}" was not found in the store catalog.`,
207
+ cause: error
229
208
  });
230
209
  }
231
210
  return new IAPError({
232
211
  code: IAPErrorCode.STORE_ERROR,
233
- message: err.message ?? `order() failed for ${productId}.`,
234
- cause: err
212
+ message: `Native purchase of "${productId}" failed.`,
213
+ cause: error
235
214
  });
236
215
  }
237
- var CdvNativeAdapter;
216
+ var CapgoNativeAdapter;
238
217
  var init_native_adapter = __esm({
239
- "src/adapters/native/cdv/native-adapter.ts"() {
218
+ "src/adapters/native/capgo/native-adapter.ts"() {
240
219
  init_errors();
241
220
  init_platform();
242
- CdvNativeAdapter = class {
243
- products;
244
- bootstrapped = false;
245
- bootstrapping = null;
246
- pendingFinish = /* @__PURE__ */ new Map();
247
- /** Long-lived bootstrap-time .approved() listener — kept for dispose(). */
248
- bootstrapApprovedHandler = null;
249
- constructor(opts) {
250
- this.products = opts.products;
251
- }
221
+ CapgoNativeAdapter = class {
252
222
  async isAvailable() {
253
223
  try {
254
- await this.bootstrap();
255
- return true;
224
+ const result = await NativePurchases.isBillingSupported();
225
+ return result.isBillingSupported;
256
226
  } catch {
257
227
  return false;
258
228
  }
259
229
  }
260
230
  async getProducts(requests) {
261
231
  if (requests.length === 0) return [];
262
- const store = await this.ensureStore();
263
- await store.update();
264
- const out = [];
232
+ const inappIds = [];
233
+ const subsIds = [];
234
+ const requestById = /* @__PURE__ */ new Map();
265
235
  for (const req of requests) {
266
- const native = store.get(req.id);
267
- if (!native) continue;
268
- out.push(normalizeProduct(native, req.type));
236
+ requestById.set(req.id, req.type);
237
+ if (req.type === "subscription") {
238
+ subsIds.push(req.id);
239
+ } else {
240
+ inappIds.push(req.id);
241
+ }
269
242
  }
270
- return out;
243
+ const [inapp, subs] = await Promise.all([
244
+ inappIds.length > 0 ? NativePurchases.getProducts({
245
+ productIdentifiers: inappIds,
246
+ productType: PURCHASE_TYPE.INAPP
247
+ }) : Promise.resolve({ products: [] }),
248
+ subsIds.length > 0 ? NativePurchases.getProducts({
249
+ productIdentifiers: subsIds,
250
+ productType: PURCHASE_TYPE.SUBS
251
+ }) : Promise.resolve({ products: [] })
252
+ ]);
253
+ const all = [...inapp.products, ...subs.products];
254
+ return all.map((p) => normalizeProduct(p, requestById.get(p.identifier) ?? "product"));
271
255
  }
272
256
  async purchaseProduct(opts) {
273
- const store = await this.ensureStore();
274
- const native = store.get(opts.productId);
275
- if (!native) {
276
- throw new IAPError({
277
- code: IAPErrorCode.PRODUCT_NOT_FOUND,
278
- message: `Product "${opts.productId}" not registered or not available from the store.`
279
- });
280
- }
281
- const offer = opts.androidPlanId ? native.getOffer(opts.androidPlanId) ?? native.getOffer() : native.getOffer();
282
- if (!offer) {
283
- throw new IAPError({
284
- code: IAPErrorCode.PRODUCT_NOT_FOUND,
285
- message: `Product "${opts.productId}" has no purchasable offer${opts.androidPlanId ? ` (planId="${opts.androidPlanId}")` : ""}.`
257
+ const purchaseType = mapToPluginPurchaseType(opts.productType);
258
+ const isConsumable = opts.productType === "consumable";
259
+ let tx;
260
+ try {
261
+ tx = await NativePurchases.purchaseProduct({
262
+ productIdentifier: opts.productId,
263
+ productType: purchaseType,
264
+ planIdentifier: opts.androidPlanId,
265
+ appAccountToken: opts.appAccountToken,
266
+ isConsumable,
267
+ autoAcknowledgePurchases: false
286
268
  });
269
+ } catch (error) {
270
+ throw mapPurchaseError(error, opts.productId);
287
271
  }
288
- return new Promise((resolve, reject) => {
289
- let settled = false;
290
- const cleanup = () => {
291
- store.off(handleApproved);
292
- };
293
- const handleApproved = (tx) => {
294
- if (settled) return;
295
- if (!tx.products.some((p) => p.id === opts.productId)) return;
296
- const token = transactionToken(tx);
297
- if (!token) {
298
- settled = true;
299
- cleanup();
300
- reject(
301
- new IAPError({
302
- code: IAPErrorCode.STORE_ERROR,
303
- message: `Approved transaction for "${opts.productId}" has no token; cannot verify.`
304
- })
305
- );
306
- return;
307
- }
308
- settled = true;
309
- cleanup();
310
- const normalized = normalizeTransaction(tx, opts.productType, token);
311
- this.pendingFinish.set(token, tx);
312
- resolve(normalized);
313
- };
314
- store.when().approved(handleApproved);
315
- const additionalData = opts.appAccountToken ? { applicationUsername: opts.appAccountToken } : void 0;
316
- void Promise.resolve(offer.order(additionalData)).then((err) => {
317
- if (settled) return;
318
- if (!err) return;
319
- settled = true;
320
- cleanup();
321
- reject(mapOrderError(err, opts.productId));
322
- }).catch((cause) => {
323
- if (settled) return;
324
- settled = true;
325
- cleanup();
326
- reject(
327
- new IAPError({
328
- code: IAPErrorCode.STORE_ERROR,
329
- message: `order() rejected for ${opts.productId}.`,
330
- cause
331
- })
332
- );
333
- });
334
- });
272
+ return normalizeTransaction(tx, opts.productType);
335
273
  }
336
274
  async getOwnedTransactions() {
337
- const store = await this.ensureStore();
338
- await store.restorePurchases();
339
- const out = [];
340
- for (const tx of store.localTransactions) {
341
- if (tx.state !== getCdv().TransactionState.APPROVED) continue;
342
- const token = transactionToken(tx);
343
- if (!token) continue;
344
- const normalized = normalizeTransaction(tx, inferProductType(tx, this.products), token);
345
- this.pendingFinish.set(token, tx);
346
- out.push(normalized);
347
- }
348
- return out;
275
+ const result = await NativePurchases.getPurchases();
276
+ return result.purchases.filter((tx) => tx.purchaseState === void 0 || tx.purchaseState === "1").map((tx) => normalizeTransaction(tx, inferProductType(tx)));
349
277
  }
350
278
  async acknowledge(transaction) {
351
- const cdvTx = this.pendingFinish.get(transaction.token);
352
- if (!cdvTx) {
353
- return;
354
- }
355
279
  try {
356
- await cdvTx.finish();
357
- } catch (cause) {
280
+ await NativePurchases.acknowledgePurchase({
281
+ purchaseToken: transaction.token
282
+ });
283
+ } catch (error) {
358
284
  throw new IAPError({
359
285
  code: IAPErrorCode.STORE_ERROR,
360
- message: `Failed to finish transaction for ${transaction.productId}.`,
361
- cause,
286
+ message: `Failed to acknowledge transaction for ${transaction.productId}.`,
287
+ cause: error,
362
288
  recoverable: true
363
289
  });
364
290
  }
365
- this.pendingFinish.delete(transaction.token);
366
291
  }
367
292
  async manageSubscriptions() {
368
- const store = await this.ensureStore();
369
- const err = await store.manageSubscriptions();
370
- if (err) {
293
+ try {
294
+ await NativePurchases.manageSubscriptions();
295
+ } catch (error) {
371
296
  throw new IAPError({
372
297
  code: IAPErrorCode.STORE_ERROR,
373
- message: err.message ?? "Failed to open subscription management."
298
+ message: "Failed to open the native subscription management UI.",
299
+ cause: error
374
300
  });
375
301
  }
376
302
  }
377
303
  async dispose() {
378
- if (this.bootstrapApprovedHandler) {
379
- try {
380
- const cdv = globalThis.CdvPurchase;
381
- cdv?.store?.off(this.bootstrapApprovedHandler);
382
- } catch {
383
- }
384
- this.bootstrapApprovedHandler = null;
385
- }
386
- this.pendingFinish.clear();
387
- this.bootstrapped = false;
388
- this.bootstrapping = null;
389
- }
390
- // ----- internals -----
391
- async ensureStore() {
392
- await this.bootstrap();
393
- return getCdv().store;
394
- }
395
- bootstrap() {
396
- if (this.bootstrapped) return Promise.resolve();
397
- if (this.bootstrapping) return this.bootstrapping;
398
- this.bootstrapping = (async () => {
399
- const cdv = getCdv();
400
- const platform = currentCdvPlatform();
401
- cdv.store.register(
402
- this.products.map((p) => ({
403
- id: p.id,
404
- type: mapProductType(p.type),
405
- platform
406
- }))
407
- );
408
- const errors = await cdv.store.initialize([platform]);
409
- if (errors && errors.length > 0) {
410
- const first = errors[0];
411
- throw new IAPError({
412
- code: IAPErrorCode.BILLING_NOT_AVAILABLE,
413
- message: first?.message ?? "cordova-plugin-purchase initialize() reported errors."
414
- });
415
- }
416
- const handler = (tx) => {
417
- const token = transactionToken(tx);
418
- if (!token) return;
419
- if (!this.pendingFinish.has(token)) {
420
- this.pendingFinish.set(token, tx);
421
- }
422
- };
423
- this.bootstrapApprovedHandler = handler;
424
- cdv.store.when().approved(handler);
425
- await cdv.store.update();
426
- this.bootstrapped = true;
427
- })();
428
- return this.bootstrapping;
429
304
  }
430
305
  };
431
306
  }
@@ -958,11 +833,11 @@ var WebStubAdapter = class {
958
833
  };
959
834
 
960
835
  // src/adapters/native/index.ts
961
- async function selectNativeAdapter(options) {
836
+ async function selectNativeAdapter() {
962
837
  const platform = getPlatform();
963
838
  if (platform === "ios" || platform === "android") {
964
839
  const mod = await Promise.resolve().then(() => (init_native_adapter(), native_adapter_exports));
965
- return new mod.CdvNativeAdapter({ products: options.products });
840
+ return new mod.CapgoNativeAdapter();
966
841
  }
967
842
  return new WebStubAdapter();
968
843
  }
@@ -1860,6 +1735,24 @@ function createIAP(input) {
1860
1735
  cachedAt: null,
1861
1736
  products: Object.freeze([...config.products ?? []])
1862
1737
  };
1738
+ async function refreshEntitlements() {
1739
+ requireInitialized(state);
1740
+ const previous = state.entitlements;
1741
+ const fetched = await state.backend.getEntitlements();
1742
+ const next = freezeAll(fetched);
1743
+ try {
1744
+ state.cachedAt = await state.cache.save(next);
1745
+ } catch (error) {
1746
+ state.logger.warn(
1747
+ "Failed to persist refreshed entitlements; in-memory state still updated.",
1748
+ error
1749
+ );
1750
+ }
1751
+ state.entitlements = next;
1752
+ if (!entitlementsEqual(previous, next)) {
1753
+ state.emitter.emit("entitlements-changed", { entitlements: next, previous });
1754
+ }
1755
+ }
1863
1756
  return {
1864
1757
  async initialize() {
1865
1758
  if (state.destroyed) {
@@ -1900,7 +1793,7 @@ function createIAP(input) {
1900
1793
  state.products = Object.freeze([...validated.data]);
1901
1794
  state.logger.debug(`Resolved ${validated.data.length} product(s) from backend manifest.`);
1902
1795
  }
1903
- state.adapter = await selectNativeAdapter({ products: state.products });
1796
+ state.adapter = await selectNativeAdapter();
1904
1797
  const configGetAuthHeaders = state.config.backend.getAuthHeaders;
1905
1798
  const getAuthHeaders = configGetAuthHeaders ? async () => configGetAuthHeaders() : async () => ({});
1906
1799
  const sharedDeps = {
@@ -1961,7 +1854,7 @@ function createIAP(input) {
1961
1854
  logger: state.logger,
1962
1855
  onResume: async () => {
1963
1856
  try {
1964
- await this.refresh();
1857
+ await refreshEntitlements();
1965
1858
  } catch (error) {
1966
1859
  state.logger.warn("refreshOnResume: refresh() failed.", error);
1967
1860
  }
@@ -1972,7 +1865,7 @@ function createIAP(input) {
1972
1865
  state.logger.debug("Cache exceeds TTL; scheduling background refresh.");
1973
1866
  queueMicrotask(() => {
1974
1867
  if (!state.initialized || state.destroyed) return;
1975
- this.refresh().catch((error) => {
1868
+ refreshEntitlements().catch((error) => {
1976
1869
  state.logger.warn("TTL background refresh failed.", error);
1977
1870
  });
1978
1871
  });
@@ -1980,24 +1873,7 @@ function createIAP(input) {
1980
1873
  state.initialized = true;
1981
1874
  state.emitter.emit("ready", void 0);
1982
1875
  },
1983
- async refresh() {
1984
- requireInitialized(state);
1985
- const previous = state.entitlements;
1986
- const fetched = await state.backend.getEntitlements();
1987
- const next = freezeAll(fetched);
1988
- try {
1989
- state.cachedAt = await state.cache.save(next);
1990
- } catch (error) {
1991
- state.logger.warn(
1992
- "Failed to persist refreshed entitlements; in-memory state still updated.",
1993
- error
1994
- );
1995
- }
1996
- state.entitlements = next;
1997
- if (!entitlementsEqual(previous, next)) {
1998
- state.emitter.emit("entitlements-changed", { entitlements: next, previous });
1999
- }
2000
- },
1876
+ refresh: refreshEntitlements,
2001
1877
  async destroy() {
2002
1878
  if (state.destroyed) return;
2003
1879
  state.destroyed = true;