capacitor-plugin-cdv-purchase 13.14.0 → 13.15.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/www/store.js CHANGED
@@ -884,26 +884,31 @@ var CdvPurchase;
884
884
  if (initResult === null || initResult === void 0 ? void 0 : initResult.code)
885
885
  return initResult;
886
886
  log.info(`${adapter.name} products: ${JSON.stringify(platformProducts)}`);
887
- if (platformProducts.length === 0)
888
- return;
887
+ // Storefront refresh runs in parallel with product / receipt loading.
888
+ // Failure or timeout is absorbed so it never blocks store readiness.
889
+ const storefrontRefresh = context.storefronts.refreshWith(adapter).catch(() => { });
889
890
  let loadProductsResult = [];
890
891
  let loadReceiptsResult = [];
891
- if (adapter.supportsParallelLoading) {
892
- [loadProductsResult, loadReceiptsResult] = yield Promise.all([
893
- adapter.loadProducts(platformProducts),
894
- adapter.loadReceipts()
895
- ]);
896
- }
897
- else {
898
- loadProductsResult = yield adapter.loadProducts(platformProducts);
899
- loadReceiptsResult = yield adapter.loadReceipts();
892
+ if (platformProducts.length > 0) {
893
+ if (adapter.supportsParallelLoading) {
894
+ [loadProductsResult, loadReceiptsResult] = yield Promise.all([
895
+ adapter.loadProducts(platformProducts),
896
+ adapter.loadReceipts()
897
+ ]);
898
+ }
899
+ else {
900
+ loadProductsResult = yield adapter.loadProducts(platformProducts);
901
+ loadReceiptsResult = yield adapter.loadReceipts();
902
+ }
903
+ log.info(`${adapter.name} products loaded: ${JSON.stringify(loadProductsResult)}`);
904
+ const loadedProducts = loadProductsResult.filter(p => p instanceof CdvPurchase.Product);
905
+ context.listener.productsUpdated(platformToInit.platform, loadedProducts);
906
+ log.info(`${adapter.name} receipts loaded: ${JSON.stringify(loadReceiptsResult)}`);
900
907
  }
901
- // const loadProductsResult = await adapter.loadProducts(platformProducts);
902
- log.info(`${adapter.name} products loaded: ${JSON.stringify(loadProductsResult)}`);
903
- const loadedProducts = loadProductsResult.filter(p => p instanceof CdvPurchase.Product);
904
- context.listener.productsUpdated(platformToInit.platform, loadedProducts);
905
- // const loadReceiptsResult = await adapter.loadReceipts();
906
- log.info(`${adapter.name} receipts loaded: ${JSON.stringify(loadReceiptsResult)}`);
908
+ // Wait for the storefront refresh (or its timeout) before returning.
909
+ // This intentionally delays storeReady by up to the timeout duration
910
+ // so that store.getStorefront() has a value once the store is ready.
911
+ yield storefrontRefresh;
907
912
  return loadProductsResult.filter(lr => 'code' in lr && 'message' in lr)[0];
908
913
  })));
909
914
  return result.filter(err => err);
@@ -937,6 +942,96 @@ var CdvPurchase;
937
942
  })(Internal = CdvPurchase.Internal || (CdvPurchase.Internal = {}));
938
943
  })(CdvPurchase || (CdvPurchase = {}));
939
944
  var CdvPurchase;
