rampkit-expo-dev 0.0.67 → 0.0.68
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/RampKitModule.swift +104 -109
- package/package.json +1 -1
package/ios/RampKitModule.swift
CHANGED
|
@@ -9,22 +9,26 @@ public class RampKitModule: Module {
|
|
|
9
9
|
private let installDateKey = "rk_install_date"
|
|
10
10
|
private let launchCountKey = "rk_launch_count"
|
|
11
11
|
private let lastLaunchKey = "rk_last_launch"
|
|
12
|
+
private let trackedTransactionsKey = "rk_tracked_transactions"
|
|
12
13
|
|
|
13
14
|
// Transaction observer task
|
|
14
15
|
private var transactionObserverTask: Task<Void, Never>?
|
|
15
16
|
private var appId: String?
|
|
16
17
|
private var userId: String?
|
|
17
|
-
|
|
18
|
-
// Queue for transactions received before SDK is configured
|
|
19
|
-
private var pendingTransactions: [Any] = []
|
|
20
18
|
private var isConfigured = false
|
|
21
19
|
|
|
20
|
+
// Set of already-tracked originalTransactionIds to prevent duplicates
|
|
21
|
+
private var trackedTransactionIds: Set<String> = []
|
|
22
|
+
|
|
22
23
|
public func definition() -> ModuleDefinition {
|
|
23
24
|
Name("RampKit")
|
|
24
25
|
|
|
25
|
-
//
|
|
26
|
+
// OnCreate runs when JS first requires the module - may be too late for some transactions
|
|
26
27
|
OnCreate {
|
|
27
|
-
|
|
28
|
+
print("[RampKit] ⚡ OnCreate called - module being initialized")
|
|
29
|
+
self.loadTrackedTransactions()
|
|
30
|
+
self.userId = self.getOrCreateUserId()
|
|
31
|
+
print("[RampKit] ⚡ OnCreate complete, userId: \(self.userId ?? "nil")")
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
// ============================================================================
|
|
@@ -249,8 +253,23 @@ public class RampKitModule: Module {
|
|
|
249
253
|
SecItemAdd(addQuery as CFDictionary, nil)
|
|
250
254
|
}
|
|
251
255
|
|
|
256
|
+
// MARK: - Tracked Transactions (Deduplication)
|
|
257
|
+
|
|
258
|
+
private func loadTrackedTransactions() {
|
|
259
|
+
if let stored = UserDefaults.standard.array(forKey: trackedTransactionsKey) as? [String] {
|
|
260
|
+
trackedTransactionIds = Set(stored)
|
|
261
|
+
print("[RampKit] 📚 Loaded \(trackedTransactionIds.count) tracked transaction IDs")
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private func saveTrackedTransactions() {
|
|
266
|
+
let array = Array(trackedTransactionIds)
|
|
267
|
+
UserDefaults.standard.set(array, forKey: trackedTransactionsKey)
|
|
268
|
+
print("[RampKit] 💾 Saved \(trackedTransactionIds.count) tracked transaction IDs")
|
|
269
|
+
}
|
|
270
|
+
|
|
252
271
|
// MARK: - Launch Tracking
|
|
253
|
-
|
|
272
|
+
|
|
254
273
|
private func getLaunchTrackingData() -> [String: Any?] {
|
|
255
274
|
let defaults = UserDefaults.standard
|
|
256
275
|
let now = ISO8601DateFormatter().string(from: Date())
|
|
@@ -412,149 +431,125 @@ public class RampKitModule: Module {
|
|
|
412
431
|
|
|
413
432
|
// MARK: - StoreKit 2 Transaction Observer
|
|
414
433
|
|
|
415
|
-
/// Called
|
|
416
|
-
private func startTransactionObserverEarly() {
|
|
417
|
-
// Get userId early so we have it ready
|
|
418
|
-
self.userId = getOrCreateUserId()
|
|
419
|
-
|
|
420
|
-
guard #available(iOS 15.0, *) else {
|
|
421
|
-
print("[RampKit] ⚠️ Transaction observer requires iOS 15+")
|
|
422
|
-
return
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// Don't restart if already running
|
|
426
|
-
guard transactionObserverTask == nil else {
|
|
427
|
-
print("[RampKit] Transaction observer already running")
|
|
428
|
-
return
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
print("[RampKit] 🚀 Starting transaction observer early (module init)...")
|
|
432
|
-
|
|
433
|
-
transactionObserverTask = Task {
|
|
434
|
-
print("[RampKit] 👂 Transaction.updates loop starting...")
|
|
435
|
-
for await result in Transaction.updates {
|
|
436
|
-
print("[RampKit] 🎉 RECEIVED TRANSACTION UPDATE!")
|
|
437
|
-
await self.handleTransactionUpdate(result)
|
|
438
|
-
}
|
|
439
|
-
print("[RampKit] ⚠️ Transaction.updates loop exited (unexpected)")
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// Also check unfinished transactions
|
|
443
|
-
Task {
|
|
444
|
-
await self.handleUnfinishedTransactions()
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
/// Called from JavaScript - sets appId and processes any pending transactions
|
|
434
|
+
/// Called from JavaScript - sets appId and IMMEDIATELY checks for purchases we may have missed
|
|
449
435
|
private func startTransactionObserver(appId: String) {
|
|
450
436
|
self.appId = appId
|
|
451
437
|
self.userId = getOrCreateUserId()
|
|
452
438
|
self.isConfigured = true
|
|
453
439
|
|
|
454
440
|
print("[RampKit] ✅ Transaction observer configured with appId: \(appId)")
|
|
441
|
+
print("[RampKit] 📊 Already tracked \(trackedTransactionIds.count) transactions")
|
|
455
442
|
|
|
456
|
-
// Process any transactions that arrived before we were configured
|
|
457
443
|
if #available(iOS 15.0, *) {
|
|
444
|
+
// CRITICAL: Check current entitlements for any purchases we missed
|
|
445
|
+
// This is the KEY mechanism for catching Superwall/RevenueCat purchases
|
|
458
446
|
Task {
|
|
459
|
-
await self.
|
|
447
|
+
await self.checkAndTrackCurrentEntitlements()
|
|
460
448
|
}
|
|
461
449
|
|
|
462
|
-
//
|
|
450
|
+
// Also start listening for future transactions
|
|
463
451
|
Task {
|
|
464
|
-
await self.
|
|
452
|
+
await self.startTransactionUpdatesListener()
|
|
465
453
|
}
|
|
466
454
|
}
|
|
467
|
-
|
|
468
|
-
// Ensure observer is running (in case OnCreate didn't fire)
|
|
469
|
-
if transactionObserverTask == nil {
|
|
470
|
-
startTransactionObserverEarly()
|
|
471
|
-
}
|
|
472
455
|
}
|
|
473
456
|
|
|
457
|
+
/// Check all current entitlements and track any we haven't seen before
|
|
458
|
+
/// This catches purchases made by Superwall/RevenueCat before our observer started
|
|
474
459
|
@available(iOS 15.0, *)
|
|
475
|
-
private func
|
|
476
|
-
|
|
477
|
-
let transaction = try self.checkVerified(result)
|
|
478
|
-
print("[RampKit] ✅ Transaction verified: \(transaction.productID)")
|
|
460
|
+
private func checkAndTrackCurrentEntitlements() async {
|
|
461
|
+
print("[RampKit] 🔍 Checking current entitlements for missed purchases...")
|
|
479
462
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
463
|
+
var foundCount = 0
|
|
464
|
+
var trackedCount = 0
|
|
465
|
+
var newCount = 0
|
|
466
|
+
|
|
467
|
+
for await result in Transaction.currentEntitlements {
|
|
468
|
+
foundCount += 1
|
|
469
|
+
|
|
470
|
+
guard case .verified(let transaction) = result else {
|
|
471
|
+
print("[RampKit] ⚠️ Unverified entitlement skipped")
|
|
472
|
+
continue
|
|
487
473
|
}
|
|
488
474
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
475
|
+
let originalId = String(transaction.originalID)
|
|
476
|
+
print("[RampKit] 📦 Found entitlement: \(transaction.productID), originalID: \(originalId)")
|
|
477
|
+
|
|
478
|
+
// Check if we've already tracked this transaction
|
|
479
|
+
if trackedTransactionIds.contains(originalId) {
|
|
480
|
+
trackedCount += 1
|
|
481
|
+
print("[RampKit] ✓ Already tracked: \(transaction.productID)")
|
|
482
|
+
continue
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// NEW transaction we haven't seen!
|
|
486
|
+
newCount += 1
|
|
487
|
+
print("[RampKit] 🆕 NEW purchase detected: \(transaction.productID)")
|
|
488
|
+
|
|
489
|
+
// Track it
|
|
490
|
+
await self.handleTransaction(transaction)
|
|
491
|
+
|
|
492
|
+
// Mark as tracked
|
|
493
|
+
trackedTransactionIds.insert(originalId)
|
|
494
|
+
saveTrackedTransactions()
|
|
494
495
|
}
|
|
496
|
+
|
|
497
|
+
print("[RampKit] 🔍 Entitlement check complete:")
|
|
498
|
+
print("[RampKit] - Total found: \(foundCount)")
|
|
499
|
+
print("[RampKit] - Already tracked: \(trackedCount)")
|
|
500
|
+
print("[RampKit] - NEW (sent events): \(newCount)")
|
|
495
501
|
}
|
|
496
502
|
|
|
503
|
+
/// Start listening for Transaction.updates (for future purchases)
|
|
497
504
|
@available(iOS 15.0, *)
|
|
498
|
-
private func
|
|
499
|
-
guard
|
|
505
|
+
private func startTransactionUpdatesListener() async {
|
|
506
|
+
guard transactionObserverTask == nil else {
|
|
507
|
+
print("[RampKit] Transaction updates listener already running")
|
|
508
|
+
return
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
print("[RampKit] 👂 Starting Transaction.updates listener...")
|
|
512
|
+
|
|
513
|
+
transactionObserverTask = Task {
|
|
514
|
+
for await result in Transaction.updates {
|
|
515
|
+
print("[RampKit] 🎉 Transaction.updates received!")
|
|
516
|
+
|
|
517
|
+
guard case .verified(let transaction) = result else {
|
|
518
|
+
print("[RampKit] ⚠️ Unverified transaction update")
|
|
519
|
+
continue
|
|
520
|
+
}
|
|
500
521
|
|
|
501
|
-
|
|
522
|
+
let originalId = String(transaction.originalID)
|
|
502
523
|
|
|
503
|
-
|
|
504
|
-
|
|
524
|
+
// Skip if already tracked
|
|
525
|
+
if self.trackedTransactionIds.contains(originalId) {
|
|
526
|
+
print("[RampKit] ✓ Transaction.updates: Already tracked \(transaction.productID)")
|
|
527
|
+
await transaction.finish()
|
|
528
|
+
continue
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
print("[RampKit] 🆕 Transaction.updates: NEW purchase \(transaction.productID)")
|
|
532
|
+
|
|
533
|
+
// Track it
|
|
505
534
|
await self.handleTransaction(transaction)
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
535
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
536
|
+
// Mark as tracked
|
|
537
|
+
self.trackedTransactionIds.insert(originalId)
|
|
538
|
+
self.saveTrackedTransactions()
|
|
512
539
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
print("[RampKit] 🔍 Checking current entitlements...")
|
|
516
|
-
var count = 0
|
|
517
|
-
for await result in Transaction.currentEntitlements {
|
|
518
|
-
if case .verified(let transaction) = result {
|
|
519
|
-
count += 1
|
|
520
|
-
print("[RampKit] 📦 Current entitlement: \(transaction.productID), originalID: \(transaction.originalID), id: \(transaction.id)")
|
|
540
|
+
// Finish the transaction
|
|
541
|
+
await transaction.finish()
|
|
521
542
|
}
|
|
522
543
|
}
|
|
523
|
-
print("[RampKit] 🔍 Found \(count) current entitlements")
|
|
524
544
|
}
|
|
525
545
|
|
|
526
546
|
private func stopTransactionObserver() {
|
|
527
547
|
transactionObserverTask?.cancel()
|
|
528
548
|
transactionObserverTask = nil
|
|
529
549
|
isConfigured = false
|
|
530
|
-
pendingTransactions.removeAll()
|
|
531
550
|
print("[RampKit] Transaction observer stopped")
|
|
532
551
|
}
|
|
533
552
|
|
|
534
|
-
@available(iOS 15.0, *)
|
|
535
|
-
private func handleUnfinishedTransactions() async {
|
|
536
|
-
print("[RampKit] 🔍 Checking for unfinished transactions...")
|
|
537
|
-
var count = 0
|
|
538
|
-
for await result in Transaction.unfinished {
|
|
539
|
-
count += 1
|
|
540
|
-
print("[RampKit] 📦 Found unfinished transaction #\(count)")
|
|
541
|
-
await self.handleTransactionUpdate(result)
|
|
542
|
-
}
|
|
543
|
-
print("[RampKit] 🔍 Finished checking unfinished transactions. Found: \(count)")
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
@available(iOS 15.0, *)
|
|
547
|
-
private func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
|
|
548
|
-
switch result {
|
|
549
|
-
case .unverified(_, let error):
|
|
550
|
-
throw error
|
|
551
|
-
case .verified(let safe):
|
|
552
|
-
return safe
|
|
553
|
-
@unknown default:
|
|
554
|
-
fatalError("Unknown VerificationResult case")
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
|
|
558
553
|
@available(iOS 15.0, *)
|
|
559
554
|
private func handleTransaction(_ transaction: Transaction) async {
|
|
560
555
|
print("[RampKit] 🔄 handleTransaction called for: \(transaction.productID)")
|
package/package.json
CHANGED