capacitor-plugin-cdv-purchase 13.14.0 → 13.15.1

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.js CHANGED
@@ -877,26 +877,31 @@ var CdvPurchase;
877
877
  if (initResult === null || initResult === void 0 ? void 0 : initResult.code)
878
878
  return initResult;
879
879
  log.info(`${adapter.name} products: ${JSON.stringify(platformProducts)}`);
880
- if (platformProducts.length === 0)
881
- return;
880
+ // Storefront refresh runs in parallel with product / receipt loading.
881
+ // Failure or timeout is absorbed so it never blocks store readiness.
882
+ const storefrontRefresh = context.storefronts.refreshWith(adapter).catch(() => { });
882
883
  let loadProductsResult = [];
883
884
  let loadReceiptsResult = [];
884
- if (adapter.supportsParallelLoading) {
885
- [loadProductsResult, loadReceiptsResult] = yield Promise.all([
886
- adapter.loadProducts(platformProducts),
887
- adapter.loadReceipts()
888
- ]);
889
- }
890
- else {
891
- loadProductsResult = yield adapter.loadProducts(platformProducts);
892
- loadReceiptsResult = yield adapter.loadReceipts();
885
+ if (platformProducts.length > 0) {
886
+ if (adapter.supportsParallelLoading) {
887
+ [loadProductsResult, loadReceiptsResult] = yield Promise.all([
888
+ adapter.loadProducts(platformProducts),
889
+ adapter.loadReceipts()
890
+ ]);
891
+ }
892
+ else {
893
+ loadProductsResult = yield adapter.loadProducts(platformProducts);
894
+ loadReceiptsResult = yield adapter.loadReceipts();
895
+ }
896
+ log.info(`${adapter.name} products loaded: ${JSON.stringify(loadProductsResult)}`);
897
+ const loadedProducts = loadProductsResult.filter(p => p instanceof CdvPurchase.Product);
898
+ context.listener.productsUpdated(platformToInit.platform, loadedProducts);
899
+ log.info(`${adapter.name} receipts loaded: ${JSON.stringify(loadReceiptsResult)}`);
893
900
  }
894
- // const loadProductsResult = await adapter.loadProducts(platformProducts);
895
- log.info(`${adapter.name} products loaded: ${JSON.stringify(loadProductsResult)}`);
896
- const loadedProducts = loadProductsResult.filter(p => p instanceof CdvPurchase.Product);
897
- context.listener.productsUpdated(platformToInit.platform, loadedProducts);
898
- // const loadReceiptsResult = await adapter.loadReceipts();
899
- log.info(`${adapter.name} receipts loaded: ${JSON.stringify(loadReceiptsResult)}`);
901
+ // Wait for the storefront refresh (or its timeout) before returning.
902
+ // This intentionally delays storeReady by up to the timeout duration
903
+ // so that store.getStorefront() has a value once the store is ready.
904
+ yield storefrontRefresh;
900
905
  return loadProductsResult.filter(lr => 'code' in lr && 'message' in lr)[0];
901
906
  })));
902
907
  return result.filter(err => err);
@@ -930,6 +935,95 @@ var CdvPurchase;
930
935
  })(CdvPurchase.Internal || (CdvPurchase.Internal = {}));
931
936
  })(CdvPurchase || (CdvPurchase = {}));
932
937
  var CdvPurchase;