945
+ (function (CdvPurchase) {
946
+ let Internal;
947
+ (function (Internal) {
948
+ /** Default timeout for a storefront refresh call, in milliseconds. */
949
+ const DEFAULT_STOREFRONT_REFRESH_TIMEOUT_MS = 2000;
950
+ /**
951
+ * Collection of per-platform storefront country codes.
952
+ *
953
+ * Maintains the cached value for each platform that exposes one and
954
+ * notifies listeners when a value changes. Adapter-agnostic — callers
955
+ * are responsible for validating that a platform has a ready adapter.
956
+ */
957
+ class Storefronts {
958
+ constructor(logger) {
959
+ /** Cached country code per platform. */
960
+ this.values = {};
961
+ this.callbacks = new Internal.Callbacks(logger, 'storefrontUpdated()');
962
+ }
963
+ /**
964
+ * Refresh the cached value for a given adapter.
965
+ *
966
+ * The returned promise:
967
+ * - resolves when the adapter responds within `timeoutMs`
968
+ * - rejects with a timeout error otherwise
969
+ *
970
+ * Regardless of timeout, if the adapter eventually yields a value,
971
+ * the cache is silently updated and listeners are notified.
972
+ * A failed or empty response never overwrites the cache.
973
+ */
974
+ refreshWith(adapter, timeoutMs = DEFAULT_STOREFRONT_REFRESH_TIMEOUT_MS) {
975
+ return __awaiter(this, void 0, void 0, function* () {
976
+ if (!adapter.getStorefront)
977
+ return;
978
+ const platform = adapter.id;
979
+ // Start the fetch; handle result + errors independently of the race.
980
+ const fetch = adapter.getStorefront()
981
+ .then(code => { if (code)
982
+ this.setValue(platform, code); })
983
+ .catch(() => { });
984
+ let timerId;
985
+ const timeout = new Promise((_, reject) => {
986
+ timerId = setTimeout(() => reject(new Error('storefront refresh timeout')), timeoutMs);
987
+ });
988
+ try {
989
+ yield Promise.race([fetch, timeout]);
990
+ }
991
+ finally {
992
+ clearTimeout(timerId);
993
+ }
994
+ });
995
+ }
996
+ /**
997
+ * Retrieve a storefront value.
998
+ *
999
+ * - With a platform: always returns `{ platform, countryCode }`,
1000
+ * where `countryCode` may be undefined if nothing is cached.
1001
+ * - Without a platform: returns the first cached non-empty
1002
+ * storefront, or `undefined` if nothing is cached.
1003
+ */
1004
+ getValueFor(platform) {
1005
+ if (platform) {
1006
+ return { platform, countryCode: this.values[platform] };
1007
+ }
1008
+ for (const p of Object.keys(this.values)) {
1009
+ if (this.values[p]) {
1010
+ return { platform: p, countryCode: this.values[p] };
1011
+ }
1012
+ }
1013
+ return undefined;
1014
+ }
1015
+ /** Register a change listener. */
1016
+ listen(cb, callbackName) {
1017
+ this.callbacks.push(cb, callbackName);
1018
+ }
1019
+ /** Remove a previously registered listener. */
1020
+ off(cb) {
1021
+ this.callbacks.remove(cb);
1022
+ }
1023
+ /** Update the cache and notify listeners on change. */
1024
+ setValue(platform, countryCode) {
1025
+ if (this.values[platform] === countryCode)
1026
+ return;
1027
+ this.values[platform] = countryCode;
1028
+ this.callbacks.trigger({ platform, countryCode }, 'storefront_changed');
1029
+ }
1030
+ }
1031
+ Internal.Storefronts = Storefronts;
1032
+ })(Internal = CdvPurchase.Internal || (CdvPurchase.Internal = {}));
1033
+ })(CdvPurchase || (CdvPurchase = {}));
1034
+ var CdvPurchase;
940
1035
  (function (CdvPurchase) {
941
1036
  let Internal;
942
1037
  (function (Internal) {
@@ -1442,6 +1537,7 @@ var CdvPurchase;
1442
1537
  /// <reference path="validator/validator.ts" />
1443
1538
  /// <reference path="log.ts" />
1444
1539
  /// <reference path="internal/adapters.ts" />
1540
+ /// <reference path="internal/storefronts.ts" />
1445
1541
  /// <reference path="internal/adapter-listener.ts" />
1446
1542
  /// <reference path="internal/callbacks.ts" />
1447
1543
  /// <reference path="internal/ready.ts" />
@@ -1482,7 +1578,7 @@ var CdvPurchase;
1482
1578
  /**
1483
1579
  * Current release number of the plugin.
1484
1580
  */
1485
- CdvPurchase.PLUGIN_VERSION = '13.14.0';
1581
+ CdvPurchase.PLUGIN_VERSION = '13.15.0';
1486
1582
  /**
1487
1583
  * Entry class of the plugin.
1488
1584
  */
@@ -1540,6 +1636,8 @@ var CdvPurchase;
1540
1636
  this.receiptsVerifiedCallbacks = new CdvPurchase.Internal.Callbacks(this.log, 'receiptsVerified()', true);
1541
1637
  /** Callbacks for errors */
1542
1638
  this.errorCallbacks = new CdvPurchase.Internal.Callbacks(this.log, 'error()');
1639
+ /** Per-platform storefront cache and change notifications. */
1640
+ this._storefronts = new CdvPurchase.Internal.Storefronts(this.log.child('Storefronts'));
1543
1641
  this.initializedHasBeenCalled = false;
1544
1642
  /** Stores the last time the store was updated (or initialized), to skip calls in quick succession. */
1545
1643
  this.lastUpdate = 0;
@@ -1683,6 +1781,7 @@ var CdvPurchase;
1683
1781
  get listener() { return store.listener; },
1684
1782
  get log() { return store.log; },
1685
1783
  get registeredProducts() { return store.registeredProducts; },
1784
+ get storefronts() { return store._storefronts; },
1686
1785
  apiDecorators: {
1687
1786
  canPurchase: this.canPurchase.bind(this),
1688
1787
  owned: this.owned.bind(this),
@@ -1708,7 +1807,6 @@ var CdvPurchase;
1708
1807
  * Call to refresh the price of products and status of purchases.
1709
1808
  */
1710
1809
  update() {
1711
- var _a;
1712
1810
  return __awaiter(this, void 0, void 0, function* () {
1713
1811
  this.log.info('update()');
1714
1812
  if (!this._readyCallbacks.isReady) {
@@ -1723,11 +1821,15 @@ var CdvPurchase;
1723
1821
  this.lastUpdate = now;
1724
1822
  // Load products metadata
1725
1823
  for (const registration of this.registeredProducts.byPlatform()) {
1726
- const products = yield ((_a = this.adapters.findReady(registration.platform)) === null || _a === void 0 ? void 0 : _a.loadProducts(registration.products));
1824
+ const adapter = this.adapters.findReady(registration.platform);
1825
+ const products = yield (adapter === null || adapter === void 0 ? void 0 : adapter.loadProducts(registration.products));
1727
1826
  products === null || products === void 0 ? void 0 : products.forEach(p => {
1728
1827
  if (p instanceof CdvPurchase.Product)
1729
1828
  this.updatedCallbacks.trigger(p, 'update_has_loaded_products');
1730
1829
  });
1830
+ if (adapter) {
1831
+ this._storefronts.refreshWith(adapter).catch(() => { });
1832
+ }
1731
1833
  }
1732
1834
  });
1733
1835
  }
@@ -1779,6 +1881,7 @@ var CdvPurchase;
1779
1881
  unverified: (cb, callbackName) => (this.unverifiedCallbacks.push(cb, callbackName), ret),
1780
1882
  receiptsReady: (cb, callbackName) => (this.receiptsReadyCallbacks.push(cb, callbackName), ret),
1781
1883
  receiptsVerified: (cb, callbackName) => (this.receiptsVerifiedCallbacks.push(cb, callbackName), ret),
1884
+ storefrontUpdated: (cb, callbackName) => (this._storefronts.listen(cb, callbackName), ret),
1782
1885
  };
1783
1886
  return ret;
1784
1887
  }
@@ -1797,6 +1900,7 @@ var CdvPurchase;
1797
1900
  this.receiptsVerifiedCallbacks.remove(callback);
1798
1901
  this.errorCallbacks.remove(callback);
1799
1902
  this._readyCallbacks.remove(callback);
1903
+ this._storefronts.off(callback);
1800
1904
  }
1801
1905
  /**
1802
1906
  * Setup a function to be notified of changes to a transaction state.
@@ -1912,6 +2016,8 @@ var CdvPurchase;
1912
2016
  const ret = yield adapter.order(offer, additionalData || {});
1913
2017
  if (ret && 'isError' in ret)
1914
2018
  CdvPurchase.store.triggerError(ret);
2019
+ // Account may have switched during checkout — refresh storefront in the background.
2020
+ this._storefronts.refreshWith(adapter).catch(() => { });
1915
2021
  return ret;
1916
2022
  });
1917
2023
  }
@@ -1968,6 +2074,7 @@ var CdvPurchase;
1968
2074
  }
1969
2075
  const promise = new CdvPurchase.PaymentRequestPromise();
1970
2076
  adapter.requestPayment(paymentRequest, additionalData).then(result => {
2077
+ this._storefronts.refreshWith(adapter).catch(() => { });
1971
2078
  promise.trigger(result);
1972
2079
  if (result instanceof CdvPurchase.Transaction) {
1973
2080
  const onStateChange = (state) => {
@@ -2068,6 +2175,8 @@ var CdvPurchase;
2068
2175
  for (const adapter of this.adapters.list) {
2069
2176
  if (adapter.ready) {
2070
2177
  error = error !== null && error !== void 0 ? error : yield adapter.restorePurchases();
2178
+ // Restore often implies a login or account switch — refresh storefront.
2179
+ this._storefronts.refreshWith(adapter).catch(() => { });
2071
2180
  }
2072
2181
  }
2073
2182
  return error;
@@ -2111,6 +2220,43 @@ var CdvPurchase;
2111
2220
  return adapter.manageBilling();
2112
2221
  });
2113
2222
  }
2223
+ /**
2224
+ * Retrieve the billing country code from the platform's storefront.
2225
+ *
2226
+ * Returns a `Storefront` object with the platform and its ISO 3166-1
2227
+ * alpha-2 country code (e.g., "US", "FR"). The country code may be
2228
+ * undefined if the underlying fetch has not yet completed or failed —
2229
+ * the platform is still reported. Returns `undefined` only when no
2230
+ * matching adapter is ready.
2231
+ *
2232
+ * The cache is populated before the `storeReady` event fires (with a
2233
+ * best-effort timeout), and refreshed after orders and `restorePurchases()`.
2234
+ *
2235
+ * @param platform - Optional platform. If omitted, returns the first
2236
+ * cached non-empty storefront, or a `{ platform, countryCode: undefined }`
2237
+ * object for the first ready adapter.
2238
+ *
2239
+ * @example
2240
+ * const storefront = store.getStorefront();
2241
+ * if (storefront?.countryCode) {
2242
+ * console.log(`Billing country: ${storefront.countryCode}`);
2243
+ * }
2244
+ */
2245
+ getStorefront(platform) {
2246
+ if (platform) {
2247
+ const adapter = this.adapters.findReady(platform);
2248
+ if (!adapter)
2249
+ return undefined;
2250
+ return this._storefronts.getValueFor(platform);
2251
+ }
2252
+ const cached = this._storefronts.getValueFor();
2253
+ if (cached)
2254
+ return cached;
2255
+ const firstReady = this.adapters.findReady();
2256
+ if (!firstReady)
2257
+ return undefined;
2258
+ return { platform: firstReady.id, countryCode: undefined };
2259
+ }
2114
2260
  /**
2115
2261
  * The default payment platform to use depending on the OS.
2116
2262
  *
@@ -3027,14 +3173,15 @@ var CdvPurchase;
3027
3173
  ready: () => {
3028
3174
  this.log.info('ready');
3029
3175
  },
3030
- purchased: (transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation) => __awaiter(this, void 0, void 0, function* () {
3176
+ purchased: (transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity) => __awaiter(this, void 0, void 0, function* () {
3031
3177
  this.log.info('purchase: id:' + transactionIdentifier + ' product:' + productId +
3032
3178
  ' originalTransaction:' + originalTransactionIdentifier +
3033
3179
  ' - date:' + transactionDate + ' - discount:' + discountId +
3034
- (jwsRepresentation ? ' - jws:present' : ''));
3180
+ (jwsRepresentation ? ' - jws:present' : '') +
3181
+ (quantity && quantity > 1 ? ' - quantity:' + quantity : ''));
3035
3182
  // we can add the transaction to the receipt here
3036
3183
  const transaction = yield this.upsertTransaction(productId, transactionIdentifier, CdvPurchase.TransactionState.APPROVED);
3037
- transaction.refresh(productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation);
3184
+ transaction.refresh(productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity);
3038
3185
  this.removeTransactionInProgress(productId);
3039
3186
  this.receiptsUpdated.call();
3040
3187
  this.callPaymentMonitor('purchased');
@@ -3049,12 +3196,7 @@ var CdvPurchase;
3049
3196
  this.log.info('purchaseFailed: ' + productId + ' - ' + code + ' - ' + message);
3050
3197
  this.removeTransactionInProgress(productId);
3051
3198
  this.context.listener.receiptsUpdated(CdvPurchase.Platform.APPLE_APPSTORE, [this.pseudoReceipt]);
3052
- if (code === CdvPurchase.ErrorCode.PAYMENT_CANCELLED) {
3053
- this.callPaymentMonitor('cancelled', code, message);
3054
- }
3055
- else {
3056
- this.callPaymentMonitor('failed', code, message);
3057
- }
3199
+ this.callPaymentMonitor('failed', code, message);
3058
3200
  },
3059
3201
  purchasing: (productId) => __awaiter(this, void 0, void 0, function* () {
3060
3202
  // purchase has been requested, but there's no transactionIdentifier yet.
@@ -3090,10 +3232,10 @@ var CdvPurchase;
3090
3232
  yield this.upsertTransaction(productId, transactionIdentifier, CdvPurchase.TransactionState.FINISHED);
3091
3233
  this.receiptsUpdated.call();
3092
3234
  }),
3093
- restored: (transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation) => __awaiter(this, void 0, void 0, function* () {
3235
+ restored: (transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity) => __awaiter(this, void 0, void 0, function* () {
3094
3236
  this.log.info('restore: ' + transactionIdentifier + ' - ' + productId);
3095
3237
  const transaction = yield this.upsertTransaction(productId, transactionIdentifier, CdvPurchase.TransactionState.APPROVED);
3096
- transaction.refresh(productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation);
3238
+ transaction.refresh(productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity);
3097
3239
  this.receiptsUpdated.call();
3098
3240
  }),
3099
3241
  receiptsRefreshed: (receipt) => {
@@ -3340,7 +3482,7 @@ var CdvPurchase;
3340
3482
  return __awaiter(this, void 0, void 0, function* () {
3341
3483
  let resolved = false;
3342
3484
  return new Promise(resolve => {
3343
- var _a;
3485
+ var _a, _b;
3344
3486
  const callResolve = (result) => {
3345
3487
  if (resolved)
3346
3488
  return;
@@ -3349,8 +3491,12 @@ var CdvPurchase;
3349
3491
  resolve(result);
3350
3492
  };
3351
3493
  this.log.info('order');
3494
+ const quantity = (_a = additionalData === null || additionalData === void 0 ? void 0 : additionalData.quantity) !== null && _a !== void 0 ? _a : 1;
3495
+ if (quantity < 1 || quantity > 10 || !Number.isInteger(quantity)) {
3496
+ return callResolve(appStoreError(CdvPurchase.ErrorCode.PURCHASE, 'Invalid quantity: must be an integer between 1 and 10', offer.productId));
3497
+ }
3352
3498
  const discountId = offer.id !== AppleAppStore.DEFAULT_OFFER_ID ? offer.id : undefined;
3353
- const discount = (_a = additionalData === null || additionalData === void 0 ? void 0 : additionalData.appStore) === null || _a === void 0 ? void 0 : _a.discount;
3499
+ const discount = (_b = additionalData === null || additionalData === void 0 ? void 0 : additionalData.appStore) === null || _b === void 0 ? void 0 : _b.discount;
3354
3500
  if (discountId && !discount) {
3355
3501
  return callResolve(appStoreError(CdvPurchase.ErrorCode.MISSING_OFFER_PARAMS, 'Missing additionalData.appStore.discount when ordering a discount offer', offer.productId));
3356
3502
  }
@@ -3389,7 +3535,7 @@ var CdvPurchase;
3389
3535
  // When we switch AppStore user, the cached receipt isn't from the new user.
3390
3536
  // so after a purchase, we want to make sure we're using the receipt from the logged in user.
3391
3537
  this.forceReceiptReload = true;
3392
- this.bridge.purchase(offer.productId, 1, this.context.getApplicationUsername(), discount, success, error);
3538
+ this.bridge.purchase(offer.productId, quantity, this.context.getApplicationUsername(), discount, success, error);
3393
3539
  });
3394
3540
  });
3395
3541
  }
@@ -3539,7 +3685,7 @@ var CdvPurchase;
3539
3685
  if (functionality === 'order')
3540
3686
  return this._canMakePayments;
3541
3687
  const supported = [
3542
- 'order', 'manageBilling', 'manageSubscriptions'
3688
+ 'order', 'orderQuantity', 'manageBilling', 'manageSubscriptions', 'getStorefront'
3543
3689
  ];
3544
3690
  return supported.indexOf(functionality) >= 0;
3545
3691
  }
@@ -3562,8 +3708,83 @@ var CdvPurchase;
3562
3708
  this.bridge.presentCodeRedemptionSheet(resolve);
3563
3709
  });
3564
3710
  }
3711
+ getStorefront() {
3712
+ return __awaiter(this, void 0, void 0, function* () {
3713
+ if (!this.bridge.getStorefront)
3714
+ return undefined;
3715
+ const countryCode = yield this.bridge.getStorefront();
3716
+ if (!countryCode)
3717
+ return undefined;
3718
+ // SKStorefront.countryCode typically returns ISO 3166-1 alpha-3 (e.g., "USA").
3719
+ // The fallback `|| countryCode` handles cases where Apple returns alpha-2 directly
3720
+ // or uses a non-standard code (e.g., territories not in ISO 3166-1).
3721
+ return isoAlpha3ToAlpha2(countryCode) || countryCode;
3722
+ });
3723
+ }
3565
3724
  }
3566
3725
  AppleAppStore.Adapter = Adapter;
3726
+ /**
3727
+ * Convert ISO 3166-1 alpha-3 country code to alpha-2.
3728
+ *
3729
+ * Apple's SKStorefront.countryCode returns alpha-3 codes (e.g., "USA").
3730
+ * This function converts them to the more common alpha-2 format (e.g., "US")
3731
+ * for consistency with Google Play which already returns alpha-2.
3732
+ */
3733
+ const ISO_ALPHA3_TO_ALPHA2 = {
3734
+ AFG: 'AF', ALB: 'AL', DZA: 'DZ', ASM: 'AS', AND: 'AD',
3735
+ AGO: 'AO', AIA: 'AI', ATA: 'AQ', ATG: 'AG', ARG: 'AR',
3736
+ ARM: 'AM', ABW: 'AW', AUS: 'AU', AUT: 'AT', AZE: 'AZ',
3737
+ BHS: 'BS', BHR: 'BH', BGD: 'BD', BRB: 'BB', BLR: 'BY',
3738
+ BEL: 'BE', BLZ: 'BZ', BEN: 'BJ', BMU: 'BM', BTN: 'BT',
3739
+ BOL: 'BO', BES: 'BQ', BIH: 'BA', BWA: 'BW', BVT: 'BV',
3740
+ BRA: 'BR', IOT: 'IO', BRN: 'BN', BGR: 'BG', BFA: 'BF',
3741
+ BDI: 'BI', CPV: 'CV', KHM: 'KH', CMR: 'CM', CAN: 'CA',
3742
+ CYM: 'KY', CAF: 'CF', TCD: 'TD', CHL: 'CL', CHN: 'CN',
3743
+ CXR: 'CX', CCK: 'CC', COL: 'CO', COM: 'KM', COG: 'CG',
3744
+ COD: 'CD', COK: 'CK', CRI: 'CR', CIV: 'CI', HRV: 'HR',
3745
+ CUB: 'CU', CUW: 'CW', CYP: 'CY', CZE: 'CZ', DNK: 'DK',
3746
+ DJI: 'DJ', DMA: 'DM', DOM: 'DO', ECU: 'EC', EGY: 'EG',
3747
+ SLV: 'SV', GNQ: 'GQ', ERI: 'ER', EST: 'EE', SWZ: 'SZ',
3748
+ ETH: 'ET', FLK: 'FK', FRO: 'FO', FJI: 'FJ', FIN: 'FI',
3749
+ FRA: 'FR', GUF: 'GF', PYF: 'PF', ATF: 'TF', GAB: 'GA',
3750
+ GMB: 'GM', GEO: 'GE', DEU: 'DE', GHA: 'GH', GIB: 'GI',
3751
+ GRC: 'GR', GRL: 'GL', GRD: 'GD', GLP: 'GP', GUM: 'GU',
3752
+ GTM: 'GT', GGY: 'GG', GIN: 'GN', GNB: 'GW', GUY: 'GY',
3753
+ HTI: 'HT', HMD: 'HM', VAT: 'VA', HND: 'HN', HKG: 'HK',
3754
+ HUN: 'HU', ISL: 'IS', IND: 'IN', IDN: 'ID', IRN: 'IR',
3755
+ IRQ: 'IQ', IRL: 'IE', IMN: 'IM', ISR: 'IL', ITA: 'IT',
3756
+ JAM: 'JM', JPN: 'JP', JEY: 'JE', JOR: 'JO', KAZ: 'KZ',
3757
+ KEN: 'KE', KIR: 'KI', PRK: 'KP', KOR: 'KR', KWT: 'KW',
3758
+ KGZ: 'KG', LAO: 'LA', LVA: 'LV', LBN: 'LB', LSO: 'LS',
3759
+ LBR: 'LR', LBY: 'LY', LIE: 'LI', LTU: 'LT', LUX: 'LU',
3760
+ MAC: 'MO', MDG: 'MG', MWI: 'MW', MYS: 'MY', MDV: 'MV',
3761
+ MLI: 'ML', MLT: 'MT', MHL: 'MH', MTQ: 'MQ', MRT: 'MR',
3762
+ MUS: 'MU', MYT: 'YT', MEX: 'MX', FSM: 'FM', MDA: 'MD',
3763
+ MCO: 'MC', MNG: 'MN', MNE: 'ME', MSR: 'MS', MAR: 'MA',
3764
+ MOZ: 'MZ', MMR: 'MM', NAM: 'NA', NRU: 'NR', NPL: 'NP',
3765
+ NLD: 'NL', NCL: 'NC', NZL: 'NZ', NIC: 'NI', NER: 'NE',
3766
+ NGA: 'NG', NIU: 'NU', NFK: 'NF', MKD: 'MK', MNP: 'MP',
3767
+ NOR: 'NO', OMN: 'OM', PAK: 'PK', PLW: 'PW', PSE: 'PS',
3768
+ PAN: 'PA', PNG: 'PG', PRY: 'PY', PER: 'PE', PHL: 'PH',
3769
+ PCN: 'PN', POL: 'PL', PRT: 'PT', PRI: 'PR', QAT: 'QA',
3770
+ REU: 'RE', ROU: 'RO', RUS: 'RU', RWA: 'RW', BLM: 'BL',
3771
+ SHN: 'SH', KNA: 'KN', LCA: 'LC', MAF: 'MF', SPM: 'PM',
3772
+ VCT: 'VC', WSM: 'WS', SMR: 'SM', STP: 'ST', SAU: 'SA',
3773
+ SEN: 'SN', SRB: 'RS', SYC: 'SC', SLE: 'SL', SGP: 'SG',
3774
+ SXM: 'SX', SVK: 'SK', SVN: 'SI', SLB: 'SB', SOM: 'SO',
3775
+ ZAF: 'ZA', SGS: 'GS', SSD: 'SS', ESP: 'ES', LKA: 'LK',
3776
+ SDN: 'SD', SUR: 'SR', SJM: 'SJ', SWE: 'SE', CHE: 'CH',
3777
+ SYR: 'SY', TWN: 'TW', TJK: 'TJ', TZA: 'TZ', THA: 'TH',
3778
+ TLS: 'TL', TGO: 'TG', TKL: 'TK', TON: 'TO', TTO: 'TT',
3779
+ TUN: 'TN', TUR: 'TR', TKM: 'TM', TCA: 'TC', TUV: 'TV',
3780
+ UGA: 'UG', UKR: 'UA', ARE: 'AE', GBR: 'GB', USA: 'US',
3781
+ UMI: 'UM', URY: 'UY', UZB: 'UZ', VUT: 'VU', VEN: 'VE',
3782
+ VNM: 'VN', VGB: 'VG', VIR: 'VI', WLF: 'WF', ESH: 'EH',
3783
+ YEM: 'YE', ZMB: 'ZM', ZWE: 'ZW',
3784
+ };
3785
+ function isoAlpha3ToAlpha2(alpha3) {
3786
+ return ISO_ALPHA3_TO_ALPHA2[alpha3.toUpperCase()];
3787
+ }
3567
3788
  function appStoreError(code, message, productId) {
3568
3789
  return CdvPurchase.storeError(code, message, CdvPurchase.Platform.APPLE_APPSTORE, productId);
3569
3790
  }
@@ -3624,7 +3845,7 @@ var CdvPurchase;
3624
3845
  }
3625
3846
  // Listen for transaction updates from native
3626
3847
  plugin.addListener('transactionUpdated', (data) => {
3627
- this.transactionUpdated(data.state, data.errorCode, data.errorText, data.transactionIdentifier, data.productId, data.transactionReceipt, data.originalTransactionIdentifier, data.transactionDate, data.discountId, data.expirationDate, data.jwsRepresentation);
3848
+ 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);
3628
3849
  });
3629
3850
  plugin.addListener('restoreCompleted', () => {
3630
3851
  this.restoreCompletedTransactionsFinished();
@@ -3645,7 +3866,7 @@ var CdvPurchase;
3645
3866
  const pending = this.pendingTransactionUpdates;
3646
3867
  this.pendingTransactionUpdates = [];
3647
3868
  for (const args of pending) {
3648
- this.transactionUpdated(args.state, args.errorCode, args.errorText, args.transactionIdentifier, args.productId, args.transactionReceipt, args.originalTransactionIdentifier, args.transactionDate, args.discountId, args.expirationDate, args.jwsRepresentation);
3869
+ 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);
3649
3870
  }
3650
3871
  if (this.options.ready)
3651
3872
  this.options.ready();
@@ -3729,12 +3950,13 @@ var CdvPurchase;
3729
3950
  .catch((err) => errorCb(CdvPurchase.ErrorCode.LOAD, (err === null || err === void 0 ? void 0 : err.message) || 'loadReceipts failed'));
3730
3951
  }
3731
3952
  // Called when the native side sends a transaction update
3732
- transactionUpdated(state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation) {
3953
+ transactionUpdated(state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity) {
3733
3954
  if (!this.initialized) {
3734
3955
  this.pendingTransactionUpdates.push({
3735
3956
  state, errorCode, errorText, transactionIdentifier,
3736
3957
  productId, transactionReceipt, originalTransactionIdentifier,
3737
3958
  transactionDate, discountId, expirationDate, jwsRepresentation,
3959
+ quantity,
3738
3960
  });
3739
3961
  return;
3740
3962
  }
@@ -3754,7 +3976,7 @@ var CdvPurchase;
3754
3976
  break;
3755
3977
  case 'PaymentTransactionStatePurchased':
3756
3978
  if (this.options.purchased) {
3757
- this.options.purchased(transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation);
3979
+ this.options.purchased(transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity);
3758
3980
  }
3759
3981
  break;
3760
3982
  case 'PaymentTransactionStateFailed':
@@ -3766,8 +3988,11 @@ var CdvPurchase;
3766
3988
  }
3767
3989
  break;
3768
3990
  case 'PaymentTransactionStateRestored':
3991
+ // quantity is passed through for positional consistency with
3992
+ // purchased, but is meaningless here: consumables cannot be
3993
+ // restored, so restored transactions are always quantity 1.
3769
3994
  if (this.options.restored) {
3770
- this.options.restored(transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation);
3995
+ this.options.restored(transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity);
3771
3996
  }
3772
3997
  break;
3773
3998
  case 'PaymentTransactionStateDeferred':
@@ -3795,6 +4020,23 @@ var CdvPurchase;
3795
4020
  this.options.restoreFailed(errorCode);
3796
4021
  }
3797
4022
  }
