rampkit-expo-dev 0.0.71 → 0.0.73

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/build/RampKit.js CHANGED
@@ -297,6 +297,8 @@ class RampKitCore {
297
297
  var _a;
298
298
  // Track onboarding completed - trigger: finished
299
299
  EventManager_1.eventManager.trackOnboardingCompleted("finished", screens.length, screens.length, onboardingId);
300
+ // Auto-recheck transactions after onboarding finishes (catches purchases made during onboarding)
301
+ RampKitNative_1.TransactionObserver.recheck().catch(() => { });
300
302
  try {
301
303
  (_a = this.onOnboardingFinished) === null || _a === void 0 ? void 0 : _a.call(this, payload);
302
304
  }
@@ -306,6 +308,8 @@ class RampKitCore {
306
308
  // Track onboarding completed - trigger: paywall_shown
307
309
  EventManager_1.eventManager.trackOnboardingCompleted("paywall_shown", screens.length, // We don't know exact step, use total
308
310
  screens.length, onboardingId);
311
+ // Auto-recheck transactions after paywall shown (catches purchases made during onboarding)
312
+ RampKitNative_1.TransactionObserver.recheck().catch(() => { });
309
313
  // Call the original callback
310
314
  const paywallCallback = (opts === null || opts === void 0 ? void 0 : opts.onShowPaywall) || (opts === null || opts === void 0 ? void 0 : opts.showPaywall) || this.onShowPaywall;
311
315
  try {
@@ -323,6 +327,8 @@ class RampKitCore {
323
327
  onCloseAction: (screenIndex, _screenId) => {
324
328
  // Track onboarding completed - trigger: closed
325
329
  EventManager_1.eventManager.trackOnboardingCompleted("closed", screenIndex + 1, screens.length, onboardingId);
330
+ // Auto-recheck transactions after onboarding closes (catches purchases made before closing)
331
+ RampKitNative_1.TransactionObserver.recheck().catch(() => { });
326
332
  },
327
333
  });
328
334
  }
@@ -486,10 +486,25 @@ public class RampKitModule: Module {
486
486
  /// Check all current entitlements and track any we haven't seen before
487
487
  /// This catches purchases made by Superwall/RevenueCat before our observer started
488
488
  /// Returns a dictionary with the results for JavaScript logging
489
+ ///
490
+ /// IMPORTANT: "Already sent" means we previously sent this transaction to the backend
491
+ /// and received a successful HTTP 2xx response. The originalTransactionId is stored
492
+ /// in UserDefaults ONLY after a successful send.
489
493
  @available(iOS 15.0, *)
490
494
  private func checkAndTrackCurrentEntitlements() async -> [String: Any] {
491
- print("[RampKit] 🔍 Checking current entitlements for missed purchases...")
492
- print("[RampKit] 📚 Currently have \(trackedTransactionIds.count) tracked transaction IDs in storage")
495
+ print("[RampKit] ")
496
+ print("[RampKit] ════════════════════════════════════════════════════════════")
497
+ print("[RampKit] 🔍 CHECKING ENTITLEMENTS FOR UNSENT PURCHASES")
498
+ print("[RampKit] ════════════════════════════════════════════════════════════")
499
+ print("[RampKit] ")
500
+ print("[RampKit] 📚 Tracked transaction IDs in storage: \(trackedTransactionIds.count)")
501
+ if !trackedTransactionIds.isEmpty {
502
+ print("[RampKit] These IDs were SUCCESSFULLY sent to backend (HTTP 2xx):")
503
+ for id in trackedTransactionIds {
504
+ print("[RampKit] - \(id)")
505
+ }
506
+ }
507
+ print("[RampKit] ")
493
508
 
494
509
  let formatter = ISO8601DateFormatter()
495
510
  formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
@@ -537,25 +552,35 @@ public class RampKitModule: Module {
537
552
  print("[RampKit] - purchaseDate: \(formatter.string(from: transaction.purchaseDate))")
538
553
 
539
554
  // Check if we've already tracked this transaction
540
- if trackedTransactionIds.contains(originalId) {
555
+ // "Tracked" means we successfully sent a purchase_completed event to the backend
556
+ // and received an HTTP 2xx response. We store the transactionId after success.
557
+ // NOTE: We track by transactionId (not originalTransactionId) so renewals are also sent.
558
+ if trackedTransactionIds.contains(transactionId) {
541
559
  trackedCount += 1
542
560
  txDetails["status"] = "already_sent"
543
561
  alreadyTrackedDetails.append(txDetails)
544
- print("[RampKit] ✅ STATUS: Already sent to backend (originalTransactionId in tracked set)")
562
+ print("[RampKit] ✅ STATUS: ALREADY SENT TO BACKEND")
563
+ print("[RampKit] (transactionId \(transactionId) is in our sent-transactions storage)")
564
+ print("[RampKit] This means we previously sent a purchase_completed event")
565
+ print("[RampKit] and received a successful HTTP 2xx response.")
566
+ print("[RampKit] ")
545
567
  continue
546
568
  }
547
569
 
548
- // Skip renewals and revocations
549
- guard transaction.originalID == transaction.id,
550
- transaction.revocationDate == nil else {
551
- let reason = transaction.revocationDate != nil ? "revoked" : "renewal"
570
+ // Skip revocations only (NOT renewals - we want to track renewals too!)
571
+ if transaction.revocationDate != nil {
552
572
  txDetails["status"] = "skipped"
553
- txDetails["reason"] = reason
573
+ txDetails["reason"] = "revoked"
554
574
  skippedReasons.append(txDetails)
555
- print("[RampKit] â­ī¸ STATUS: Skipped (\(reason))")
575
+ print("[RampKit] â­ī¸ STATUS: Skipped (revoked)")
556
576
  continue
557
577
  }
558
578
 
579
+ // Log if this is a renewal
580
+ if transaction.originalID != transaction.id {
581
+ print("[RampKit] â„šī¸ This is a RENEWAL (originalID != transactionID)")
582
+ }
583
+
559
584
  // NEW transaction we haven't seen!
560
585
  newCount += 1
561
586
  newProductIds.append(transaction.productID)
@@ -567,10 +592,10 @@ public class RampKitModule: Module {
567
592
 
568
593
  // Only mark as tracked if send succeeded
569
594
  if let status = sendResult["status"] as? String, status == "sent" {
570
- trackedTransactionIds.insert(originalId)
595
+ trackedTransactionIds.insert(transactionId)
571
596
  saveTrackedTransactions()
572
597
  print("[RampKit] ✅ Event sent successfully! HTTP status: \(sendResult["httpStatus"] ?? "unknown")")
573
- print("[RampKit] ✅ Marked originalTransactionId \(originalId) as tracked")
598
+ print("[RampKit] ✅ Marked transactionId \(transactionId) as tracked")
574
599
  } else {
575
600
  print("[RampKit] ❌ Send failed: \(sendResult["error"] ?? "unknown error")")
576
601
  print("[RampKit] âš ī¸ Will retry on next app launch")
@@ -578,15 +603,19 @@ public class RampKitModule: Module {
578
603
  }
579
604
 
580
605
  print("[RampKit] ")
581
- print("[RampKit] 🔍 ═══════════════════════════════════════════")
582
- print("[RampKit] 🔍 ENTITLEMENT CHECK SUMMARY:")
583
- print("[RampKit] 🔍 ═══════════════════════════════════════════")
584
- print("[RampKit] Total entitlements found: \(foundCount)")
585
- print("[RampKit] Already sent to backend: \(trackedCount)")
586
- print("[RampKit] Skipped (renewal/revoked): \(skippedReasons.count)")
587
- print("[RampKit] NEW events sent: \(newCount)")
588
- print("[RampKit] Tracked IDs in storage: \(trackedTransactionIds.count)")
589
- print("[RampKit] 🔍 ═══════════════════════════════════════════")
606
+ print("[RampKit] ════════════════════════════════════════════════════════════")
607
+ print("[RampKit] 📊 ENTITLEMENT CHECK SUMMARY")
608
+ print("[RampKit] ════════════════════════════════════════════════════════════")
609
+ print("[RampKit] Total entitlements found: \(foundCount)")
610
+ print("[RampKit] Already sent (HTTP 2xx): \(trackedCount) (no action needed)")
611
+ print("[RampKit] Skipped (renewal/revoked): \(skippedReasons.count) (backend gets via S2S)")
612
+ print("[RampKit] NEW events sent this session: \(newCount)")
613
+ print("[RampKit] Total tracked IDs in storage: \(trackedTransactionIds.count)")
614
+ if newCount == 0 && foundCount > 0 {
615
+ print("[RampKit] ")
616
+ print("[RampKit] â„šī¸ All purchases already sent or skipped - nothing new to send")
617
+ }
618
+ print("[RampKit] ════════════════════════════════════════════════════════════")
590
619
 
591
620
  return [
592
621
  "totalFound": foundCount,
@@ -620,39 +649,38 @@ public class RampKitModule: Module {
620
649
  continue
621
650
  }
622
651
 
652
+ let transactionId = String(transaction.id)
623
653
  let originalId = String(transaction.originalID)
624
654
 
625
- // Skip if already tracked
626
- if self.trackedTransactionIds.contains(originalId) {
627
- print("[RampKit] ✓ Transaction.updates: Already tracked \(transaction.productID)")
655
+ // Skip if already tracked (by transactionId, not originalId - so renewals are tracked)
656
+ if self.trackedTransactionIds.contains(transactionId) {
657
+ print("[RampKit] ✓ Transaction.updates: Already tracked \(transaction.productID) (txId: \(transactionId))")
628
658
  await transaction.finish()
629
659
  continue
630
660
  }
631
661
 
632
- // Skip renewals - backend gets these from App Store S2S notifications
633
- if transaction.originalID != transaction.id {
634
- print("[RampKit] â­ī¸ Transaction.updates: skipped (renewal) \(transaction.productID)")
635
- await transaction.finish()
636
- continue
637
- }
638
-
639
- // Skip revocations - backend gets these from S2S notifications
662
+ // Skip revocations only (NOT renewals - we want to track renewals!)
640
663
  if transaction.revocationDate != nil {
641
664
  print("[RampKit] â­ī¸ Transaction.updates: skipped (revoked) \(transaction.productID)")
642
665
  await transaction.finish()
643
666
  continue
644
667
  }
645
668
 
646
- print("[RampKit] 🆕 Transaction.updates: NEW purchase \(transaction.productID)")
669
+ // Log if this is a renewal
670
+ if transaction.originalID != transaction.id {
671
+ print("[RampKit] 🔄 Transaction.updates: RENEWAL detected for \(transaction.productID)")
672
+ }
673
+
674
+ print("[RampKit] 🆕 Transaction.updates: NEW transaction \(transaction.productID) (txId: \(transactionId))")
647
675
 
648
676
  // Track it and check result
649
677
  let sendResult = await self.handleTransactionWithResult(transaction)
650
678
 
651
- // Only mark as tracked if send succeeded
679
+ // Only mark as tracked if send succeeded (use transactionId for renewals support)
652
680
  if let status = sendResult["status"] as? String, status == "sent" {
653
- self.trackedTransactionIds.insert(originalId)
681
+ self.trackedTransactionIds.insert(transactionId)
654
682
  self.saveTrackedTransactions()
655
- print("[RampKit] ✅ Transaction.updates: Sent and tracked \(transaction.productID)")
683
+ print("[RampKit] ✅ Transaction.updates: Sent and tracked \(transaction.productID) (txId: \(transactionId))")
656
684
  } else {
657
685
  print("[RampKit] âš ī¸ Transaction.updates: Send failed, will retry \(transaction.productID)")
658
686
  }
@@ -914,8 +942,8 @@ public class RampKitModule: Module {
914
942
  }
915
943
  }
916
944
 
917
- private func sendPurchaseEvent(appId: String, userId: String, eventName: String, properties: [String: Any]) async {
918
- let _ = await sendPurchaseEventWithResult(appId: appId, userId: userId, eventName: eventName, properties: properties)
945
+ private func sendPurchaseEvent(appId: String, userId: String, eventName: String, properties: [String: Any], paywallId: String? = nil) async {
946
+ let _ = await sendPurchaseEventWithResult(appId: appId, userId: userId, eventName: eventName, properties: properties, paywallId: paywallId)
919
947
  }
920
948
 
921
949
  private struct SendEventResult {
@@ -924,7 +952,16 @@ public class RampKitModule: Module {
924
952
  let error: String?
925
953
  }
926
954
 
927
- private func sendPurchaseEventWithResult(appId: String, userId: String, eventName: String, properties: [String: Any]) async -> SendEventResult {
955
+ private func sendPurchaseEventWithResult(appId: String, userId: String, eventName: String, properties: [String: Any], paywallId: String? = nil) async -> SendEventResult {
956
+ // Build context with optional paywallId for attribution
957
+ var context: [String: Any] = [
958
+ "locale": Locale.current.identifier,
959
+ "regionCode": Locale.current.regionCode as Any
960
+ ]
961
+ if let paywallId = paywallId {
962
+ context["paywallId"] = paywallId
963
+ }
964
+
928
965
  let event: [String: Any] = [
929
966
  "appId": appId,
930
967
  "appUserId": userId,
@@ -937,17 +974,53 @@ public class RampKitModule: Module {
937
974
  "platformVersion": UIDevice.current.systemVersion,
938
975
  "deviceModel": getDeviceModelIdentifier(),
939
976
  "sdkVersion": "1.0.0",
940
- "appVersion": Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String,
941
- "buildNumber": Bundle.main.infoDictionary?["CFBundleVersion"] as? String
942
- ],
943
- "context": [
944
- "locale": Locale.current.identifier,
945
- "regionCode": Locale.current.regionCode
977
+ "appVersion": Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown",
978
+ "buildNumber": Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "unknown"
946
979
  ],
980
+ "context": context,
947
981
  "properties": properties
948
982
  ]
949
983
 
984
+ // DETAILED LOGGING: Log the full request body
985
+ print("[RampKit] ")
986
+ print("[RampKit] ════════════════════════════════════════════════════════════")
987
+ print("[RampKit] 📤 SENDING PURCHASE EVENT TO BACKEND")
988
+ print("[RampKit] ════════════════════════════════════════════════════════════")
989
+ print("[RampKit] URL: https://uustlzuvjmochxkxatfx.supabase.co/functions/v1/app-user-events")
990
+ print("[RampKit] Method: POST")
991
+ print("[RampKit] ")
992
+ print("[RampKit] 📋 REQUEST BODY:")
993
+ print("[RampKit] appId: \(appId)")
994
+ print("[RampKit] appUserId: \(userId)")
995
+ print("[RampKit] eventName: \(eventName)")
996
+ if let originalTxId = properties["originalTransactionId"] {
997
+ print("[RampKit] properties.originalTransactionId: \(originalTxId) ✓ (CRITICAL for attribution)")
998
+ } else {
999
+ print("[RampKit] âš ī¸ WARNING: properties.originalTransactionId is MISSING!")
1000
+ }
1001
+ if let productId = properties["productId"] {
1002
+ print("[RampKit] properties.productId: \(productId)")
1003
+ }
1004
+ if let amount = properties["amount"], let currency = properties["currency"] {
1005
+ print("[RampKit] properties.amount: \(amount) \(currency)")
1006
+ }
1007
+ if let paywallId = paywallId {
1008
+ print("[RampKit] context.paywallId: \(paywallId) ✓")
1009
+ }
1010
+
1011
+ // Log full JSON for debugging
1012
+ if let jsonData = try? JSONSerialization.data(withJSONObject: event, options: .prettyPrinted),
1013
+ let jsonString = String(data: jsonData, encoding: .utf8) {
1014
+ print("[RampKit] ")
1015
+ print("[RampKit] 📄 FULL JSON PAYLOAD:")
1016
+ for line in jsonString.components(separatedBy: "\n") {
1017
+ print("[RampKit] \(line)")
1018
+ }
1019
+ }
1020
+ print("[RampKit] ════════════════════════════════════════════════════════════")
1021
+
950
1022
  guard let url = URL(string: "https://uustlzuvjmochxkxatfx.supabase.co/functions/v1/app-user-events") else {
1023
+ print("[RampKit] ❌ SEND FAILED: Invalid URL")
951
1024
  return SendEventResult(success: false, statusCode: 0, error: "Invalid URL")
952
1025
  }
953
1026
 
@@ -959,15 +1032,40 @@ public class RampKitModule: Module {
959
1032
 
960
1033
  do {
961
1034
  request.httpBody = try JSONSerialization.data(withJSONObject: event)
962
- let (_, response) = try await URLSession.shared.data(for: request)
1035
+ let (data, response) = try await URLSession.shared.data(for: request)
1036
+
963
1037
  if let httpResponse = response as? HTTPURLResponse {
964
- print("[RampKit] Purchase event sent: \(eventName) - Status: \(httpResponse.statusCode)")
965
1038
  let success = httpResponse.statusCode >= 200 && httpResponse.statusCode < 300
966
- return SendEventResult(success: success, statusCode: httpResponse.statusCode, error: success ? nil : "HTTP \(httpResponse.statusCode)")
1039
+ let responseBody = String(data: data, encoding: .utf8) ?? "(empty)"
1040
+
1041
+ // DETAILED LOGGING: Log the response
1042
+ print("[RampKit] ")
1043
+ print("[RampKit] đŸ“Ĩ RESPONSE FROM BACKEND:")
1044
+ print("[RampKit] HTTP Status: \(httpResponse.statusCode)")
1045
+ print("[RampKit] Success: \(success ? "✅ YES" : "❌ NO")")
1046
+ print("[RampKit] Response Body: \(responseBody)")
1047
+
1048
+ if success {
1049
+ print("[RampKit] ")
1050
+ print("[RampKit] ✅ EVENT SENT SUCCESSFULLY!")
1051
+ print("[RampKit] originalTransactionId \(properties["originalTransactionId"] ?? "unknown") will be marked as SENT")
1052
+ print("[RampKit] ════════════════════════════════════════════════════════════")
1053
+ } else {
1054
+ print("[RampKit] ")
1055
+ print("[RampKit] ❌ EVENT SEND FAILED!")
1056
+ print("[RampKit] Will retry on next app launch")
1057
+ print("[RampKit] ════════════════════════════════════════════════════════════")
1058
+ }
1059
+
1060
+ return SendEventResult(success: success, statusCode: httpResponse.statusCode, error: success ? nil : "HTTP \(httpResponse.statusCode): \(responseBody)")
967
1061
  }
1062
+ print("[RampKit] ❌ SEND FAILED: No HTTP response")
968
1063
  return SendEventResult(success: false, statusCode: 0, error: "No HTTP response")
969
1064
  } catch {
970
- print("[RampKit] Failed to send purchase event: \(error)")
1065
+ print("[RampKit] ")
1066
+ print("[RampKit] ❌ SEND FAILED: Network error")
1067
+ print("[RampKit] Error: \(error.localizedDescription)")
1068
+ print("[RampKit] ════════════════════════════════════════════════════════════")
971
1069
  return SendEventResult(success: false, statusCode: 0, error: error.localizedDescription)
972
1070
  }
973
1071
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rampkit-expo-dev",
3
- "version": "0.0.71",
3
+ "version": "0.0.73",
4
4
  "description": "The Expo SDK for RampKit. Build, test, and personalize app onboardings with instant updates.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",