938
+ (function (CdvPurchase) {
939
+ (function (Internal) {
940
+ /** Default timeout for a storefront refresh call, in milliseconds. */
941
+ const DEFAULT_STOREFRONT_REFRESH_TIMEOUT_MS = 2000;
942
+ /**
943
+ * Collection of per-platform storefront country codes.
944
+ *
945
+ * Maintains the cached value for each platform that exposes one and
946
+ * notifies listeners when a value changes. Adapter-agnostic — callers
947
+ * are responsible for validating that a platform has a ready adapter.
948
+ */
949
+ class Storefronts {
950
+ constructor(logger) {
951
+ /** Cached country code per platform. */
952
+ this.values = {};
953
+ this.callbacks = new Internal.Callbacks(logger, 'storefrontUpdated()');
954
+ }
955
+ /**
956
+ * Refresh the cached value for a given adapter.
957
+ *
958
+ * The returned promise:
959
+ * - resolves when the adapter responds within `timeoutMs`
960
+ * - rejects with a timeout error otherwise
961
+ *
962
+ * Regardless of timeout, if the adapter eventually yields a value,
963
+ * the cache is silently updated and listeners are notified.
964
+ * A failed or empty response never overwrites the cache.
965
+ */
966
+ refreshWith(adapter, timeoutMs = DEFAULT_STOREFRONT_REFRESH_TIMEOUT_MS) {
967
+ return __awaiter(this, void 0, void 0, function* () {
968
+ if (!adapter.getStorefront)
969
+ return;
970
+ const platform = adapter.id;
971
+ // Start the fetch; handle result + errors independently of the race.
972
+ const fetch = adapter.getStorefront()
973
+ .then(code => { if (code)
974
+ this.setValue(platform, code); })
975
+ .catch(() => { });
976
+ let timerId;
977
+ const timeout = new Promise((_, reject) => {
978
+ timerId = setTimeout(() => reject(new Error('storefront refresh timeout')), timeoutMs);
979
+ });
980
+ try {
981
+ yield Promise.race([fetch, timeout]);
982
+ }
983
+ finally {
984
+ clearTimeout(timerId);
985
+ }
986
+ });
987
+ }
988
+ /**
989
+ * Retrieve a storefront value.
990
+ *
991
+ * - With a platform: always returns `{ platform, countryCode }`,
992
+ * where `countryCode` may be undefined if nothing is cached.
993
+ * - Without a platform: returns the first cached non-empty
994
+ * storefront, or `undefined` if nothing is cached.
995
+ */
996
+ getValueFor(platform) {
997
+ if (platform) {
998
+ return { platform, countryCode: this.values[platform] };
999
+ }
1000
+ for (const p of Object.keys(this.values)) {
1001
+ if (this.values[p]) {
1002
+ return { platform: p, countryCode: this.values[p] };
1003
+ }
1004
+ }
1005
+ return undefined;
1006
+ }
1007
+ /** Register a change listener. */
1008
+ listen(cb, callbackName) {
1009
+ this.callbacks.push(cb, callbackName);
1010
+ }
1011
+ /** Remove a previously registered listener. */
1012
+ off(cb) {
1013
+ this.callbacks.remove(cb);
1014
+ }
1015
+ /** Update the cache and notify listeners on change. */
1016
+ setValue(platform, countryCode) {
1017
+ if (this.values[platform] === countryCode)
1018
+ return;
1019
+ this.values[platform] = countryCode;
1020
+ this.callbacks.trigger({ platform, countryCode }, 'storefront_changed');
1021
+ }
1022
+ }
1023
+ Internal.Storefronts = Storefronts;
1024
+ })(CdvPurchase.Internal || (CdvPurchase.Internal = {}));
1025
+ })(CdvPurchase || (CdvPurchase = {}));
1026
+ var CdvPurchase;
933
1027
  (function (CdvPurchase) {
934
1028
  (function (Internal) {
935
1029
  /**
@@ -1426,6 +1520,7 @@ var CdvPurchase;
1426
1520
  /// <reference path="validator/validator.ts" />
1427
1521
  /// <reference path="log.ts" />
1428
1522
  /// <reference path="internal/adapters.ts" />
1523
+ /// <reference path="internal/storefronts.ts" />
1429
1524
  /// <reference path="internal/adapter-listener.ts" />
1430
1525
  /// <reference path="internal/callbacks.ts" />
1431
1526
  /// <reference path="internal/ready.ts" />
@@ -1466,7 +1561,7 @@ var CdvPurchase;
1466
1561
  /**
1467
1562
  * Current release number of the plugin.
1468
1563
  */
1469
- CdvPurchase.PLUGIN_VERSION = '13.14.0';
1564
+ CdvPurchase.PLUGIN_VERSION = '13.15.1';
1470
1565
  /**
1471
1566
  * Entry class of the plugin.
1472
1567
  */
@@ -1524,6 +1619,8 @@ var CdvPurchase;
1524
1619
  this.receiptsVerifiedCallbacks = new CdvPurchase.Internal.Callbacks(this.log, 'receiptsVerified()', true);
1525
1620
  /** Callbacks for errors */
1526
1621
  this.errorCallbacks = new CdvPurchase.Internal.Callbacks(this.log, 'error()');
1622
+ /** Per-platform storefront cache and change notifications. */
1623
+ this._storefronts = new CdvPurchase.Internal.Storefronts(this.log.child('Storefronts'));
1527
1624
  this.initializedHasBeenCalled = false;
1528
1625
  /** Stores the last time the store was updated (or initialized), to skip calls in quick succession. */
1529
1626
  this.lastUpdate = 0;
@@ -1667,6 +1764,7 @@ var CdvPurchase;
1667
1764
  get listener() { return store.listener; },
1668
1765
  get log() { return store.log; },
1669
1766
  get registeredProducts() { return store.registeredProducts; },
1767
+ get storefronts() { return store._storefronts; },
1670
1768
  apiDecorators: {
1671
1769
  canPurchase: this.canPurchase.bind(this),
1672
1770
  owned: this.owned.bind(this),
@@ -1692,7 +1790,6 @@ var CdvPurchase;
1692
1790
  * Call to refresh the price of products and status of purchases.
1693
1791
  */
1694
1792
  update() {
1695
- var _a;
1696
1793
  return __awaiter(this, void 0, void 0, function* () {
1697
1794
  this.log.info('update()');
1698
1795
  if (!this._readyCallbacks.isReady) {
@@ -1707,11 +1804,15 @@ var CdvPurchase;
1707
1804
  this.lastUpdate = now;
1708
1805
  // Load products metadata
1709
1806
  for (const registration of this.registeredProducts.byPlatform()) {
1710
- const products = yield ((_a = this.adapters.findReady(registration.platform)) === null || _a === void 0 ? void 0 : _a.loadProducts(registration.products));
1807
+ const adapter = this.adapters.findReady(registration.platform);
1808
+ const products = yield (adapter === null || adapter === void 0 ? void 0 : adapter.loadProducts(registration.products));
1711
1809
  products === null || products === void 0 ? void 0 : products.forEach(p => {
1712
1810
  if (p instanceof CdvPurchase.Product)
1713
1811
  this.updatedCallbacks.trigger(p, 'update_has_loaded_products');
1714
1812
  });
1813
+ if (adapter) {
1814
+ this._storefronts.refreshWith(adapter).catch(() => { });
1815
+ }
1715
1816
  }
1716
1817
  });
1717
1818
  }
@@ -1763,6 +1864,7 @@ var CdvPurchase;
1763
1864
  unverified: (cb, callbackName) => (this.unverifiedCallbacks.push(cb, callbackName), ret),
1764
1865
  receiptsReady: (cb, callbackName) => (this.receiptsReadyCallbacks.push(cb, callbackName), ret),
1765
1866
  receiptsVerified: (cb, callbackName) => (this.receiptsVerifiedCallbacks.push(cb, callbackName), ret),
1867
+ storefrontUpdated: (cb, callbackName) => (this._storefronts.listen(cb, callbackName), ret),
1766
1868
  };
1767
1869
  return ret;
1768
1870
  }
@@ -1781,6 +1883,7 @@ var CdvPurchase;
1781
1883
  this.receiptsVerifiedCallbacks.remove(callback);
1782
1884
  this.errorCallbacks.remove(callback);
1783
1885
  this._readyCallbacks.remove(callback);
1886
+ this._storefronts.off(callback);
1784
1887
  }
1785
1888
  /**
1786
1889
  * Setup a function to be notified of changes to a transaction state.
@@ -1896,6 +1999,8 @@ var CdvPurchase;
1896
1999
  const ret = yield adapter.order(offer, additionalData || {});
1897
2000
  if (ret && 'isError' in ret)
1898
2001
  CdvPurchase.store.triggerError(ret);
2002
+ // Account may have switched during checkout — refresh storefront in the background.
2003
+ this._storefronts.refreshWith(adapter).catch(() => { });
1899
2004
  return ret;
1900
2005
  });
1901
2006
  }
@@ -1952,6 +2057,7 @@ var CdvPurchase;
1952
2057
  }
1953
2058
  const promise = new CdvPurchase.PaymentRequestPromise();
1954
2059
  adapter.requestPayment(paymentRequest, additionalData).then(result => {
2060
+ this._storefronts.refreshWith(adapter).catch(() => { });
1955
2061
  promise.trigger(result);
1956
2062
  if (result instanceof CdvPurchase.Transaction) {
1957
2063
  const onStateChange = (state) => {
@@ -2052,6 +2158,8 @@ var CdvPurchase;
2052
2158
  for (const adapter of this.adapters.list) {
2053
2159
  if (adapter.ready) {
2054
2160
  error = error !== null && error !== void 0 ? error : yield adapter.restorePurchases();
2161
+ // Restore often implies a login or account switch — refresh storefront.
2162
+ this._storefronts.refreshWith(adapter).catch(() => { });
2055
2163
  }
2056
2164
  }
2057
2165
  return error;
@@ -2095,6 +2203,43 @@ var CdvPurchase;
2095
2203
  return adapter.manageBilling();
2096
2204
  });
2097
2205
  }
2206
+ /**
2207
+ * Retrieve the billing country code from the platform's storefront.
2208
+ *
2209
+ * Returns a `Storefront` object with the platform and its ISO 3166-1
2210
+ * alpha-2 country code (e.g., "US", "FR"). The country code may be
2211
+ * undefined if the underlying fetch has not yet completed or failed —
2212
+ * the platform is still reported. Returns `undefined` only when no
2213
+ * matching adapter is ready.
2214
+ *
2215
+ * The cache is populated before the `storeReady` event fires (with a
2216
+ * best-effort timeout), and refreshed after orders and `restorePurchases()`.
2217
+ *
2218
+ * @param platform - Optional platform. If omitted, returns the first
2219
+ * cached non-empty storefront, or a `{ platform, countryCode: undefined }`
2220
+ * object for the first ready adapter.
2221
+ *
2222
+ * @example
2223
+ * const storefront = store.getStorefront();
2224
+ * if (storefront?.countryCode) {
2225
+ * console.log(`Billing country: ${storefront.countryCode}`);
2226
+ * }
2227
+ */
2228
+ getStorefront(platform) {
2229
+ if (platform) {
2230
+ const adapter = this.adapters.findReady(platform);
2231
+ if (!adapter)
2232
+ return undefined;
2233
+ return this._storefronts.getValueFor(platform);
2234
+ }
2235
+ const cached = this._storefronts.getValueFor();
2236
+ if (cached)
2237
+ return cached;
2238
+ const firstReady = this.adapters.findReady();
2239
+ if (!firstReady)
2240
+ return undefined;
2241
+ return { platform: firstReady.id, countryCode: undefined };
2242
+ }
2098
2243
  /**
2099
2244
  * The default payment platform to use depending on the OS.
2100
2245
  *
@@ -2955,14 +3100,15 @@ var CdvPurchase;
2955
3100
  ready: () => {
2956
3101
  this.log.info('ready');
2957
3102
  },
2958
- purchased: (transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation) => __awaiter(this, void 0, void 0, function* () {
3103
+ purchased: (transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity) => __awaiter(this, void 0, void 0, function* () {
2959
3104
  this.log.info('purchase: id:' + transactionIdentifier + ' product:' + productId +
2960
3105
  ' originalTransaction:' + originalTransactionIdentifier +
2961
3106
  ' - date:' + transactionDate + ' - discount:' + discountId +
2962
- (jwsRepresentation ? ' - jws:present' : ''));
3107
+ (jwsRepresentation ? ' - jws:present' : '') +
3108
+ (quantity && quantity > 1 ? ' - quantity:' + quantity : ''));
2963
3109
  // we can add the transaction to the receipt here
2964
3110
  const transaction = yield this.upsertTransaction(productId, transactionIdentifier, CdvPurchase.TransactionState.APPROVED);
2965
- transaction.refresh(productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation);
3111
+ transaction.refresh(productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity);
2966
3112
  this.removeTransactionInProgress(productId);
2967
3113
  this.receiptsUpdated.call();
2968
3114
  this.callPaymentMonitor('purchased');
@@ -2977,12 +3123,7 @@ var CdvPurchase;
2977
3123
  this.log.info('purchaseFailed: ' + productId + ' - ' + code + ' - ' + message);
2978
3124
  this.removeTransactionInProgress(productId);
2979
3125
  this.context.listener.receiptsUpdated(CdvPurchase.Platform.APPLE_APPSTORE, [this.pseudoReceipt]);
2980
- if (code === CdvPurchase.ErrorCode.PAYMENT_CANCELLED) {
2981
- this.callPaymentMonitor('cancelled', code, message);
2982
- }
2983
- else {
2984
- this.callPaymentMonitor('failed', code, message);
2985
- }
3126
+ this.callPaymentMonitor('failed', code, message);
2986
3127
  },
2987
3128
  purchasing: (productId) => __awaiter(this, void 0, void 0, function* () {
2988
3129
  // purchase has been requested, but there's no transactionIdentifier yet.
@@ -3018,10 +3159,10 @@ var CdvPurchase;
3018
3159
  yield this.upsertTransaction(productId, transactionIdentifier, CdvPurchase.TransactionState.FINISHED);
3019
3160
  this.receiptsUpdated.call();
3020
3161
  }),
3021
- restored: (transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation) => __awaiter(this, void 0, void 0, function* () {
3162
+ restored: (transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity) => __awaiter(this, void 0, void 0, function* () {
3022
3163
  this.log.info('restore: ' + transactionIdentifier + ' - ' + productId);
3023
3164
  const transaction = yield this.upsertTransaction(productId, transactionIdentifier, CdvPurchase.TransactionState.APPROVED);
3024
- transaction.refresh(productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation);
3165
+ transaction.refresh(productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity);
3025
3166
  this.receiptsUpdated.call();
3026
3167
  }),
3027
3168
  receiptsRefreshed: (receipt) => {
@@ -3268,7 +3409,7 @@ var CdvPurchase;
3268
3409
  return __awaiter(this, void 0, void 0, function* () {
3269
3410
  let resolved = false;
3270
3411
  return new Promise(resolve => {
3271
- var _a;
3412
+ var _a, _b;
3272
3413
  const callResolve = (result) => {
3273
3414
  if (resolved)
3274
3415
  return;
@@ -3277,8 +3418,12 @@ var CdvPurchase;
3277
3418
  resolve(result);
3278
3419
  };
3279
3420
  this.log.info('order');
3421
+ const quantity = (_a = additionalData === null || additionalData === void 0 ? void 0 : additionalData.quantity) !== null && _a !== void 0 ? _a : 1;
3422
+ if (quantity < 1 || quantity > 10 || !Number.isInteger(quantity)) {
3423
+ return callResolve(appStoreError(CdvPurchase.ErrorCode.PURCHASE, 'Invalid quantity: must be an integer between 1 and 10', offer.productId));
3424
+ }
3280
3425
  const discountId = offer.id !== AppleAppStore.DEFAULT_OFFER_ID ? offer.id : undefined;
3281
- const discount = (_a = additionalData === null || additionalData === void 0 ? void 0 : additionalData.appStore) === null || _a === void 0 ? void 0 : _a.discount;
3426
+ const discount = (_b = additionalData === null || additionalData === void 0 ? void 0 : additionalData.appStore) === null || _b === void 0 ? void 0 : _b.discount;
3282
3427
  if (discountId && !discount) {
3283
3428
  return callResolve(appStoreError(CdvPurchase.ErrorCode.MISSING_OFFER_PARAMS, 'Missing additionalData.appStore.discount when ordering a discount offer', offer.productId));
3284
3429
  }
@@ -3317,7 +3462,7 @@ var CdvPurchase;
3317
3462
  // When we switch AppStore user, the cached receipt isn't from the new user.
3318
3463
  // so after a purchase, we want to make sure we're using the receipt from the logged in user.
3319
3464
  this.forceReceiptReload = true;
3320
- this.bridge.purchase(offer.productId, 1, this.context.getApplicationUsername(), discount, success, error);
3465
+ this.bridge.purchase(offer.productId, quantity, this.context.getApplicationUsername(), discount, success, error);
3321
3466
  });
3322
3467
  });
3323
3468
  }
@@ -3467,7 +3612,7 @@ var CdvPurchase;
3467
3612
  if (functionality === 'order')
3468
3613
  return this._canMakePayments;
3469
3614
  const supported = [
3470
- 'order', 'manageBilling', 'manageSubscriptions'
3615
+ 'order', 'orderQuantity', 'manageBilling', 'manageSubscriptions', 'getStorefront'
3471
3616
  ];
3472
3617
  return supported.indexOf(functionality) >= 0;
3473
3618
  }
@@ -3490,8 +3635,83 @@ var CdvPurchase;
3490
3635
  this.bridge.presentCodeRedemptionSheet(resolve);
3491
3636
  });
3492
3637
  }
3638
+ getStorefront() {
3639
+ return __awaiter(this, void 0, void 0, function* () {
3640
+ if (!this.bridge.getStorefront)
3641
+ return undefined;
3642
+ const countryCode = yield this.bridge.getStorefront();
3643
+ if (!countryCode)
3644
+ return undefined;
3645
+ // SKStorefront.countryCode typically returns ISO 3166-1 alpha-3 (e.g., "USA").
3646
+ // The fallback `|| countryCode` handles cases where Apple returns alpha-2 directly
3647
+ // or uses a non-standard code (e.g., territories not in ISO 3166-1).
3648
+ return isoAlpha3ToAlpha2(countryCode) || countryCode;
3649
+ });
3650
+ }
3493
3651
  }
3494
3652
  AppleAppStore.Adapter = Adapter;
3653
+ /**
3654
+ * Convert ISO 3166-1 alpha-3 country code to alpha-2.
3655
+ *
3656
+ * Apple's SKStorefront.countryCode returns alpha-3 codes (e.g., "USA").
3657
+ * This function converts them to the more common alpha-2 format (e.g., "US")
3658
+ * for consistency with Google Play which already returns alpha-2.
3659
+ */
3660
+ const ISO_ALPHA3_TO_ALPHA2 = {
3661
+ AFG: 'AF', ALB: 'AL', DZA: 'DZ', ASM: 'AS', AND: 'AD',
3662
+ AGO: 'AO', AIA: 'AI', ATA: 'AQ', ATG: 'AG', ARG: 'AR',
3663
+ ARM: 'AM', ABW: 'AW', AUS: 'AU', AUT: 'AT', AZE: 'AZ',
3664
+ BHS: 'BS', BHR: 'BH', BGD: 'BD', BRB: 'BB', BLR: 'BY',
3665
+ BEL: 'BE', BLZ: 'BZ', BEN: 'BJ', BMU: 'BM', BTN: 'BT',
3666
+ BOL: 'BO', BES: 'BQ', BIH: 'BA', BWA: 'BW', BVT: 'BV',
3667
+ BRA: 'BR', IOT: 'IO', BRN: 'BN', BGR: 'BG', BFA: 'BF',
3668
+ BDI: 'BI', CPV: 'CV', KHM: 'KH', CMR: 'CM', CAN: 'CA',
3669
+ CYM: 'KY', CAF: 'CF', TCD: 'TD', CHL: 'CL', CHN: 'CN',
3670
+ CXR: 'CX', CCK: 'CC', COL: 'CO', COM: 'KM', COG: 'CG',
3671
+ COD: 'CD', COK: 'CK', CRI: 'CR', CIV: 'CI', HRV: 'HR',
3672
+ CUB: 'CU', CUW: 'CW', CYP: 'CY', CZE: 'CZ', DNK: 'DK',
3673
+ DJI: 'DJ', DMA: 'DM', DOM: 'DO', ECU: 'EC', EGY: 'EG',
3674
+ SLV: 'SV', GNQ: 'GQ', ERI: 'ER', EST: 'EE', SWZ: 'SZ',
3675
+ ETH: 'ET', FLK: 'FK', FRO: 'FO', FJI: 'FJ', FIN: 'FI',
3676
+ FRA: 'FR', GUF: 'GF', PYF: 'PF', ATF: 'TF', GAB: 'GA',
3677
+ GMB: 'GM', GEO: 'GE', DEU: 'DE', GHA: 'GH', GIB: 'GI',
3678
+ GRC: 'GR', GRL: 'GL', GRD: 'GD', GLP: 'GP', GUM: 'GU',
3679
+ GTM: 'GT', GGY: 'GG', GIN: 'GN', GNB: 'GW', GUY: 'GY',
3680
+ HTI: 'HT', HMD: 'HM', VAT: 'VA', HND: 'HN', HKG: 'HK',
3681
+ HUN: 'HU', ISL: 'IS', IND: 'IN', IDN: 'ID', IRN: 'IR',
3682
+ IRQ: 'IQ', IRL: 'IE', IMN: 'IM', ISR: 'IL', ITA: 'IT',
3683
+ JAM: 'JM', JPN: 'JP', JEY: 'JE', JOR: 'JO', KAZ: 'KZ',
3684
+ KEN: 'KE', KIR: 'KI', PRK: 'KP', KOR: 'KR', KWT: 'KW',
3685
+ KGZ: 'KG', LAO: 'LA', LVA: 'LV', LBN: 'LB', LSO: 'LS',
3686
+ LBR: 'LR', LBY: 'LY', LIE: 'LI', LTU: 'LT', LUX: 'LU',
3687
+ MAC: 'MO', MDG: 'MG', MWI: 'MW', MYS: 'MY', MDV: 'MV',
3688
+ MLI: 'ML', MLT: 'MT', MHL: 'MH', MTQ: 'MQ', MRT: 'MR',
3689
+ MUS: 'MU', MYT: 'YT', MEX: 'MX', FSM: 'FM', MDA: 'MD',
3690
+ MCO: 'MC', MNG: 'MN', MNE: 'ME', MSR: 'MS', MAR: 'MA',
3691
+ MOZ: 'MZ', MMR: 'MM', NAM: 'NA', NRU: 'NR', NPL: 'NP',
3692
+ NLD: 'NL', NCL: 'NC', NZL: 'NZ', NIC: 'NI', NER: 'NE',
3693
+ NGA: 'NG', NIU: 'NU', NFK: 'NF', MKD: 'MK', MNP: 'MP',
3694
+ NOR: 'NO', OMN: 'OM', PAK: 'PK', PLW: 'PW', PSE: 'PS',
3695
+ PAN: 'PA', PNG: 'PG', PRY: 'PY', PER: 'PE', PHL: 'PH',
3696
+ PCN: 'PN', POL: 'PL', PRT: 'PT', PRI: 'PR', QAT: 'QA',
3697
+ REU: 'RE', ROU: 'RO', RUS: 'RU', RWA: 'RW', BLM: 'BL',
3698
+ SHN: 'SH', KNA: 'KN', LCA: 'LC', MAF: 'MF', SPM: 'PM',
3699
+ VCT: 'VC', WSM: 'WS', SMR: 'SM', STP: 'ST', SAU: 'SA',
3700
+ SEN: 'SN', SRB: 'RS', SYC: 'SC', SLE: 'SL', SGP: 'SG',
3701
+ SXM: 'SX', SVK: 'SK', SVN: 'SI', SLB: 'SB', SOM: 'SO',
3702
+ ZAF: 'ZA', SGS: 'GS', SSD: 'SS', ESP: 'ES', LKA: 'LK',
3703
+ SDN: 'SD', SUR: 'SR', SJM: 'SJ', SWE: 'SE', CHE: 'CH',
3704
+ SYR: 'SY', TWN: 'TW', TJK: 'TJ', TZA: 'TZ', THA: 'TH',
3705
+ TLS: 'TL', TGO: 'TG', TKL: 'TK', TON: 'TO', TTO: 'TT',
3706
+ TUN: 'TN', TUR: 'TR', TKM: 'TM', TCA: 'TC', TUV: 'TV',
3707
+ UGA: 'UG', UKR: 'UA', ARE: 'AE', GBR: 'GB', USA: 'US',
3708
+ UMI: 'UM', URY: 'UY', UZB: 'UZ', VUT: 'VU', VEN: 'VE',
3709
+ VNM: 'VN', VGB: 'VG', VIR: 'VI', WLF: 'WF', ESH: 'EH',
3710
+ YEM: 'YE', ZMB: 'ZM', ZWE: 'ZW',
3711
+ };
3712
+ function isoAlpha3ToAlpha2(alpha3) {
3713
+ return ISO_ALPHA3_TO_ALPHA2[alpha3.toUpperCase()];
3714
+ }
3495
3715
  function appStoreError(code, message, productId) {
3496
3716
  return CdvPurchase.storeError(code, message, CdvPurchase.Platform.APPLE_APPSTORE, productId);
3497
3717
  }
@@ -3501,6 +3721,9 @@ var CdvPurchase;
3501
3721
  (function (CdvPurchase) {
3502
3722
  (function (AppleAppStore) {
3503
3723
  (function (CapacitorBridge) {
3724
+ let log = function log(msg) {
3725
+ console.log("StoreKit[capacitor]: " + msg);
3726
+ };
3504
3727
  /**
3505
3728
  * Capacitor implementation of the Apple AppStore bridge.
3506
3729
  * Uses Capacitor.Plugins.PurchasePlugin with StoreKit 2 (iOS 15+).
@@ -3537,7 +3760,7 @@ var CdvPurchase;
3537
3760
  }
3538
3761
  init(options, success, error) {
3539
3762
  if (options.log)
3540
- options.log;
3763
+ log = options.log;
3541
3764
  // Merge provided options over defaults (noop callbacks)
3542
3765
  this.options = Object.assign(Object.assign({}, this.options), options);
3543
3766
  const plugin = this.plugin;
@@ -3547,7 +3770,7 @@ var CdvPurchase;
3547
3770
  }
3548
3771
  // Listen for transaction updates from native
3549
3772
  plugin.addListener('transactionUpdated', (data) => {
3550
- this.transactionUpdated(data.state, data.errorCode, data.errorText, data.transactionIdentifier, data.productId, data.transactionReceipt, data.originalTransactionIdentifier, data.transactionDate, data.discountId, data.expirationDate, data.jwsRepresentation);
3773
+ this.transactionUpdated(data.state, data.errorCode, data.errorText, data.transactionIdentifier, data.productId, data.transactionReceipt, data.originalTransactionIdentifier, data.transactionDate, data.discountId, data.expirationDate, data.jwsRepresentation, data.quantity);
3551
3774
  });
3552
3775
  plugin.addListener('restoreCompleted', () => {
3553
3776
  this.restoreCompletedTransactionsFinished();
@@ -3568,7 +3791,7 @@ var CdvPurchase;
3568
3791
  const pending = this.pendingTransactionUpdates;
3569
3792
  this.pendingTransactionUpdates = [];
3570
3793
  for (const args of pending) {
3571
- this.transactionUpdated(args.state, args.errorCode, args.errorText, args.transactionIdentifier, args.productId, args.transactionReceipt, args.originalTransactionIdentifier, args.transactionDate, args.discountId, args.expirationDate, args.jwsRepresentation);
3794
+ this.transactionUpdated(args.state, args.errorCode, args.errorText, args.transactionIdentifier, args.productId, args.transactionReceipt, args.originalTransactionIdentifier, args.transactionDate, args.discountId, args.expirationDate, args.jwsRepresentation, args.quantity);
3572
3795
  }
3573
3796
  if (this.options.ready)
3574
3797
  this.options.ready();
@@ -3652,12 +3875,13 @@ var CdvPurchase;
3652
3875
  .catch((err) => errorCb(CdvPurchase.ErrorCode.LOAD, (err === null || err === void 0 ? void 0 : err.message) || 'loadReceipts failed'));
3653
3876
  }
3654
3877
  // Called when the native side sends a transaction update
3655
- transactionUpdated(state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation) {
3878
+ transactionUpdated(state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity) {
3656
3879
  if (!this.initialized) {
3657
3880
  this.pendingTransactionUpdates.push({
3658
3881
  state, errorCode, errorText, transactionIdentifier,
3659
3882
  productId, transactionReceipt, originalTransactionIdentifier,
3660
3883
  transactionDate, discountId, expirationDate, jwsRepresentation,
3884
+ quantity,
3661
3885
  });
3662
3886
  return;
3663
3887
  }
@@ -3677,7 +3901,7 @@ var CdvPurchase;
3677
3901
  break;
3678
3902
  case 'PaymentTransactionStatePurchased':
3679
3903
  if (this.options.purchased) {
3680
- this.options.purchased(transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation);
3904
+ this.options.purchased(transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity);
3681
3905
  }
3682
3906
  break;
3683
3907
  case 'PaymentTransactionStateFailed':
@@ -3689,8 +3913,11 @@ var CdvPurchase;
3689
3913
  }
3690
3914
  break;
3691
3915
  case 'PaymentTransactionStateRestored':
3916
+ // quantity is passed through for positional consistency with
3917
+ // purchased, but is meaningless here: consumables cannot be
3918
+ // restored, so restored transactions are always quantity 1.
3692
3919
  if (this.options.restored) {
3693
- this.options.restored(transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation);
3920
+ this.options.restored(transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity);
3694
3921
  }
3695
3922
  break;
3696
3923
  case 'PaymentTransactionStateDeferred':
@@ -3718,6 +3945,23 @@ var CdvPurchase;
3718
3945
  this.options.restoreFailed(errorCode);
3719
3946
  }
3720
3947
  }
3948
+ /** Retrieve the storefront country code from StoreKit */
3949
+ getStorefront() {
3950
+ return new Promise((resolve) => {
3951
+ const plugin = this.plugin;
3952
+ if (!plugin) {
3953
+ log('getStorefront failed: plugin not available');
3954
+ resolve(undefined);
3955
+ return;
3956
+ }
3957
+ plugin.getStorefront()
3958
+ .then((result) => resolve(result.countryCode || undefined))
3959
+ .catch((err) => {
3960
+ log('getStorefront failed: ' + ((err === null || err === void 0 ? void 0 : err.message) || err));
3961
+ resolve(undefined);
3962
+ });
3963
+ });
3964
+ }
3721
3965
  }
3722
3966
  CapacitorBridge.CapacitorNativeBridge = CapacitorNativeBridge;
3723
3967
  })(AppleAppStore.CapacitorBridge || (AppleAppStore.CapacitorBridge = {}));
@@ -3887,7 +4131,7 @@ var CdvPurchase;
3887
4131
  finalizeTransactionUpdates() {
3888
4132
  for (let i = 0; i < this.pendingUpdates.length; ++i) {
3889
4133
  const args = this.pendingUpdates[i];
3890
- this.transactionUpdated(args.state, args.errorCode, args.errorText, args.transactionIdentifier, args.productId, args.transactionReceipt, args.originalTransactionIdentifier, args.transactionDate, args.discountId, args.expirationDate, args.jwsRepresentation);
4134
+ this.transactionUpdated(args.state, args.errorCode, args.errorText, args.transactionIdentifier, args.productId, args.transactionReceipt, args.originalTransactionIdentifier, args.transactionDate, args.discountId, args.expirationDate, args.jwsRepresentation, args.quantity);
3891
4135
  }
3892
4136
  this.pendingUpdates = [];
3893
4137
  }
@@ -3895,12 +4139,13 @@ var CdvPurchase;
3895
4139
  // no more pending transactions
3896
4140
  }
3897
4141
  /** Called from native. Same as SK1 but with extra SK2 fields. */
3898
- transactionUpdated(state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation) {
4142
+ transactionUpdated(state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity) {
3899
4143
  if (!this.initialized) {
3900
4144
  this.pendingUpdates.push({
3901
4145
  state, errorCode, errorText, transactionIdentifier,
3902
4146
  productId, transactionReceipt, originalTransactionIdentifier,
3903
- transactionDate, discountId, expirationDate, jwsRepresentation
4147
+ transactionDate, discountId, expirationDate, jwsRepresentation,
4148
+ quantity
3904
4149
  });
3905
4150
  return;
3906
4151
  }
@@ -3919,7 +4164,7 @@ var CdvPurchase;
3919
4164
  protectCall(this.options.purchasing, 'options.purchasing', productId);
3920
4165
  return;
3921
4166
  case "PaymentTransactionStatePurchased":
3922
- protectCall(this.options.purchased, 'options.purchased', transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation);
4167
+ protectCall(this.options.purchased, 'options.purchased', transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity);
3923
4168
  return;
3924
4169
  case "PaymentTransactionStateDeferred":
3925
4170
  protectCall(this.options.deferred, 'options.deferred', productId);
@@ -3929,7 +4174,10 @@ var CdvPurchase;
3929
4174
  protectCall(this.options.error, 'options.error', errorCode || CdvPurchase.ErrorCode.UNKNOWN, errorText || 'ERROR', { productId });
3930
4175
  return;
3931
4176
  case "PaymentTransactionStateRestored":
3932
- protectCall(this.options.restored, 'options.restored', transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation);
4177
+ // Note: quantity is always irrelevant for restored transactions on iOS —
4178
+ // consumable products cannot be restored. Passed through to maintain
4179
+ // positional argument consistency with the purchased callback.
4180
+ protectCall(this.options.restored, 'options.restored', transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity);
3933
4181
  return;
3934
4182
  case "PaymentTransactionStateFinished":
3935
4183
  protectCall(this.options.finished, 'options.finished', transactionIdentifier, productId);
@@ -3971,6 +4219,19 @@ var CdvPurchase;
3971
4219
  this.appStoreReceipt = null;
3972
4220
  exec('appStoreRefreshReceipt', [], loaded, error);
3973
4221
  }
4222
+ /** Retrieve the storefront country code from StoreKit */
4223
+ getStorefront() {
4224
+ return new Promise((resolve) => {
4225
+ // Use StoreKit 2's Storefront.current via the SK2 plugin,
4226
+ // which works on Mac Catalyst where SK1's storefront is nil.
4227
+ window.cordova.exec((countryCode) => {
4228
+ resolve(countryCode || undefined);
4229
+ }, (err) => {
4230
+ log('getStorefront failed: ' + err);
4231
+ resolve(undefined);
4232
+ }, "StoreKit2Plugin", "getStorefront", []);
4233
+ });
4234
+ }
3974
4235
  loadReceipts(callback, errorCb) {
3975
4236
  const loaded = (args) => {
3976
4237
  const data = this.parseReceiptArgs(args);
@@ -4257,7 +4518,7 @@ var CdvPurchase;
4257
4518
  finalizeTransactionUpdates() {
4258
4519
  for (let i = 0; i < this.pendingUpdates.length; ++i) {
4259
4520
  const args = this.pendingUpdates[i];
4260
- this.transactionUpdated(args.state, args.errorCode, args.errorText, args.transactionIdentifier, args.productId, args.transactionReceipt, args.originalTransactionIdentifier, args.transactionDate, args.discountId);
4521
+ this.transactionUpdated(args.state, args.errorCode, args.errorText, args.transactionIdentifier, args.productId, args.transactionReceipt, args.originalTransactionIdentifier, args.transactionDate, args.discountId, args.quantity);
4261
4522
  }
4262
4523
  this.pendingUpdates = [];
4263
4524
  }
@@ -4268,9 +4529,9 @@ var CdvPurchase;
4268
4529
  //
4269
4530
  // Note that it may eventually be called before initialization... unfortunately.
4270
4531
  // In this case, we'll just keep pending updates in a list for later processing.
4271
- transactionUpdated(state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt, originalTransactionIdentifier, transactionDate, discountId) {
4532
+ transactionUpdated(state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt, originalTransactionIdentifier, transactionDate, discountId, quantity) {
4272
4533
  if (!this.initialized) {
4273
- this.pendingUpdates.push({ state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt, originalTransactionIdentifier, transactionDate, discountId });
4534
+ this.pendingUpdates.push({ state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt, originalTransactionIdentifier, transactionDate, discountId, quantity });
4274
4535
  return;
4275
4536
  }
4276
4537
  log("transaction updated:" + transactionIdentifier + " state:" + state + " product:" + productId);
@@ -4287,7 +4548,7 @@ var CdvPurchase;
4287
4548
  protectCall(this.options.purchasing, 'options.purchasing', productId);
4288
4549
  return;
4289
4550
  case "PaymentTransactionStatePurchased":
4290
- protectCall(this.options.purchased, 'options.purchase', transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId);
4551
+ protectCall(this.options.purchased, 'options.purchase', transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, undefined, undefined, quantity);
4291
4552
  return;
4292
4553
  case "PaymentTransactionStateDeferred":
4293
4554
  protectCall(this.options.deferred, 'options.deferred', productId);
@@ -4297,7 +4558,10 @@ var CdvPurchase;
4297
4558
  protectCall(this.options.error, 'options.error', errorCode || CdvPurchase.ErrorCode.UNKNOWN, errorText || 'ERROR', { productId });
4298
4559
  return;
4299
4560
  case "PaymentTransactionStateRestored":
4300
- protectCall(this.options.restored, 'options.restore', transactionIdentifier, productId);
4561
+ // Note: quantity is always irrelevant for restored transactions on iOS —
4562
+ // consumable products cannot be restored. Passed through to maintain
4563
+ // positional argument consistency with the purchased callback.
4564
+ protectCall(this.options.restored, 'options.restore', transactionIdentifier, productId, undefined, undefined, undefined, undefined, undefined, quantity);
4301
4565
  return;
4302
4566
  case "PaymentTransactionStateFinished":
4303
4567
  protectCall(this.options.finished, 'options.finish', transactionIdentifier, productId);
@@ -4350,6 +4614,17 @@ var CdvPurchase;
4350
4614
  log('refreshing appStoreReceipt');
4351
4615
  exec('appStoreRefreshReceipt', [], loaded, error);
4352
4616
  }
4617
+ /** Retrieve the storefront country code from StoreKit */
4618
+ getStorefront() {
4619
+ return new Promise((resolve) => {
4620
+ exec('getStorefront', [], (countryCode) => {
4621
+ resolve(countryCode || undefined);
4622
+ }, (err) => {
4623
+ log('getStorefront failed: ' + err);
4624
+ resolve(undefined);
4625
+ });
4626
+ });
4627
+ }
4353
4628
  loadReceipts(callback, errorCb) {
4354
4629
  const loaded = (args) => {
4355
4630
  const data = this.parseReceiptArgs(args);
@@ -4539,7 +4814,7 @@ var CdvPurchase;
4539
4814
  AppleAppStore.SKApplicationReceipt = SKApplicationReceipt;
4540
4815
  /** StoreKit transaction */
4541
4816
  class SKTransaction extends CdvPurchase.Transaction {
4542
- refresh(productId, originalTransactionIdentifier, transactionDate, discountId, expirationDateMs, jwsRepresentation) {
4817
+ refresh(productId, originalTransactionIdentifier, transactionDate, discountId, expirationDateMs, jwsRepresentation, quantity) {
4543
4818
  if (productId)
4544
4819
  this.products = [{ id: productId, offerId: discountId }];
4545
4820
  if (originalTransactionIdentifier)
@@ -4550,6 +4825,8 @@ var CdvPurchase;
4550
4825
  this.expirationDate = new Date(+expirationDateMs);
4551
4826
  if (jwsRepresentation)
4552
4827
  this.jwsRepresentation = jwsRepresentation;
4828
+ if (quantity !== undefined)
4829
+ this.quantity = quantity;
4553
4830
  }
4554
4831
  }
4555
4832
  AppleAppStore.SKTransaction = SKTransaction;
@@ -5880,9 +6157,21 @@ var CdvPurchase;
5880
6157
  return;
5881
6158
  });
5882
6159
  }
6160
+ getStorefront() {
6161
+ return __awaiter(this, void 0, void 0, function* () {
6162
+ return new Promise((resolve) => {
6163
+ this.bridge.getStorefront((countryCode) => {
6164
+ resolve(countryCode || undefined);
6165
+ }, (message) => {
6166
+ this.log.warn('getStorefront failed: ' + message);
6167
+ resolve(undefined);
6168
+ });
6169
+ });
6170
+ });
6171
+ }
5883
6172
  checkSupport(functionality) {
5884
6173
  const supported = [
5885
- 'order', 'manageBilling', 'manageSubscriptions'
6174
+ 'order', 'manageBilling', 'manageSubscriptions', 'getStorefront'
5886
6175
  ];
5887
6176
  return supported.indexOf(functionality) >= 0;
5888
6177
  }
@@ -6051,6 +6340,14 @@ var CdvPurchase;
6051
6340
  launchPriceChangeConfirmationFlow(productId) {
6052
6341
  this.plugin.launchPriceChangeConfirmationFlow({ productId });
6053
6342
  }
6343
+ getStorefront(success, fail) {
6344
+ if (this.options.showLog) {
6345
+ log('getStorefront()');
6346
+ }
6347
+ this.plugin.getStorefront()
6348
+ .then((result) => success(result.countryCode))
6349
+ .catch((err) => fail((err === null || err === void 0 ? void 0 : err.message) || 'getStorefront failed', err === null || err === void 0 ? void 0 : err.code));
6350
+ }
6054
6351
  }
6055
6352
  Bridge.CapacitorBridge = CapacitorBridge;
6056
6353
  function ensureObject(obj) {
@@ -6242,6 +6539,12 @@ var CdvPurchase;
6242
6539
  manageBilling() {
6243
6540
  return window.cordova.exec(function () { }, function () { }, "InAppBillingPlugin", "manageBilling", []);
6244
6541
  }
6542
+ getStorefront(success, fail) {
6543
+ if (this.options.showLog) {
6544
+ log('getStorefront()');
6545
+ }
6546
+ return window.cordova.exec(success, errorCb(fail), "InAppBillingPlugin", "getStorefront", []);
6547
+ }
6245
6548
  launchPriceChangeConfirmationFlow(productId) {
6246
6549
  return window.cordova.exec(function () { }, function () { }, "InAppBillingPlugin", "launchPriceChangeConfirmationFlow", [productId]);
6247
6550
  }
@@ -7503,6 +7806,11 @@ var CdvPurchase;
7503
7806
  return undefined;
7504
7807
  });
7505
7808
  }
7809
+ getStorefront() {
7810
+ return __awaiter(this, void 0, void 0, function* () {
7811
+ return 'US';
7812
+ });
7813
+ }
7506
7814
  }
7507
7815
  Test.Adapter = Adapter;
7508
7816
  function testStoreError(code, message, productId) {