4023
+ /** Retrieve the storefront country code from StoreKit */
4024
+ getStorefront() {
4025
+ return new Promise((resolve) => {
4026
+ const plugin = this.plugin;
4027
+ if (!plugin) {
4028
+ log('getStorefront failed: plugin not available');
4029
+ resolve(undefined);
4030
+ return;
4031
+ }
4032
+ plugin.getStorefront()
4033
+ .then((result) => resolve(result.countryCode || undefined))
4034
+ .catch((err) => {
4035
+ log('getStorefront failed: ' + ((err === null || err === void 0 ? void 0 : err.message) || err));
4036
+ resolve(undefined);
4037
+ });
4038
+ });
4039
+ }
3798
4040
  }
3799
4041
  CapacitorBridge.CapacitorNativeBridge = CapacitorNativeBridge;
3800
4042
  })(CapacitorBridge = AppleAppStore.CapacitorBridge || (AppleAppStore.CapacitorBridge = {}));
@@ -3966,7 +4208,7 @@ var CdvPurchase;
3966
4208
  finalizeTransactionUpdates() {
3967
4209
  for (let i = 0; i < this.pendingUpdates.length; ++i) {
3968
4210
  const args = this.pendingUpdates[i];
3969
- this.transactionUpdated(args.state, args.errorCode, args.errorText, args.transactionIdentifier, args.productId, args.transactionReceipt, args.originalTransactionIdentifier, args.transactionDate, args.discountId, args.expirationDate, args.jwsRepresentation);
4211
+ 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);
3970
4212
  }
