capacitor-plugin-cdv-purchase 13.15.1 → 13.15.3
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/{ios/CapacitorPluginPurchase.podspec → CapacitorPluginCdvPurchase.podspec} +2 -2
- package/Package.swift +27 -0
- package/android/src/main/java/cc/fovea/iap/PurchasePlugin.java +3 -2
- package/dist/index.js +154 -19
- package/dist/index.js.map +1 -1
- package/ios/Sources/PurchasePlugin/PurchasePlugin.swift +68 -3
- package/package.json +5 -3
- package/www/store.d.ts +33 -4
- package/www/store.js +154 -19
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require 'json'
|
|
2
2
|
|
|
3
|
-
package = JSON.parse(File.read(File.join(__dir__, '
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
4
4
|
|
|
5
5
|
Pod::Spec.new do |s|
|
|
6
6
|
s.name = 'CapacitorPluginCdvPurchase'
|
|
@@ -10,7 +10,7 @@ Pod::Spec.new do |s|
|
|
|
10
10
|
s.homepage = package['repository']['url']
|
|
11
11
|
s.author = 'Jean-Christophe Hoelt'
|
|
12
12
|
s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
|
|
13
|
-
s.source_files = 'Sources/PurchasePlugin/**/*.{swift,h,m}'
|
|
13
|
+
s.source_files = 'ios/Sources/PurchasePlugin/**/*.{swift,h,m}'
|
|
14
14
|
s.ios.deployment_target = '13.0'
|
|
15
15
|
s.swift_version = '5.9'
|
|
16
16
|
s.dependency 'Capacitor'
|
package/Package.swift
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// swift-tools-version: 5.9
|
|
2
|
+
import PackageDescription
|
|
3
|
+
|
|
4
|
+
let package = Package(
|
|
5
|
+
name: "CapacitorPluginCdvPurchase",
|
|
6
|
+
platforms: [.iOS(.v15)],
|
|
7
|
+
products: [
|
|
8
|
+
.library(
|
|
9
|
+
name: "CapacitorPluginCdvPurchase",
|
|
10
|
+
targets: ["PurchasePlugin"])
|
|
11
|
+
],
|
|
12
|
+
dependencies: [
|
|
13
|
+
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", "6.0.0"..<"10.0.0")
|
|
14
|
+
],
|
|
15
|
+
targets: [
|
|
16
|
+
.target(
|
|
17
|
+
name: "PurchasePlugin",
|
|
18
|
+
dependencies: [
|
|
19
|
+
.product(name: "Capacitor", package: "capacitor-swift-pm"),
|
|
20
|
+
.product(name: "Cordova", package: "capacitor-swift-pm")
|
|
21
|
+
],
|
|
22
|
+
path: "ios/Sources/PurchasePlugin",
|
|
23
|
+
swiftSettings: [
|
|
24
|
+
.enableExperimentalFeature("NonescapableTypes")
|
|
25
|
+
])
|
|
26
|
+
]
|
|
27
|
+
)
|
|
@@ -499,6 +499,7 @@ public class PurchasePlugin extends Plugin implements
|
|
|
499
499
|
.enableOneTimeProducts()
|
|
500
500
|
.enablePrepaidPlans()
|
|
501
501
|
.build())
|
|
502
|
+
.enableAutoServiceReconnection()
|
|
502
503
|
.setListener(this)
|
|
503
504
|
.build();
|
|
504
505
|
|
|
@@ -795,8 +796,8 @@ public class PurchasePlugin extends Plugin implements
|
|
|
795
796
|
mPendingBuyCall = null;
|
|
796
797
|
}
|
|
797
798
|
}
|
|
798
|
-
} catch (
|
|
799
|
-
Log.w(TAG, "onPurchasesUpdated() ->
|
|
799
|
+
} catch (Exception e) {
|
|
800
|
+
Log.w(TAG, "onPurchasesUpdated() -> Exception "
|
|
800
801
|
+ e.getMessage());
|
|
801
802
|
}
|
|
802
803
|
}
|
package/dist/index.js
CHANGED
|
@@ -683,9 +683,39 @@ var CdvPurchase;
|
|
|
683
683
|
body.introPriceMicros = intro.priceMicros;
|
|
684
684
|
}
|
|
685
685
|
}
|
|
686
|
+
// Only include the products array once per day to reduce request size.
|
|
687
|
+
// The products array is used by the validator for analytics (price history,
|
|
688
|
+
// transaction amounts) but not for the validation flow itself.
|
|
689
|
+
// The top-level price/currency fields serve as fallback when products is omitted.
|
|
690
|
+
if (!this.shouldSendProducts()) {
|
|
691
|
+
delete body.products;
|
|
692
|
+
}
|
|
686
693
|
return body;
|
|
687
694
|
});
|
|
688
695
|
}
|
|
696
|
+
/** Check if the products array should be included in the validation request.
|
|
697
|
+
* Returns true at most once per day, tracked via localStorage. */
|
|
698
|
+
shouldSendProducts() {
|
|
699
|
+
var _a, _b;
|
|
700
|
+
const STORAGE_KEY = 'cdvpurchase_has_sent_products_in_validation';
|
|
701
|
+
const ONE_DAY_MS = 86400000;
|
|
702
|
+
try {
|
|
703
|
+
const stored = (_a = window.localStorage) === null || _a === void 0 ? void 0 : _a.getItem(STORAGE_KEY);
|
|
704
|
+
if (stored) {
|
|
705
|
+
const lastSent = parseInt(stored, 10);
|
|
706
|
+
if (!isNaN(lastSent) && (Date.now() - lastSent) < ONE_DAY_MS) {
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
// Send products this time and record the date
|
|
711
|
+
(_b = window.localStorage) === null || _b === void 0 ? void 0 : _b.setItem(STORAGE_KEY, String(Date.now()));
|
|
712
|
+
return true;
|
|
713
|
+
}
|
|
714
|
+
catch (_e) {
|
|
715
|
+
// localStorage not available (e.g. private browsing)
|
|
716
|
+
return true;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
689
719
|
removeExpiredCache() {
|
|
690
720
|
const now = +new Date();
|
|
691
721
|
const deleteList = [];
|
|
@@ -1038,6 +1068,8 @@ var CdvPurchase;
|
|
|
1038
1068
|
/** Those platforms have reported that their receipts are ready */
|
|
1039
1069
|
this.platformWithReceiptsReady = [];
|
|
1040
1070
|
this.lastTransactionState = {};
|
|
1071
|
+
/** Remember the first transactionId for each subscription dedup key */
|
|
1072
|
+
this.subscriptionFirstTransactionId = {};
|
|
1041
1073
|
/** Store the listener's latest calling time (in ms) for a given transaction at a given state */
|
|
1042
1074
|
this.lastCallTimeForState = {};
|
|
1043
1075
|
this.updatedReceiptsToProcess = [];
|
|
@@ -1047,6 +1079,26 @@ var CdvPurchase;
|
|
|
1047
1079
|
static makeTransactionToken(transaction) {
|
|
1048
1080
|
return transaction.platform + '|' + transaction.transactionId;
|
|
1049
1081
|
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Create a subscription dedup key from a transaction.
|
|
1084
|
+
*
|
|
1085
|
+
* StoreKit 2 can deliver the same subscription purchase event twice with
|
|
1086
|
+
* different `transactionId` but identical `originalTransactionId` and
|
|
1087
|
+
* `purchaseDate`. This key groups those duplicates together so only one
|
|
1088
|
+
* `approved`/`finished` event is surfaced per billing period.
|
|
1089
|
+
*
|
|
1090
|
+
* Returns `undefined` for non-subscription transactions (no `originalTransactionId`).
|
|
1091
|
+
*/
|
|
1092
|
+
static makeSubscriptionKey(transaction) {
|
|
1093
|
+
if (transaction.platform !== CdvPurchase.Platform.APPLE_APPSTORE)
|
|
1094
|
+
return undefined;
|
|
1095
|
+
const skTransaction = transaction;
|
|
1096
|
+
if (!skTransaction.originalTransactionId)
|
|
1097
|
+
return undefined;
|
|
1098
|
+
if (!transaction.purchaseDate)
|
|
1099
|
+
return undefined;
|
|
1100
|
+
return transaction.platform + '|' + skTransaction.originalTransactionId + '|' + transaction.purchaseDate.getTime();
|
|
1101
|
+
}
|
|
1050
1102
|
/**
|
|
1051
1103
|
* Set the list of supported platforms.
|
|
1052
1104
|
*
|
|
@@ -1126,16 +1178,32 @@ var CdvPurchase;
|
|
|
1126
1178
|
const transactionToken = StoreAdapterListener.makeTransactionToken(transaction);
|
|
1127
1179
|
const tokenWithState = transactionToken + '@' + transaction.state;
|
|
1128
1180
|
const lastState = this.lastTransactionState[transactionToken];
|
|
1181
|
+
const subscriptionKey = StoreAdapterListener.makeSubscriptionKey(transaction);
|
|
1182
|
+
const isSubscriptionDuplicate = !!subscriptionKey
|
|
1183
|
+
&& !!this.subscriptionFirstTransactionId[subscriptionKey]
|
|
1184
|
+
&& this.subscriptionFirstTransactionId[subscriptionKey] !== transaction.transactionId;
|
|
1185
|
+
// Remember the first transactionId for this subscription key
|
|
1186
|
+
if (subscriptionKey && !this.subscriptionFirstTransactionId[subscriptionKey]) {
|
|
1187
|
+
this.subscriptionFirstTransactionId[subscriptionKey] = transaction.transactionId;
|
|
1188
|
+
}
|
|
1129
1189
|
// Retrigger "approved", so validation is rerun on potential update.
|
|
1130
1190
|
if (transaction.state === CdvPurchase.TransactionState.APPROVED) {
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
this.
|
|
1135
|
-
this.delegate.approvedCallbacks.trigger(transaction, 'adapterListener_receiptsUpdated_approved');
|
|
1191
|
+
if (isSubscriptionDuplicate) {
|
|
1192
|
+
// Finish the duplicate at the native level so StoreKit won't re-deliver it
|
|
1193
|
+
this.log.debug(`Auto-finishing subscription duplicate ${transactionToken}, already processed as ${this.subscriptionFirstTransactionId[subscriptionKey]}`);
|
|
1194
|
+
this.delegate.finishDuplicate(transaction);
|
|
1136
1195
|
}
|
|
1137
1196
|
else {
|
|
1138
|
-
|
|
1197
|
+
// Use subscription key for dedup when available, otherwise fall back to transactionToken
|
|
1198
|
+
const dedupKey = subscriptionKey ? subscriptionKey + '@' + transaction.state : tokenWithState;
|
|
1199
|
+
const lastCalled = (_a = this.lastCallTimeForState[dedupKey]) !== null && _a !== void 0 ? _a : 0;
|
|
1200
|
+
if (now - lastCalled > 60000) {
|
|
1201
|
+
this.lastCallTimeForState[dedupKey] = now;
|
|
1202
|
+
this.delegate.approvedCallbacks.trigger(transaction, 'adapterListener_receiptsUpdated_approved');
|
|
1203
|
+
}
|
|
1204
|
+
else {
|
|
1205
|
+
this.log.debug(`Skipping ${tokenWithState}, because it has been last called ${lastCalled > 0 ? Math.round(now - lastCalled) + 'ms ago (' + now + '-' + lastCalled + ')' : 'never'}`);
|
|
1206
|
+
}
|
|
1139
1207
|
}
|
|
1140
1208
|
}
|
|
1141
1209
|
else if (lastState !== transaction.state) {
|
|
@@ -1144,15 +1212,31 @@ var CdvPurchase;
|
|
|
1144
1212
|
this.delegate.initiatedCallbacks.trigger(transaction, 'adapterListener_receiptsUpdated_initiated');
|
|
1145
1213
|
}
|
|
1146
1214
|
else if (transaction.state === CdvPurchase.TransactionState.FINISHED) {
|
|
1147
|
-
|
|
1148
|
-
|
|
1215
|
+
if (isSubscriptionDuplicate) {
|
|
1216
|
+
// Finish the duplicate at the native level so StoreKit won't re-deliver it
|
|
1217
|
+
this.log.debug(`Auto-finishing subscription duplicate ${transactionToken}, already processed as ${this.subscriptionFirstTransactionId[subscriptionKey]}`);
|
|
1218
|
+
this.delegate.finishDuplicate(transaction);
|
|
1219
|
+
}
|
|
1220
|
+
else {
|
|
1221
|
+
this.lastCallTimeForState[tokenWithState] = now;
|
|
1222
|
+
this.delegate.finishedCallbacks.trigger(transaction, 'adapterListener_receiptsUpdated_finished');
|
|
1223
|
+
}
|
|
1149
1224
|
}
|
|
1150
1225
|
else if (transaction.state === CdvPurchase.TransactionState.PENDING) {
|
|
1151
1226
|
this.lastCallTimeForState[tokenWithState] = now;
|
|
1152
1227
|
this.delegate.pendingCallbacks.trigger(transaction, 'adapterListener_receiptsUpdated_pending');
|
|
1153
1228
|
}
|
|
1154
1229
|
}
|
|
1155
|
-
|
|
1230
|
+
// Mark subscription duplicates as FINISHED so the re-processing triggered
|
|
1231
|
+
// by finishDuplicate()'s receiptsUpdated.call() won't enter the FINISHED
|
|
1232
|
+
// handler and call finishDuplicate() a second time on an already-finished
|
|
1233
|
+
// native transaction.
|
|
1234
|
+
if (isSubscriptionDuplicate) {
|
|
1235
|
+
this.lastTransactionState[transactionToken] = CdvPurchase.TransactionState.FINISHED;
|
|
1236
|
+
}
|
|
1237
|
+
else {
|
|
1238
|
+
this.lastTransactionState[transactionToken] = transaction.state;
|
|
1239
|
+
}
|
|
1156
1240
|
});
|
|
1157
1241
|
});
|
|
1158
1242
|
}
|
|
@@ -1561,7 +1645,7 @@ var CdvPurchase;
|
|
|
1561
1645
|
/**
|
|
1562
1646
|
* Current release number of the plugin.
|
|
1563
1647
|
*/
|
|
1564
|
-
CdvPurchase.PLUGIN_VERSION = '13.15.
|
|
1648
|
+
CdvPurchase.PLUGIN_VERSION = '13.15.3';
|
|
1565
1649
|
/**
|
|
1566
1650
|
* Entry class of the plugin.
|
|
1567
1651
|
*/
|
|
@@ -1641,6 +1725,12 @@ var CdvPurchase;
|
|
|
1641
1725
|
finishedCallbacks: this.finishedCallbacks,
|
|
1642
1726
|
pendingCallbacks: this.pendingCallbacks,
|
|
1643
1727
|
receiptsReadyCallbacks: this.receiptsReadyCallbacks,
|
|
1728
|
+
finishDuplicate: (transaction) => {
|
|
1729
|
+
const adapter = this.adapters.findReady(transaction.platform);
|
|
1730
|
+
if (adapter) {
|
|
1731
|
+
adapter.finish(transaction);
|
|
1732
|
+
}
|
|
1733
|
+
},
|
|
1644
1734
|
}, this.log);
|
|
1645
1735
|
this.transactionStateMonitors = new CdvPurchase.Internal.TransactionStateMonitors(this.when());
|
|
1646
1736
|
this._validator = new CdvPurchase.Internal.Validator({
|
|
@@ -3031,9 +3121,14 @@ var CdvPurchase;
|
|
|
3031
3121
|
this.initializeAppReceipt(() => {
|
|
3032
3122
|
var _a;
|
|
3033
3123
|
if (!this._receipt) {
|
|
3034
|
-
//
|
|
3035
|
-
|
|
3036
|
-
|
|
3124
|
+
// Receipt failed to load — create a minimal receipt so the
|
|
3125
|
+
// transaction can still be tracked and finished. Without this
|
|
3126
|
+
// fallback the Promise would never resolve, leaving the native
|
|
3127
|
+
// transaction unfinished (causing the purchase dialog to loop on
|
|
3128
|
+
// iOS — see #1568).
|
|
3129
|
+
this.log.warn('Application receipt unavailable, creating a fallback receipt to avoid blocking transactions');
|
|
3130
|
+
this._receipt = new AppleAppStore.SKApplicationReceipt({ appStoreReceipt: '', bundleIdentifier: '',
|
|
3131
|
+
bundleShortVersion: '', bundleNumericVersion: 0, bundleSignature: '' }, this.needAppReceipt, this.context.apiDecorators);
|
|
3037
3132
|
}
|
|
3038
3133
|
const existing = (_a = this._receipt) === null || _a === void 0 ? void 0 : _a.transactions.find(t => t.transactionId === transactionId);
|
|
3039
3134
|
if (existing) {
|
|
@@ -3195,8 +3290,22 @@ var CdvPurchase;
|
|
|
3195
3290
|
});
|
|
3196
3291
|
}
|
|
3197
3292
|
loadReceipts() {
|
|
3198
|
-
return
|
|
3199
|
-
|
|
3293
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3294
|
+
// Wait for native pending transactions to be processed before
|
|
3295
|
+
// initializing the receipt. Previously used a fixed 300ms delay
|
|
3296
|
+
// which was unreliable and could miss transactions that arrived
|
|
3297
|
+
// from the native queue (see #1529).
|
|
3298
|
+
//
|
|
3299
|
+
// The pendingTransactionsReady Promise resolves when the native
|
|
3300
|
+
// `processPendingTransactions` call returns, but the per-transaction
|
|
3301
|
+
// callbacks it triggers via evalJs land asynchronously. Yield one
|
|
3302
|
+
// task so those purchased/restored callbacks (and their async
|
|
3303
|
+
// upsertTransaction chain) settle before we snapshot _receipt.
|
|
3304
|
+
if (this.bridge.pendingTransactionsReady) {
|
|
3305
|
+
yield this.bridge.pendingTransactionsReady;
|
|
3306
|
+
yield new Promise(r => setTimeout(r, 0));
|
|
3307
|
+
}
|
|
3308
|
+
return new Promise((resolve) => {
|
|
3200
3309
|
this.initializeAppReceipt(() => {
|
|
3201
3310
|
this.receiptsUpdated.call();
|
|
3202
3311
|
if (this._receipt) {
|
|
@@ -3206,7 +3315,7 @@ var CdvPurchase;
|
|
|
3206
3315
|
resolve([this.pseudoReceipt]);
|
|
3207
3316
|
}
|
|
3208
3317
|
});
|
|
3209
|
-
}
|
|
3318
|
+
});
|
|
3210
3319
|
});
|
|
3211
3320
|
}
|
|
3212
3321
|
canMakePayments() {
|
|
@@ -3262,6 +3371,7 @@ var CdvPurchase;
|
|
|
3262
3371
|
return;
|
|
3263
3372
|
}
|
|
3264
3373
|
this._receipt = new AppleAppStore.SKApplicationReceipt(nativeData, this.needAppReceipt, this.context.apiDecorators);
|
|
3374
|
+
this._appStoreReceiptLoading = false;
|
|
3265
3375
|
callCallbacks(undefined);
|
|
3266
3376
|
});
|
|
3267
3377
|
}
|
|
@@ -3475,16 +3585,20 @@ var CdvPurchase;
|
|
|
3475
3585
|
this.receiptsUpdated.call();
|
|
3476
3586
|
return resolve(undefined);
|
|
3477
3587
|
}
|
|
3588
|
+
const wasAlreadyFinished = transaction.state === CdvPurchase.TransactionState.FINISHED;
|
|
3478
3589
|
const success = () => {
|
|
3479
3590
|
transaction.state = CdvPurchase.TransactionState.FINISHED;
|
|
3480
|
-
|
|
3591
|
+
if (!wasAlreadyFinished) {
|
|
3592
|
+
this.receiptsUpdated.call();
|
|
3593
|
+
}
|
|
3481
3594
|
resolve(undefined);
|
|
3482
3595
|
};
|
|
3483
3596
|
const error = (msg) => {
|
|
3484
3597
|
var _a, _b;
|
|
3485
3598
|
if (msg === null || msg === void 0 ? void 0 : msg.includes('[#CdvPurchase:100]')) {
|
|
3486
|
-
// already finished
|
|
3487
|
-
|
|
3599
|
+
// already finished at the native level
|
|
3600
|
+
transaction.state = CdvPurchase.TransactionState.FINISHED;
|
|
3601
|
+
resolve(undefined);
|
|
3488
3602
|
}
|
|
3489
3603
|
else {
|
|
3490
3604
|
resolve(appStoreError(CdvPurchase.ErrorCode.FINISH, 'Failed to finish transaction', (_b = (_a = transaction.products[0]) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null));
|
|
@@ -4046,6 +4160,9 @@ var CdvPurchase;
|
|
|
4046
4160
|
protectCall(this.options.ready, 'options.ready');
|
|
4047
4161
|
protectCall(success, 'init.success');
|
|
4048
4162
|
this.initialized = true;
|
|
4163
|
+
this.pendingTransactionsReady = new Promise(resolve => {
|
|
4164
|
+
this._pendingTransactionsResolve = resolve;
|
|
4165
|
+
});
|
|
4049
4166
|
setTimeout(() => this.processPendingTransactions(), 50);
|
|
4050
4167
|
};
|
|
4051
4168
|
const setupFailed = (err) => {
|
|
@@ -4058,6 +4175,10 @@ var CdvPurchase;
|
|
|
4058
4175
|
log('processing pending transactions');
|
|
4059
4176
|
exec('processPendingTransactions', [], () => {
|
|
4060
4177
|
this.finalizeTransactionUpdates();
|
|
4178
|
+
if (this._pendingTransactionsResolve) {
|
|
4179
|
+
this._pendingTransactionsResolve();
|
|
4180
|
+
this._pendingTransactionsResolve = undefined;
|
|
4181
|
+
}
|
|
4061
4182
|
}, undefined);
|
|
4062
4183
|
}
|
|
4063
4184
|
purchase(productId, quantity, applicationUsername, discount, success, error) {
|
|
@@ -4365,6 +4486,9 @@ var CdvPurchase;
|
|
|
4365
4486
|
protectCall(this.options.ready, 'options.ready');
|
|
4366
4487
|
protectCall(success, 'init.success');
|
|
4367
4488
|
this.initialized = true;
|
|
4489
|
+
this.pendingTransactionsReady = new Promise(resolve => {
|
|
4490
|
+
this._pendingTransactionsResolve = resolve;
|
|
4491
|
+
});
|
|
4368
4492
|
setTimeout(() => this.processPendingTransactions(), 50);
|
|
4369
4493
|
};
|
|
4370
4494
|
const setupFailed = (err) => {
|
|
@@ -4378,6 +4502,10 @@ var CdvPurchase;
|
|
|
4378
4502
|
log('processing pending transactions');
|
|
4379
4503
|
exec('processPendingTransactions', [], () => {
|
|
4380
4504
|
this.finalizeTransactionUpdates();
|
|
4505
|
+
if (this._pendingTransactionsResolve) {
|
|
4506
|
+
this._pendingTransactionsResolve();
|
|
4507
|
+
this._pendingTransactionsResolve = undefined;
|
|
4508
|
+
}
|
|
4381
4509
|
}, undefined);
|
|
4382
4510
|
}
|
|
4383
4511
|
/**
|
|
@@ -4757,6 +4885,13 @@ var CdvPurchase;
|
|
|
4757
4885
|
offerType: 'Default',
|
|
4758
4886
|
}, decorator));
|
|
4759
4887
|
}
|
|
4888
|
+
// Ensure the default offer is always first so that getOffer()
|
|
4889
|
+
// without arguments returns it instead of a discount offer.
|
|
4890
|
+
const defaultIndex = this.offers.findIndex(o => o.id === AppleAppStore.DEFAULT_OFFER_ID);
|
|
4891
|
+
if (defaultIndex > 0) {
|
|
4892
|
+
const [defaultOffer] = this.offers.splice(defaultIndex, 1);
|
|
4893
|
+
this.offers.unshift(defaultOffer);
|
|
4894
|
+
}
|
|
4760
4895
|
function hasIntroductoryOffer(product) {
|
|
4761
4896
|
return product.offers.filter(offer => {
|
|
4762
4897
|
const skOffer = offer;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["esm/index.js","../build/entry.mjs"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\n// Set the marker so the shared store.js knows Capacitor native bridges are available\nif (typeof window !== 'undefined') {\n window.CdvPurchaseCapacitor = { installed: true };\n}\nconst PurchasePlugin = registerPlugin('PurchasePlugin');\nexport { PurchasePlugin };\n","// Capacitor bridge — sets CdvPurchaseCapacitor marker and registers native plugin\nexport { PurchasePlugin } from '../dist/esm/index.js';\n\n// Store runtime — executes the IIFE, creates window.CdvPurchase\nimport { CdvPurchase } from 'virtual:store-js';\n\n// Re-export the full namespace\nexport { CdvPurchase };\n\n// Convenience named exports\nexport const store = CdvPurchase.store;\nexport const Store = CdvPurchase.Store;\nexport const ProductType = CdvPurchase.ProductType;\nexport const Platform = CdvPurchase.Platform;\nexport const LogLevel = CdvPurchase.LogLevel;\nexport const ErrorCode = CdvPurchase.ErrorCode;\nexport const Logger = CdvPurchase.Logger;\nexport const Iaptic = CdvPurchase.Iaptic;\n"],"names":[],"mappings":";;AACA;AACA,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;AACnC,IAAI,MAAM,CAAC,oBAAoB,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE;AACrD;AACK,MAAC,cAAc,GAAG,cAAc,CAAC,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACLtD;;AASA;AACY,MAAC,KAAK,GAAG,WAAW,CAAC;AACrB,MAAC,KAAK,GAAG,WAAW,CAAC;AACrB,MAAC,WAAW,GAAG,WAAW,CAAC;AAC3B,MAAC,QAAQ,GAAG,WAAW,CAAC;AACxB,MAAC,QAAQ,GAAG,WAAW,CAAC;AACxB,MAAC,SAAS,GAAG,WAAW,CAAC;AACzB,MAAC,MAAM,GAAG,WAAW,CAAC;AACtB,MAAC,MAAM,GAAG,WAAW,CAAC;;;;"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["esm/index.js","../build/entry.mjs"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\n// Set the marker so the shared store.js knows Capacitor native bridges are available\nif (typeof window !== 'undefined') {\n window.CdvPurchaseCapacitor = { installed: true };\n}\nconst PurchasePlugin = registerPlugin('PurchasePlugin');\nexport { PurchasePlugin };\n","// Capacitor bridge — sets CdvPurchaseCapacitor marker and registers native plugin\nexport { PurchasePlugin } from '../dist/esm/index.js';\n\n// Store runtime — executes the IIFE, creates window.CdvPurchase\nimport { CdvPurchase } from 'virtual:store-js';\n\n// Re-export the full namespace\nexport { CdvPurchase };\n\n// Convenience named exports\nexport const store = CdvPurchase.store;\nexport const Store = CdvPurchase.Store;\nexport const ProductType = CdvPurchase.ProductType;\nexport const Platform = CdvPurchase.Platform;\nexport const LogLevel = CdvPurchase.LogLevel;\nexport const ErrorCode = CdvPurchase.ErrorCode;\nexport const Logger = CdvPurchase.Logger;\nexport const Iaptic = CdvPurchase.Iaptic;\n"],"names":[],"mappings":";;AACA;AACA,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;AACnC,IAAI,MAAM,CAAC,oBAAoB,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE;AACrD;AACK,MAAC,cAAc,GAAG,cAAc,CAAC,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACLtD;;AASA;AACY,MAAC,KAAK,GAAG,WAAW,CAAC;AACrB,MAAC,KAAK,GAAG,WAAW,CAAC;AACrB,MAAC,WAAW,GAAG,WAAW,CAAC;AAC3B,MAAC,QAAQ,GAAG,WAAW,CAAC;AACxB,MAAC,QAAQ,GAAG,WAAW,CAAC;AACxB,MAAC,SAAS,GAAG,WAAW,CAAC;AACzB,MAAC,MAAM,GAAG,WAAW,CAAC;AACtB,MAAC,MAAM,GAAG,WAAW,CAAC;;;;"}
|
|
@@ -7,6 +7,10 @@ private class SK2State {
|
|
|
7
7
|
var products: [String: Product] = [:]
|
|
8
8
|
var unfinishedTransactions: [String: Transaction] = [:]
|
|
9
9
|
var transactionObserverTask: Task<Void, Never>?
|
|
10
|
+
/// Transaction IDs already emitted to JS. Prevents duplicate delivery when
|
|
11
|
+
/// both init() (via currentEntitlements) and Transaction.updates deliver
|
|
12
|
+
/// the same transaction. Access must be serialized on the main thread.
|
|
13
|
+
var processedTransactionIds: Set<UInt64> = []
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
@objc(PurchasePlugin)
|
|
@@ -69,6 +73,18 @@ public class PurchasePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
69
73
|
let jwsRepresentation = result.jwsRepresentation
|
|
70
74
|
switch result {
|
|
71
75
|
case .verified(let transaction):
|
|
76
|
+
// Serialize access to processedTransactionIds on the main thread
|
|
77
|
+
// since this method runs from a detached Task while init()/purchase()
|
|
78
|
+
// also mutate the set.
|
|
79
|
+
let shouldEmit: Bool = await MainActor.run {
|
|
80
|
+
if sk2.processedTransactionIds.contains(transaction.id) {
|
|
81
|
+
debugLog("Transaction.updates: skipping duplicate id=\(transaction.id)")
|
|
82
|
+
return false
|
|
83
|
+
}
|
|
84
|
+
sk2.processedTransactionIds.insert(transaction.id)
|
|
85
|
+
return true
|
|
86
|
+
}
|
|
87
|
+
guard shouldEmit else { return }
|
|
72
88
|
await emitTransactionUpdate(transaction, state: "PaymentTransactionStatePurchased",
|
|
73
89
|
jwsRepresentation: jwsRepresentation)
|
|
74
90
|
case .unverified(let transaction, _):
|
|
@@ -88,11 +104,27 @@ public class PurchasePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
88
104
|
debugEnabled = call.getBool("debug", false)
|
|
89
105
|
debugLog("init")
|
|
90
106
|
|
|
91
|
-
//
|
|
107
|
+
// Load current entitlements and emit to JS so existing subscriptions
|
|
108
|
+
// are visible immediately on app launch (without requiring a manual restore).
|
|
92
109
|
Task {
|
|
93
110
|
for await result in Transaction.currentEntitlements {
|
|
94
|
-
|
|
111
|
+
switch result {
|
|
112
|
+
case .verified(let transaction):
|
|
113
|
+
if transaction.isUpgraded {
|
|
114
|
+
debugLog("init: skipping upgraded entitlement id=\(transaction.id) product=\(transaction.productID)")
|
|
115
|
+
continue
|
|
116
|
+
}
|
|
117
|
+
if self.sk2.processedTransactionIds.contains(transaction.id) {
|
|
118
|
+
debugLog("init: skipping already-processed entitlement id=\(transaction.id)")
|
|
119
|
+
continue
|
|
120
|
+
}
|
|
121
|
+
self.sk2.processedTransactionIds.insert(transaction.id)
|
|
95
122
|
self.sk2.unfinishedTransactions[String(transaction.id)] = transaction
|
|
123
|
+
await self.emitTransactionUpdate(transaction,
|
|
124
|
+
state: "PaymentTransactionStateRestored",
|
|
125
|
+
jwsRepresentation: result.jwsRepresentation)
|
|
126
|
+
case .unverified(let transaction, let error):
|
|
127
|
+
debugLog("init: unverified entitlement id=\(transaction.id) product=\(transaction.productID) error=\(error)")
|
|
96
128
|
}
|
|
97
129
|
}
|
|
98
130
|
call.resolve()
|
|
@@ -162,12 +194,16 @@ public class PurchasePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
162
194
|
options.insert(.quantity(quantity))
|
|
163
195
|
}
|
|
164
196
|
|
|
197
|
+
// Clear expired unfinished transactions that could block the purchase
|
|
198
|
+
await clearExpiredUnfinishedTransactions()
|
|
199
|
+
|
|
165
200
|
let result = try await product.purchase(options: options)
|
|
166
201
|
switch result {
|
|
167
202
|
case .success(let verification):
|
|
168
203
|
let jwsRepresentation = verification.jwsRepresentation
|
|
169
204
|
switch verification {
|
|
170
205
|
case .verified(let transaction):
|
|
206
|
+
sk2.processedTransactionIds.insert(transaction.id)
|
|
171
207
|
await emitTransactionUpdate(transaction,
|
|
172
208
|
state: "PaymentTransactionStatePurchased",
|
|
173
209
|
jwsRepresentation: jwsRepresentation)
|
|
@@ -218,6 +254,10 @@ public class PurchasePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
218
254
|
if let transaction = sk2.unfinishedTransactions[transactionId] {
|
|
219
255
|
await transaction.finish()
|
|
220
256
|
sk2.unfinishedTransactions.removeValue(forKey: transactionId)
|
|
257
|
+
// Note: do NOT remove from processedTransactionIds here.
|
|
258
|
+
// Once a transaction ID has been emitted to JS, it must stay in the set
|
|
259
|
+
// for the lifetime of the session to prevent Transaction.updates from
|
|
260
|
+
// re-delivering it.
|
|
221
261
|
notifyListeners("transactionUpdated", data: [
|
|
222
262
|
"state": "PaymentTransactionStateFinished",
|
|
223
263
|
"transactionIdentifier": transactionId,
|
|
@@ -247,10 +287,18 @@ public class PurchasePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
247
287
|
do {
|
|
248
288
|
try await AppStore.sync()
|
|
249
289
|
for await result in Transaction.currentEntitlements {
|
|
250
|
-
|
|
290
|
+
switch result {
|
|
291
|
+
case .verified(let transaction):
|
|
292
|
+
if transaction.isUpgraded {
|
|
293
|
+
debugLog("restore: skipping upgraded entitlement id=\(transaction.id) product=\(transaction.productID)")
|
|
294
|
+
continue
|
|
295
|
+
}
|
|
296
|
+
sk2.processedTransactionIds.insert(transaction.id)
|
|
251
297
|
await emitTransactionUpdate(transaction,
|
|
252
298
|
state: "PaymentTransactionStateRestored",
|
|
253
299
|
jwsRepresentation: result.jwsRepresentation)
|
|
300
|
+
case .unverified(let transaction, let error):
|
|
301
|
+
debugLog("restore: unverified entitlement id=\(transaction.id) product=\(transaction.productID) error=\(error)")
|
|
254
302
|
}
|
|
255
303
|
}
|
|
256
304
|
notifyListeners("restoreCompleted", data: [:])
|
|
@@ -346,6 +394,23 @@ public class PurchasePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
346
394
|
|
|
347
395
|
// MARK: - Helpers
|
|
348
396
|
|
|
397
|
+
/// Finish any unfinished transactions whose subscription has already expired.
|
|
398
|
+
/// Stale unfinished transactions can block product.purchase() from initiating
|
|
399
|
+
/// a new purchase flow (confirmed on Apple Developer Forums).
|
|
400
|
+
@available(iOS 15.0, *)
|
|
401
|
+
private func clearExpiredUnfinishedTransactions() async {
|
|
402
|
+
for await result in Transaction.unfinished {
|
|
403
|
+
guard case .verified(let transaction) = result else { continue }
|
|
404
|
+
if let expirationDate = transaction.expirationDate, expirationDate < Date() {
|
|
405
|
+
debugLog("clearExpired: finishing expired transaction id=\(transaction.id) product=\(transaction.productID) expired=\(expirationDate)")
|
|
406
|
+
await transaction.finish()
|
|
407
|
+
sk2.unfinishedTransactions.removeValue(forKey: String(transaction.id))
|
|
408
|
+
// Note: do NOT remove from processedTransactionIds.
|
|
409
|
+
// The set must grow monotonically to prevent re-delivery via Transaction.updates.
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
349
414
|
@available(iOS 15.0, *)
|
|
350
415
|
private func emitTransactionUpdate(_ transaction: Transaction, state: String,
|
|
351
416
|
errorCode: Int? = nil, errorText: String? = nil,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "capacitor-plugin-cdv-purchase",
|
|
3
|
-
"version": "13.15.
|
|
3
|
+
"version": "13.15.3",
|
|
4
4
|
"description": "In-App Purchases for Capacitor — iOS (StoreKit 2) and Android (Google Play Billing)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"android": { "src": "android" }
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"@capacitor/core": "^6.0.0"
|
|
20
|
+
"@capacitor/core": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@capacitor/core": "^6.0.0",
|
|
@@ -35,7 +35,9 @@
|
|
|
35
35
|
"www/",
|
|
36
36
|
"android/",
|
|
37
37
|
"!android/build",
|
|
38
|
-
"ios/"
|
|
38
|
+
"ios/",
|
|
39
|
+
"Package.swift",
|
|
40
|
+
"CapacitorPluginCdvPurchase.podspec"
|
|
39
41
|
],
|
|
40
42
|
"license": "MIT",
|
|
41
43
|
"repository": {
|
package/www/store.d.ts
CHANGED
|
@@ -376,6 +376,9 @@ declare namespace CdvPurchase {
|
|
|
376
376
|
private runOnReceipt;
|
|
377
377
|
private runValidatorFunction;
|
|
378
378
|
private buildRequestBody;
|
|
379
|
+
/** Check if the products array should be included in the validation request.
|
|
380
|
+
* Returns true at most once per day, tracked via localStorage. */
|
|
381
|
+
private shouldSendProducts;
|
|
379
382
|
/**
|
|
380
383
|
* For each md5-hashed values of the validator request's ".transaction" field,
|
|
381
384
|
* store the response from the server.
|
|
@@ -564,6 +567,8 @@ declare namespace CdvPurchase {
|
|
|
564
567
|
updatedCallbacks: Callbacks<Product>;
|
|
565
568
|
updatedReceiptCallbacks: Callbacks<Receipt>;
|
|
566
569
|
receiptsReadyCallbacks: Callbacks<void>;
|
|
570
|
+
/** Finish a duplicate subscription transaction at the native level so StoreKit won't re-deliver it */
|
|
571
|
+
finishDuplicate(transaction: Transaction): void;
|
|
567
572
|
}
|
|
568
573
|
/**
|
|
569
574
|
* Monitor the updates for products and receipt.
|
|
@@ -582,6 +587,21 @@ declare namespace CdvPurchase {
|
|
|
582
587
|
[transactionToken: string]: TransactionState;
|
|
583
588
|
};
|
|
584
589
|
static makeTransactionToken(transaction: Transaction): string;
|
|
590
|
+
/**
|
|
591
|
+
* Create a subscription dedup key from a transaction.
|
|
592
|
+
*
|
|
593
|
+
* StoreKit 2 can deliver the same subscription purchase event twice with
|
|
594
|
+
* different `transactionId` but identical `originalTransactionId` and
|
|
595
|
+
* `purchaseDate`. This key groups those duplicates together so only one
|
|
596
|
+
* `approved`/`finished` event is surfaced per billing period.
|
|
597
|
+
*
|
|
598
|
+
* Returns `undefined` for non-subscription transactions (no `originalTransactionId`).
|
|
599
|
+
*/
|
|
600
|
+
static makeSubscriptionKey(transaction: Transaction): string | undefined;
|
|
601
|
+
/** Remember the first transactionId for each subscription dedup key */
|
|
602
|
+
subscriptionFirstTransactionId: {
|
|
603
|
+
[subscriptionKey: string]: string;
|
|
604
|
+
};
|
|
585
605
|
/** Store the listener's latest calling time (in ms) for a given transaction at a given state */
|
|
586
606
|
lastCallTimeForState: {
|
|
587
607
|
[transactionTokenWithState: string]: number;
|
|
@@ -696,7 +716,9 @@ declare namespace CdvPurchase {
|
|
|
696
716
|
/**
|
|
697
717
|
* Name of the group your subscription product is a member of.
|
|
698
718
|
*
|
|
699
|
-
*
|
|
719
|
+
* When set, purchasing a subscription in a group will replace the currently
|
|
720
|
+
* owned one (on Google Play, this triggers the subscription replacement flow).
|
|
721
|
+
* When not set, subscriptions are independent — no automatic replacement.
|
|
700
722
|
*/
|
|
701
723
|
group?: string;
|
|
702
724
|
}
|
|
@@ -864,7 +886,7 @@ declare namespace CdvPurchase {
|
|
|
864
886
|
/**
|
|
865
887
|
* Current release number of the plugin.
|
|
866
888
|
*/
|
|
867
|
-
const PLUGIN_VERSION = "13.15.
|
|
889
|
+
const PLUGIN_VERSION = "13.15.3";
|
|
868
890
|
/**
|
|
869
891
|
* Entry class of the plugin.
|
|
870
892
|
*/
|
|
@@ -2812,6 +2834,8 @@ declare namespace CdvPurchase {
|
|
|
2812
2834
|
};
|
|
2813
2835
|
/** Whether this bridge uses StoreKit 2 */
|
|
2814
2836
|
readonly isSK2?: boolean;
|
|
2837
|
+
/** Resolves when pending transactions from the native queue have been processed */
|
|
2838
|
+
pendingTransactionsReady?: Promise<void>;
|
|
2815
2839
|
init(options: Partial<BridgeOptions>, success: () => void, error: (code: ErrorCode, message: string) => void): void;
|
|
2816
2840
|
load(productIds: string[], success: (validProducts: ValidProduct[], invalidProductIds: string[]) => void, error: (code: ErrorCode, message: string) => void): void;
|
|
2817
2841
|
purchase(productId: string, quantity: number, applicationUsername: string | undefined, discount: PaymentDiscount | undefined, success: () => void, error: () => void): void;
|
|
@@ -2846,6 +2870,8 @@ declare namespace CdvPurchase {
|
|
|
2846
2870
|
appStoreReceipt?: AppleAppStore.ApplicationReceipt | null;
|
|
2847
2871
|
private registeredProducts;
|
|
2848
2872
|
private needRestoreNotification;
|
|
2873
|
+
pendingTransactionsReady?: Promise<void>;
|
|
2874
|
+
private _pendingTransactionsResolve?;
|
|
2849
2875
|
private pendingUpdates;
|
|
2850
2876
|
/** True when this bridge is active (SK2 extension installed + iOS 15+) */
|
|
2851
2877
|
readonly isSK2 = true;
|
|
@@ -3038,6 +3064,9 @@ declare namespace CdvPurchase {
|
|
|
3038
3064
|
private registeredProducts;
|
|
3039
3065
|
/** True if "restoreCompleted" or "restoreFailed" should be called when restore is done */
|
|
3040
3066
|
private needRestoreNotification;
|
|
3067
|
+
/** Resolves when pending transactions from the native queue have been processed */
|
|
3068
|
+
pendingTransactionsReady?: Promise<void>;
|
|
3069
|
+
private _pendingTransactionsResolve?;
|
|
3041
3070
|
/** List of transaction updates to process */
|
|
3042
3071
|
private pendingUpdates;
|
|
3043
3072
|
constructor();
|
|
@@ -6168,8 +6197,8 @@ declare namespace CdvPurchase {
|
|
|
6168
6197
|
trialPeriodUnit?: SubscriptionPeriodUnit;
|
|
6169
6198
|
/** Metadata about the user's device */
|
|
6170
6199
|
device?: CdvPurchase.Validator.DeviceInfo;
|
|
6171
|
-
/** List of products available in the store */
|
|
6172
|
-
products
|
|
6200
|
+
/** List of products available in the store. Included at most once per day. */
|
|
6201
|
+
products?: {
|
|
6173
6202
|
/** Type of product (subscription, consumable, etc.) */
|
|
6174
6203
|
type: ProductType;
|
|
6175
6204
|
/** Product identifier on the store (unique per platform) */
|
package/www/store.js
CHANGED
|
@@ -688,9 +688,39 @@ var CdvPurchase;
|
|
|
688
688
|
body.introPriceMicros = intro.priceMicros;
|
|
689
689
|
}
|
|
690
690
|
}
|
|
691
|
+
// Only include the products array once per day to reduce request size.
|
|
692
|
+
// The products array is used by the validator for analytics (price history,
|
|
693
|
+
// transaction amounts) but not for the validation flow itself.
|
|
694
|
+
// The top-level price/currency fields serve as fallback when products is omitted.
|
|
695
|
+
if (!this.shouldSendProducts()) {
|
|
696
|
+
delete body.products;
|
|
697
|
+
}
|
|
691
698
|
return body;
|
|
692
699
|
});
|
|
693
700
|
}
|
|
701
|
+
/** Check if the products array should be included in the validation request.
|
|
702
|
+
* Returns true at most once per day, tracked via localStorage. */
|
|
703
|
+
shouldSendProducts() {
|
|
704
|
+
var _a, _b;
|
|
705
|
+
const STORAGE_KEY = 'cdvpurchase_has_sent_products_in_validation';
|
|
706
|
+
const ONE_DAY_MS = 86400000;
|
|
707
|
+
try {
|
|
708
|
+
const stored = (_a = window.localStorage) === null || _a === void 0 ? void 0 : _a.getItem(STORAGE_KEY);
|
|
709
|
+
if (stored) {
|
|
710
|
+
const lastSent = parseInt(stored, 10);
|
|
711
|
+
if (!isNaN(lastSent) && (Date.now() - lastSent) < ONE_DAY_MS) {
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
// Send products this time and record the date
|
|
716
|
+
(_b = window.localStorage) === null || _b === void 0 ? void 0 : _b.setItem(STORAGE_KEY, String(Date.now()));
|
|
717
|
+
return true;
|
|
718
|
+
}
|
|
719
|
+
catch (_e) {
|
|
720
|
+
// localStorage not available (e.g. private browsing)
|
|
721
|
+
return true;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
694
724
|
removeExpiredCache() {
|
|
695
725
|
const now = +new Date();
|
|
696
726
|
const deleteList = [];
|
|
@@ -1047,6 +1077,8 @@ var CdvPurchase;
|
|
|
1047
1077
|
/** Those platforms have reported that their receipts are ready */
|
|
1048
1078
|
this.platformWithReceiptsReady = [];
|
|
1049
1079
|
this.lastTransactionState = {};
|
|
1080
|
+
/** Remember the first transactionId for each subscription dedup key */
|
|
1081
|
+
this.subscriptionFirstTransactionId = {};
|
|
1050
1082
|
/** Store the listener's latest calling time (in ms) for a given transaction at a given state */
|
|
1051
1083
|
this.lastCallTimeForState = {};
|
|
1052
1084
|
this.updatedReceiptsToProcess = [];
|
|
@@ -1056,6 +1088,26 @@ var CdvPurchase;
|
|
|
1056
1088
|
static makeTransactionToken(transaction) {
|
|
1057
1089
|
return transaction.platform + '|' + transaction.transactionId;
|
|
1058
1090
|
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Create a subscription dedup key from a transaction.
|
|
1093
|
+
*
|
|
1094
|
+
* StoreKit 2 can deliver the same subscription purchase event twice with
|
|
1095
|
+
* different `transactionId` but identical `originalTransactionId` and
|
|
1096
|
+
* `purchaseDate`. This key groups those duplicates together so only one
|
|
1097
|
+
* `approved`/`finished` event is surfaced per billing period.
|
|
1098
|
+
*
|
|
1099
|
+
* Returns `undefined` for non-subscription transactions (no `originalTransactionId`).
|
|
1100
|
+
*/
|
|
1101
|
+
static makeSubscriptionKey(transaction) {
|
|
1102
|
+
if (transaction.platform !== CdvPurchase.Platform.APPLE_APPSTORE)
|
|
1103
|
+
return undefined;
|
|
1104
|
+
const skTransaction = transaction;
|
|
1105
|
+
if (!skTransaction.originalTransactionId)
|
|
1106
|
+
return undefined;
|
|
1107
|
+
if (!transaction.purchaseDate)
|
|
1108
|
+
return undefined;
|
|
1109
|
+
return transaction.platform + '|' + skTransaction.originalTransactionId + '|' + transaction.purchaseDate.getTime();
|
|
1110
|
+
}
|
|
1059
1111
|
/**
|
|
1060
1112
|
* Set the list of supported platforms.
|
|
1061
1113
|
*
|
|
@@ -1135,16 +1187,32 @@ var CdvPurchase;
|
|
|
1135
1187
|
const transactionToken = StoreAdapterListener.makeTransactionToken(transaction);
|
|
1136
1188
|
const tokenWithState = transactionToken + '@' + transaction.state;
|
|
1137
1189
|
const lastState = this.lastTransactionState[transactionToken];
|
|
1190
|
+
const subscriptionKey = StoreAdapterListener.makeSubscriptionKey(transaction);
|
|
1191
|
+
const isSubscriptionDuplicate = !!subscriptionKey
|
|
1192
|
+
&& !!this.subscriptionFirstTransactionId[subscriptionKey]
|
|
1193
|
+
&& this.subscriptionFirstTransactionId[subscriptionKey] !== transaction.transactionId;
|
|
1194
|
+
// Remember the first transactionId for this subscription key
|
|
1195
|
+
if (subscriptionKey && !this.subscriptionFirstTransactionId[subscriptionKey]) {
|
|
1196
|
+
this.subscriptionFirstTransactionId[subscriptionKey] = transaction.transactionId;
|
|
1197
|
+
}
|
|
1138
1198
|
// Retrigger "approved", so validation is rerun on potential update.
|
|
1139
1199
|
if (transaction.state === CdvPurchase.TransactionState.APPROVED) {
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
this.
|
|
1144
|
-
this.delegate.approvedCallbacks.trigger(transaction, 'adapterListener_receiptsUpdated_approved');
|
|
1200
|
+
if (isSubscriptionDuplicate) {
|
|
1201
|
+
// Finish the duplicate at the native level so StoreKit won't re-deliver it
|
|
1202
|
+
this.log.debug(`Auto-finishing subscription duplicate ${transactionToken}, already processed as ${this.subscriptionFirstTransactionId[subscriptionKey]}`);
|
|
1203
|
+
this.delegate.finishDuplicate(transaction);
|
|
1145
1204
|
}
|
|
1146
1205
|
else {
|
|
1147
|
-
|
|
1206
|
+
// Use subscription key for dedup when available, otherwise fall back to transactionToken
|
|
1207
|
+
const dedupKey = subscriptionKey ? subscriptionKey + '@' + transaction.state : tokenWithState;
|
|
1208
|
+
const lastCalled = (_a = this.lastCallTimeForState[dedupKey]) !== null && _a !== void 0 ? _a : 0;
|
|
1209
|
+
if (now - lastCalled > 60000) {
|
|
1210
|
+
this.lastCallTimeForState[dedupKey] = now;
|
|
1211
|
+
this.delegate.approvedCallbacks.trigger(transaction, 'adapterListener_receiptsUpdated_approved');
|
|
1212
|
+
}
|
|
1213
|
+
else {
|
|
1214
|
+
this.log.debug(`Skipping ${tokenWithState}, because it has been last called ${lastCalled > 0 ? Math.round(now - lastCalled) + 'ms ago (' + now + '-' + lastCalled + ')' : 'never'}`);
|
|
1215
|
+
}
|
|
1148
1216
|
}
|
|
1149
1217
|
}
|
|
1150
1218
|
else if (lastState !== transaction.state) {
|
|
@@ -1153,15 +1221,31 @@ var CdvPurchase;
|
|
|
1153
1221
|
this.delegate.initiatedCallbacks.trigger(transaction, 'adapterListener_receiptsUpdated_initiated');
|
|
1154
1222
|
}
|
|
1155
1223
|
else if (transaction.state === CdvPurchase.TransactionState.FINISHED) {
|
|
1156
|
-
|
|
1157
|
-
|
|
1224
|
+
if (isSubscriptionDuplicate) {
|
|
1225
|
+
// Finish the duplicate at the native level so StoreKit won't re-deliver it
|
|
1226
|
+
this.log.debug(`Auto-finishing subscription duplicate ${transactionToken}, already processed as ${this.subscriptionFirstTransactionId[subscriptionKey]}`);
|
|
1227
|
+
this.delegate.finishDuplicate(transaction);
|
|
1228
|
+
}
|
|
1229
|
+
else {
|
|
1230
|
+
this.lastCallTimeForState[tokenWithState] = now;
|
|
1231
|
+
this.delegate.finishedCallbacks.trigger(transaction, 'adapterListener_receiptsUpdated_finished');
|
|
1232
|
+
}
|
|
1158
1233
|
}
|
|
1159
1234
|
else if (transaction.state === CdvPurchase.TransactionState.PENDING) {
|
|
1160
1235
|
this.lastCallTimeForState[tokenWithState] = now;
|
|
1161
1236
|
this.delegate.pendingCallbacks.trigger(transaction, 'adapterListener_receiptsUpdated_pending');
|
|
1162
1237
|
}
|
|
1163
1238
|
}
|
|
1164
|
-
|
|
1239
|
+
// Mark subscription duplicates as FINISHED so the re-processing triggered
|
|
1240
|
+
// by finishDuplicate()'s receiptsUpdated.call() won't enter the FINISHED
|
|
1241
|
+
// handler and call finishDuplicate() a second time on an already-finished
|
|
1242
|
+
// native transaction.
|
|
1243
|
+
if (isSubscriptionDuplicate) {
|
|
1244
|
+
this.lastTransactionState[transactionToken] = CdvPurchase.TransactionState.FINISHED;
|
|
1245
|
+
}
|
|
1246
|
+
else {
|
|
1247
|
+
this.lastTransactionState[transactionToken] = transaction.state;
|
|
1248
|
+
}
|
|
1165
1249
|
});
|
|
1166
1250
|
});
|
|
1167
1251
|
}
|
|
@@ -1578,7 +1662,7 @@ var CdvPurchase;
|
|
|
1578
1662
|
/**
|
|
1579
1663
|
* Current release number of the plugin.
|
|
1580
1664
|
*/
|
|
1581
|
-
CdvPurchase.PLUGIN_VERSION = '13.15.
|
|
1665
|
+
CdvPurchase.PLUGIN_VERSION = '13.15.3';
|
|
1582
1666
|
/**
|
|
1583
1667
|
* Entry class of the plugin.
|
|
1584
1668
|
*/
|
|
@@ -1658,6 +1742,12 @@ var CdvPurchase;
|
|
|
1658
1742
|
finishedCallbacks: this.finishedCallbacks,
|
|
1659
1743
|
pendingCallbacks: this.pendingCallbacks,
|
|
1660
1744
|
receiptsReadyCallbacks: this.receiptsReadyCallbacks,
|
|
1745
|
+
finishDuplicate: (transaction) => {
|
|
1746
|
+
const adapter = this.adapters.findReady(transaction.platform);
|
|
1747
|
+
if (adapter) {
|
|
1748
|
+
adapter.finish(transaction);
|
|
1749
|
+
}
|
|
1750
|
+
},
|
|
1661
1751
|
}, this.log);
|
|
1662
1752
|
this.transactionStateMonitors = new CdvPurchase.Internal.TransactionStateMonitors(this.when());
|
|
1663
1753
|
this._validator = new CdvPurchase.Internal.Validator({
|
|
@@ -3104,9 +3194,14 @@ var CdvPurchase;
|
|
|
3104
3194
|
this.initializeAppReceipt(() => {
|
|
3105
3195
|
var _a;
|
|
3106
3196
|
if (!this._receipt) {
|
|
3107
|
-
//
|
|
3108
|
-
|
|
3109
|
-
|
|
3197
|
+
// Receipt failed to load — create a minimal receipt so the
|
|
3198
|
+
// transaction can still be tracked and finished. Without this
|
|
3199
|
+
// fallback the Promise would never resolve, leaving the native
|
|
3200
|
+
// transaction unfinished (causing the purchase dialog to loop on
|
|
3201
|
+
// iOS — see #1568).
|
|
3202
|
+
this.log.warn('Application receipt unavailable, creating a fallback receipt to avoid blocking transactions');
|
|
3203
|
+
this._receipt = new AppleAppStore.SKApplicationReceipt({ appStoreReceipt: '', bundleIdentifier: '',
|
|
3204
|
+
bundleShortVersion: '', bundleNumericVersion: 0, bundleSignature: '' }, this.needAppReceipt, this.context.apiDecorators);
|
|
3110
3205
|
}
|
|
3111
3206
|
const existing = (_a = this._receipt) === null || _a === void 0 ? void 0 : _a.transactions.find(t => t.transactionId === transactionId);
|
|
3112
3207
|
if (existing) {
|
|
@@ -3268,8 +3363,22 @@ var CdvPurchase;
|
|
|
3268
3363
|
});
|
|
3269
3364
|
}
|
|
3270
3365
|
loadReceipts() {
|
|
3271
|
-
return
|
|
3272
|
-
|
|
3366
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3367
|
+
// Wait for native pending transactions to be processed before
|
|
3368
|
+
// initializing the receipt. Previously used a fixed 300ms delay
|
|
3369
|
+
// which was unreliable and could miss transactions that arrived
|
|
3370
|
+
// from the native queue (see #1529).
|
|
3371
|
+
//
|
|
3372
|
+
// The pendingTransactionsReady Promise resolves when the native
|
|
3373
|
+
// `processPendingTransactions` call returns, but the per-transaction
|
|
3374
|
+
// callbacks it triggers via evalJs land asynchronously. Yield one
|
|
3375
|
+
// task so those purchased/restored callbacks (and their async
|
|
3376
|
+
// upsertTransaction chain) settle before we snapshot _receipt.
|
|
3377
|
+
if (this.bridge.pendingTransactionsReady) {
|
|
3378
|
+
yield this.bridge.pendingTransactionsReady;
|
|
3379
|
+
yield new Promise(r => setTimeout(r, 0));
|
|
3380
|
+
}
|
|
3381
|
+
return new Promise((resolve) => {
|
|
3273
3382
|
this.initializeAppReceipt(() => {
|
|
3274
3383
|
this.receiptsUpdated.call();
|
|
3275
3384
|
if (this._receipt) {
|
|
@@ -3279,7 +3388,7 @@ var CdvPurchase;
|
|
|
3279
3388
|
resolve([this.pseudoReceipt]);
|
|
3280
3389
|
}
|
|
3281
3390
|
});
|
|
3282
|
-
}
|
|
3391
|
+
});
|
|
3283
3392
|
});
|
|
3284
3393
|
}
|
|
3285
3394
|
canMakePayments() {
|
|
@@ -3335,6 +3444,7 @@ var CdvPurchase;
|
|
|
3335
3444
|
return;
|
|
3336
3445
|
}
|
|
3337
3446
|
this._receipt = new AppleAppStore.SKApplicationReceipt(nativeData, this.needAppReceipt, this.context.apiDecorators);
|
|
3447
|
+
this._appStoreReceiptLoading = false;
|
|
3338
3448
|
callCallbacks(undefined);
|
|
3339
3449
|
});
|
|
3340
3450
|
}
|
|
@@ -3548,16 +3658,20 @@ var CdvPurchase;
|
|
|
3548
3658
|
this.receiptsUpdated.call();
|
|
3549
3659
|
return resolve(undefined);
|
|
3550
3660
|
}
|
|
3661
|
+
const wasAlreadyFinished = transaction.state === CdvPurchase.TransactionState.FINISHED;
|
|
3551
3662
|
const success = () => {
|
|
3552
3663
|
transaction.state = CdvPurchase.TransactionState.FINISHED;
|
|
3553
|
-
|
|
3664
|
+
if (!wasAlreadyFinished) {
|
|
3665
|
+
this.receiptsUpdated.call();
|
|
3666
|
+
}
|
|
3554
3667
|
resolve(undefined);
|
|
3555
3668
|
};
|
|
3556
3669
|
const error = (msg) => {
|
|
3557
3670
|
var _a, _b;
|
|
3558
3671
|
if (msg === null || msg === void 0 ? void 0 : msg.includes('[#CdvPurchase:100]')) {
|
|
3559
|
-
// already finished
|
|
3560
|
-
|
|
3672
|
+
// already finished at the native level
|
|
3673
|
+
transaction.state = CdvPurchase.TransactionState.FINISHED;
|
|
3674
|
+
resolve(undefined);
|
|
3561
3675
|
}
|
|
3562
3676
|
else {
|
|
3563
3677
|
resolve(appStoreError(CdvPurchase.ErrorCode.FINISH, 'Failed to finish transaction', (_b = (_a = transaction.products[0]) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null));
|
|
@@ -4123,6 +4237,9 @@ var CdvPurchase;
|
|
|
4123
4237
|
protectCall(this.options.ready, 'options.ready');
|
|
4124
4238
|
protectCall(success, 'init.success');
|
|
4125
4239
|
this.initialized = true;
|
|
4240
|
+
this.pendingTransactionsReady = new Promise(resolve => {
|
|
4241
|
+
this._pendingTransactionsResolve = resolve;
|
|
4242
|
+
});
|
|
4126
4243
|
setTimeout(() => this.processPendingTransactions(), 50);
|
|
4127
4244
|
};
|
|
4128
4245
|
const setupFailed = (err) => {
|
|
@@ -4135,6 +4252,10 @@ var CdvPurchase;
|
|
|
4135
4252
|
log('processing pending transactions');
|
|
4136
4253
|
exec('processPendingTransactions', [], () => {
|
|
4137
4254
|
this.finalizeTransactionUpdates();
|
|
4255
|
+
if (this._pendingTransactionsResolve) {
|
|
4256
|
+
this._pendingTransactionsResolve();
|
|
4257
|
+
this._pendingTransactionsResolve = undefined;
|
|
4258
|
+
}
|
|
4138
4259
|
}, undefined);
|
|
4139
4260
|
}
|
|
4140
4261
|
purchase(productId, quantity, applicationUsername, discount, success, error) {
|
|
@@ -4446,6 +4567,9 @@ var CdvPurchase;
|
|
|
4446
4567
|
protectCall(this.options.ready, 'options.ready');
|
|
4447
4568
|
protectCall(success, 'init.success');
|
|
4448
4569
|
this.initialized = true;
|
|
4570
|
+
this.pendingTransactionsReady = new Promise(resolve => {
|
|
4571
|
+
this._pendingTransactionsResolve = resolve;
|
|
4572
|
+
});
|
|
4449
4573
|
setTimeout(() => this.processPendingTransactions(), 50);
|
|
4450
4574
|
};
|
|
4451
4575
|
const setupFailed = (err) => {
|
|
@@ -4459,6 +4583,10 @@ var CdvPurchase;
|
|
|
4459
4583
|
log('processing pending transactions');
|
|
4460
4584
|
exec('processPendingTransactions', [], () => {
|
|
4461
4585
|
this.finalizeTransactionUpdates();
|
|
4586
|
+
if (this._pendingTransactionsResolve) {
|
|
4587
|
+
this._pendingTransactionsResolve();
|
|
4588
|
+
this._pendingTransactionsResolve = undefined;
|
|
4589
|
+
}
|
|
4462
4590
|
}, undefined);
|
|
4463
4591
|
}
|
|
4464
4592
|
/**
|
|
@@ -4848,6 +4976,13 @@ var CdvPurchase;
|
|
|
4848
4976
|
offerType: 'Default',
|
|
4849
4977
|
}, decorator));
|
|
4850
4978
|
}
|
|
4979
|
+
// Ensure the default offer is always first so that getOffer()
|
|
4980
|
+
// without arguments returns it instead of a discount offer.
|
|
4981
|
+
const defaultIndex = this.offers.findIndex(o => o.id === AppleAppStore.DEFAULT_OFFER_ID);
|
|
4982
|
+
if (defaultIndex > 0) {
|
|
4983
|
+
const [defaultOffer] = this.offers.splice(defaultIndex, 1);
|
|
4984
|
+
this.offers.unshift(defaultOffer);
|
|
4985
|
+
}
|
|
4851
4986
|
function hasIntroductoryOffer(product) {
|
|
4852
4987
|
return product.offers.filter(offer => {
|
|
4853
4988
|
const skOffer = offer;
|