3971
4213
  this.pendingUpdates = [];
3972
4214
  }
@@ -3974,12 +4216,13 @@ var CdvPurchase;
3974
4216
  // no more pending transactions
3975
4217
  }
3976
4218
  /** Called from native. Same as SK1 but with extra SK2 fields. */
3977
- transactionUpdated(state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation) {
4219
+ transactionUpdated(state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity) {
3978
4220
  if (!this.initialized) {
3979
4221
  this.pendingUpdates.push({
3980
4222
  state, errorCode, errorText, transactionIdentifier,
3981
4223
  productId, transactionReceipt, originalTransactionIdentifier,
3982
- transactionDate, discountId, expirationDate, jwsRepresentation
4224
+ transactionDate, discountId, expirationDate, jwsRepresentation,
4225
+ quantity
3983
4226
  });
3984
4227
  return;
3985
4228
  }
@@ -3998,7 +4241,7 @@ var CdvPurchase;
3998
4241
  protectCall(this.options.purchasing, 'options.purchasing', productId);
3999
4242
  return;
4000
4243
  case "PaymentTransactionStatePurchased":
4001
- protectCall(this.options.purchased, 'options.purchased', transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation);
4244
+ protectCall(this.options.purchased, 'options.purchased', transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity);
4002
4245
  return;
4003
4246
  case "PaymentTransactionStateDeferred":
4004
4247
  protectCall(this.options.deferred, 'options.deferred', productId);
@@ -4008,7 +4251,10 @@ var CdvPurchase;
4008
4251
  protectCall(this.options.error, 'options.error', errorCode || CdvPurchase.ErrorCode.UNKNOWN, errorText || 'ERROR', { productId });
4009
4252
  return;
4010
4253
  case "PaymentTransactionStateRestored":
4011
- protectCall(this.options.restored, 'options.restored', transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation);
4254
+ // Note: quantity is always irrelevant for restored transactions on iOS —
4255
+ // consumable products cannot be restored. Passed through to maintain
4256
+ // positional argument consistency with the purchased callback.
4257
+ protectCall(this.options.restored, 'options.restored', transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, expirationDate, jwsRepresentation, quantity);
4012
4258
  return;
4013
4259
  case "PaymentTransactionStateFinished":
4014
4260
  protectCall(this.options.finished, 'options.finished', transactionIdentifier, productId);
@@ -4050,6 +4296,18 @@ var CdvPurchase;
4050
4296
  this.appStoreReceipt = null;
4051
4297
  exec('appStoreRefreshReceipt', [], loaded, error);
4052
4298
  }
4299
+ /** Retrieve the storefront country code from StoreKit */
4300
+ getStorefront() {
4301
+ return new Promise((resolve) => {
4302
+ // SK2 uses the same native getStorefront action via InAppPurchase plugin
4303
+ window.cordova.exec((countryCode) => {
4304
+ resolve(countryCode || undefined);
4305
+ }, (err) => {
4306
+ log('getStorefront failed: ' + err);
4307
+ resolve(undefined);
4308
+ }, "InAppPurchase", "getStorefront", []);
4309
+ });
4310
+ }
4053
4311
  loadReceipts(callback, errorCb) {
4054
4312
  const loaded = (args) => {
4055
4313
  const data = this.parseReceiptArgs(args);
@@ -4340,7 +4598,7 @@ var CdvPurchase;
4340
4598
  finalizeTransactionUpdates() {
4341
4599
  for (let i = 0; i < this.pendingUpdates.length; ++i) {
4342
4600
  const args = this.pendingUpdates[i];
4343
- this.transactionUpdated(args.state, args.errorCode, args.errorText, args.transactionIdentifier, args.productId, args.transactionReceipt, args.originalTransactionIdentifier, args.transactionDate, args.discountId);
4601
+ this.transactionUpdated(args.state, args.errorCode, args.errorText, args.transactionIdentifier, args.productId, args.transactionReceipt, args.originalTransactionIdentifier, args.transactionDate, args.discountId, args.quantity);
4344
4602
  }
4345
4603
  this.pendingUpdates = [];
4346
4604
  }
@@ -4351,9 +4609,9 @@ var CdvPurchase;
4351
4609
  //
4352
4610
  // Note that it may eventually be called before initialization... unfortunately.
4353
4611
  // In this case, we'll just keep pending updates in a list for later processing.
4354
- transactionUpdated(state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt, originalTransactionIdentifier, transactionDate, discountId) {
4612
+ transactionUpdated(state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt, originalTransactionIdentifier, transactionDate, discountId, quantity) {
4355
4613
  if (!this.initialized) {
4356
- this.pendingUpdates.push({ state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt, originalTransactionIdentifier, transactionDate, discountId });
4614
+ this.pendingUpdates.push({ state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt, originalTransactionIdentifier, transactionDate, discountId, quantity });
4357
4615
  return;
4358
4616
  }
4359
4617
  log("transaction updated:" + transactionIdentifier + " state:" + state + " product:" + productId);
@@ -4370,7 +4628,7 @@ var CdvPurchase;
4370
4628
  protectCall(this.options.purchasing, 'options.purchasing', productId);
4371
4629
  return;
4372
4630
  case "PaymentTransactionStatePurchased":
4373
- protectCall(this.options.purchased, 'options.purchase', transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId);
4631
+ protectCall(this.options.purchased, 'options.purchase', transactionIdentifier, productId, originalTransactionIdentifier, transactionDate, discountId, undefined, undefined, quantity);
4374
4632
  return;
4375
4633
  case "PaymentTransactionStateDeferred":
4376
4634
  protectCall(this.options.deferred, 'options.deferred', productId);
@@ -4380,7 +4638,10 @@ var CdvPurchase;
4380
4638
  protectCall(this.options.error, 'options.error', errorCode || CdvPurchase.ErrorCode.UNKNOWN, errorText || 'ERROR', { productId });
4381
4639
  return;
4382
4640
  case "PaymentTransactionStateRestored":
4383
- protectCall(this.options.restored, 'options.restore', transactionIdentifier, productId);
4641
+ // Note: quantity is always irrelevant for restored transactions on iOS —
4642
+ // consumable products cannot be restored. Passed through to maintain
4643
+ // positional argument consistency with the purchased callback.
4644
+ protectCall(this.options.restored, 'options.restore', transactionIdentifier, productId, undefined, undefined, undefined, undefined, undefined, quantity);
4384
4645
  return;
4385
4646
  case "PaymentTransactionStateFinished":
4386
4647
  protectCall(this.options.finished, 'options.finish', transactionIdentifier, productId);
@@ -4433,6 +4694,17 @@ var CdvPurchase;
4433
4694
  log('refreshing appStoreReceipt');
4434
4695
  exec('appStoreRefreshReceipt', [], loaded, error);
4435
4696
  }
4697
+ /** Retrieve the storefront country code from StoreKit */
4698
+ getStorefront() {
4699
+ return new Promise((resolve) => {
4700
+ exec('getStorefront', [], (countryCode) => {
4701
+ resolve(countryCode || undefined);
4702
+ }, (err) => {
4703
+ log('getStorefront failed: ' + err);
4704
+ resolve(undefined);
4705
+ });
4706
+ });
4707
+ }
4436
4708
  loadReceipts(callback, errorCb) {
4437
4709
  const loaded = (args) => {
4438
4710
  const data = this.parseReceiptArgs(args);
@@ -4633,7 +4905,7 @@ var CdvPurchase;
4633
4905
  AppleAppStore.SKApplicationReceipt = SKApplicationReceipt;
4634
4906
  /** StoreKit transaction */
4635
4907
  class SKTransaction extends CdvPurchase.Transaction {
4636
- refresh(productId, originalTransactionIdentifier, transactionDate, discountId, expirationDateMs, jwsRepresentation) {
4908
+ refresh(productId, originalTransactionIdentifier, transactionDate, discountId, expirationDateMs, jwsRepresentation, quantity) {
4637
4909
  if (productId)
4638
4910
  this.products = [{ id: productId, offerId: discountId }];
4639
4911
  if (originalTransactionIdentifier)
@@ -4644,6 +4916,8 @@ var CdvPurchase;
4644
4916
  this.expirationDate = new Date(+expirationDateMs);
4645
4917
  if (jwsRepresentation)
4646
4918
  this.jwsRepresentation = jwsRepresentation;
4919
+ if (quantity !== undefined)
4920
+ this.quantity = quantity;
4647
4921
  }
4648
4922
  }
4649
4923
  AppleAppStore.SKTransaction = SKTransaction;
@@ -6033,9 +6307,21 @@ var CdvPurchase;
6033
6307
  return;
6034
6308
  });
6035
6309
  }
6310
+ getStorefront() {
6311
+ return __awaiter(this, void 0, void 0, function* () {
6312
+ return new Promise((resolve) => {
6313
+ this.bridge.getStorefront((countryCode) => {
6314
+ resolve(countryCode || undefined);
6315
+ }, (message) => {
6316
+ this.log.warn('getStorefront failed: ' + message);
6317
+ resolve(undefined);
6318
+ });
6319
+ });
6320
+ });
6321
+ }
6036
6322
  checkSupport(functionality) {
6037
6323
  const supported = [
6038
- 'order', 'manageBilling', 'manageSubscriptions'
6324
+ 'order', 'manageBilling', 'manageSubscriptions', 'getStorefront'
6039
6325
  ];
6040
6326
  return supported.indexOf(functionality) >= 0;
6041
6327
  }
@@ -6206,6 +6492,14 @@ var CdvPurchase;
6206
6492
  launchPriceChangeConfirmationFlow(productId) {
6207
6493
  this.plugin.launchPriceChangeConfirmationFlow({ productId });
6208
6494
  }
6495
+ getStorefront(success, fail) {
6496
+ if (this.options.showLog) {
6497
+ log('getStorefront()');
6498
+ }
6499
+ this.plugin.getStorefront()
6500
+ .then((result) => success(result.countryCode))
6501
+ .catch((err) => fail((err === null || err === void 0 ? void 0 : err.message) || 'getStorefront failed', err === null || err === void 0 ? void 0 : err.code));
6502
+ }
6209
6503
  }
6210
6504
  Bridge.CapacitorBridge = CapacitorBridge;
6211
6505
  function ensureObject(obj) {
@@ -6442,6 +6736,12 @@ var CdvPurchase;
6442
6736
  manageBilling() {
6443
6737
  return window.cordova.exec(function () { }, function () { }, "InAppBillingPlugin", "manageBilling", []);
6444
6738
  }
6739
+ getStorefront(success, fail) {
6740
+ if (this.options.showLog) {
6741
+ log('getStorefront()');
6742
+ }
6743
+ return window.cordova.exec(success, errorCb(fail), "InAppBillingPlugin", "getStorefront", []);
6744
+ }
6445
6745
  launchPriceChangeConfirmationFlow(productId) {
6446
6746
  return window.cordova.exec(function () { }, function () { }, "InAppBillingPlugin", "launchPriceChangeConfirmationFlow", [productId]);
6447
6747
  }
@@ -7722,6 +8022,11 @@ var CdvPurchase;
7722
8022
  return undefined;
7723
8023
  });
7724
8024
  }
8025
+ getStorefront() {
8026
+ return __awaiter(this, void 0, void 0, function* () {
8027
+ return 'US';
8028
+ });
8029
+ }
7725
8030
  }
7726
8031
  Test.Adapter = Adapter;
7727
8032
  function testStoreError(code, message, productId) {