rampkit-expo-dev 0.0.70 → 0.0.72
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 +4 -0
- package/build/RampKitNative.d.ts +32 -12
- package/build/RampKitNative.js +97 -23
- package/build/index.d.ts +1 -1
- package/ios/RampKitModule.swift +169 -25
- package/package.json +1 -1
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
|
}
|
|
@@ -323,6 +325,8 @@ class RampKitCore {
|
|
|
323
325
|
onCloseAction: (screenIndex, _screenId) => {
|
|
324
326
|
// Track onboarding completed - trigger: closed
|
|
325
327
|
EventManager_1.eventManager.trackOnboardingCompleted("closed", screenIndex + 1, screens.length, onboardingId);
|
|
328
|
+
// Auto-recheck transactions after onboarding closes (catches purchases made before closing)
|
|
329
|
+
RampKitNative_1.TransactionObserver.recheck().catch(() => { });
|
|
326
330
|
},
|
|
327
331
|
});
|
|
328
332
|
}
|
package/build/RampKitNative.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ interface RampKitNativeModule {
|
|
|
19
19
|
startTransactionObserver(appId: string): Promise<TransactionObserverResult>;
|
|
20
20
|
stopTransactionObserver(): Promise<void>;
|
|
21
21
|
clearTrackedTransactions(): Promise<number>;
|
|
22
|
+
recheckEntitlements(): Promise<EntitlementCheckResult>;
|
|
22
23
|
trackPurchaseCompleted(productId: string, transactionId?: string, originalTransactionId?: string): Promise<void>;
|
|
23
24
|
trackPurchaseFromProduct(productId: string): Promise<void>;
|
|
24
25
|
}
|
|
@@ -100,6 +101,28 @@ export interface SentEventResult {
|
|
|
100
101
|
currency?: string;
|
|
101
102
|
environment?: string;
|
|
102
103
|
}
|
|
104
|
+
export interface TrackedTransactionDetail {
|
|
105
|
+
productId: string;
|
|
106
|
+
transactionId: string;
|
|
107
|
+
originalTransactionId: string;
|
|
108
|
+
purchaseDate: string;
|
|
109
|
+
expirationDate?: string;
|
|
110
|
+
environment?: string;
|
|
111
|
+
status: "already_sent" | "skipped";
|
|
112
|
+
reason?: string;
|
|
113
|
+
}
|
|
114
|
+
export interface EntitlementCheckResult {
|
|
115
|
+
totalFound: number;
|
|
116
|
+
alreadyTracked: number;
|
|
117
|
+
newPurchases: number;
|
|
118
|
+
productIds: string[];
|
|
119
|
+
newProductIds: string[];
|
|
120
|
+
sentEvents?: SentEventResult[];
|
|
121
|
+
skippedReasons?: TrackedTransactionDetail[];
|
|
122
|
+
alreadyTrackedDetails?: TrackedTransactionDetail[];
|
|
123
|
+
trackedIdsCount: number;
|
|
124
|
+
error?: string;
|
|
125
|
+
}
|
|
103
126
|
export interface TransactionObserverResult {
|
|
104
127
|
configured: boolean;
|
|
105
128
|
appId: string;
|
|
@@ -107,18 +130,7 @@ export interface TransactionObserverResult {
|
|
|
107
130
|
previouslyTrackedCount: number;
|
|
108
131
|
iOSVersion: string;
|
|
109
132
|
listenerStarted: boolean;
|
|
110
|
-
entitlementCheck?:
|
|
111
|
-
totalFound: number;
|
|
112
|
-
alreadyTracked: number;
|
|
113
|
-
newPurchases: number;
|
|
114
|
-
productIds: string[];
|
|
115
|
-
newProductIds: string[];
|
|
116
|
-
sentEvents?: SentEventResult[];
|
|
117
|
-
skippedReasons?: Array<{
|
|
118
|
-
productId: string;
|
|
119
|
-
reason: string;
|
|
120
|
-
}>;
|
|
121
|
-
};
|
|
133
|
+
entitlementCheck?: EntitlementCheckResult;
|
|
122
134
|
error?: string;
|
|
123
135
|
}
|
|
124
136
|
export type ImpactStyle = "light" | "medium" | "heavy" | "rigid" | "soft";
|
|
@@ -224,4 +236,12 @@ export declare const TransactionObserver: {
|
|
|
224
236
|
* @returns The number of tracked transactions that were cleared
|
|
225
237
|
*/
|
|
226
238
|
clearTracked(): Promise<number>;
|
|
239
|
+
/**
|
|
240
|
+
* Re-check current entitlements for any new purchases
|
|
241
|
+
* Call this after onboarding finishes or after a paywall is shown
|
|
242
|
+
* to catch any purchases that may have been made
|
|
243
|
+
*
|
|
244
|
+
* @returns The entitlement check result with details of all transactions
|
|
245
|
+
*/
|
|
246
|
+
recheck(): Promise<EntitlementCheckResult | null>;
|
|
227
247
|
};
|
package/build/RampKitNative.js
CHANGED
|
@@ -81,6 +81,17 @@ function createFallbackModule() {
|
|
|
81
81
|
},
|
|
82
82
|
async stopTransactionObserver() { },
|
|
83
83
|
async clearTrackedTransactions() { return 0; },
|
|
84
|
+
async recheckEntitlements() {
|
|
85
|
+
return {
|
|
86
|
+
totalFound: 0,
|
|
87
|
+
alreadyTracked: 0,
|
|
88
|
+
newPurchases: 0,
|
|
89
|
+
productIds: [],
|
|
90
|
+
newProductIds: [],
|
|
91
|
+
trackedIdsCount: 0,
|
|
92
|
+
error: "Native module not available - using fallback"
|
|
93
|
+
};
|
|
94
|
+
},
|
|
84
95
|
async trackPurchaseCompleted(_productId, _transactionId, _originalTransactionId) { },
|
|
85
96
|
async trackPurchaseFromProduct(_productId) { },
|
|
86
97
|
};
|
|
@@ -279,6 +290,72 @@ exports.Notifications = {
|
|
|
279
290
|
// ============================================================================
|
|
280
291
|
// Transaction Observer API (StoreKit 2 / Google Play Billing)
|
|
281
292
|
// ============================================================================
|
|
293
|
+
/**
|
|
294
|
+
* Helper function to log entitlement check results with full details
|
|
295
|
+
*/
|
|
296
|
+
function logEntitlementCheckResult(result, context) {
|
|
297
|
+
console.log("[RampKit] ");
|
|
298
|
+
console.log("[RampKit] ═══════════════════════════════════════════════════════════");
|
|
299
|
+
console.log(`[RampKit] 📊 ENTITLEMENT CHECK RESULT (${context})`);
|
|
300
|
+
console.log("[RampKit] ═══════════════════════════════════════════════════════════");
|
|
301
|
+
console.log("[RampKit] Total entitlements found:", result.totalFound);
|
|
302
|
+
console.log("[RampKit] Already sent to backend: ", result.alreadyTracked);
|
|
303
|
+
console.log("[RampKit] New events sent: ", result.newPurchases);
|
|
304
|
+
console.log("[RampKit] Tracked IDs in storage: ", result.trackedIdsCount);
|
|
305
|
+
console.log("[RampKit] Product IDs: ", result.productIds);
|
|
306
|
+
// Log already tracked transactions with full details
|
|
307
|
+
if (result.alreadyTrackedDetails && result.alreadyTrackedDetails.length > 0) {
|
|
308
|
+
console.log("[RampKit] ");
|
|
309
|
+
console.log("[RampKit] ✅ ALREADY SENT TRANSACTIONS:");
|
|
310
|
+
for (const tx of result.alreadyTrackedDetails) {
|
|
311
|
+
console.log("[RampKit] ────────────────────────────────────────");
|
|
312
|
+
console.log("[RampKit] 📦 Product:", tx.productId);
|
|
313
|
+
console.log("[RampKit] Transaction ID:", tx.transactionId);
|
|
314
|
+
console.log("[RampKit] Original Transaction ID:", tx.originalTransactionId);
|
|
315
|
+
console.log("[RampKit] Purchase Date:", tx.purchaseDate);
|
|
316
|
+
if (tx.expirationDate) {
|
|
317
|
+
console.log("[RampKit] Expiration Date:", tx.expirationDate);
|
|
318
|
+
}
|
|
319
|
+
if (tx.environment) {
|
|
320
|
+
console.log("[RampKit] Environment:", tx.environment);
|
|
321
|
+
}
|
|
322
|
+
console.log("[RampKit] Status: ✅ ALREADY SENT TO BACKEND");
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// Log newly sent events
|
|
326
|
+
if (result.sentEvents && result.sentEvents.length > 0) {
|
|
327
|
+
console.log("[RampKit] ");
|
|
328
|
+
console.log("[RampKit] 📤 NEWLY SENT EVENTS:");
|
|
329
|
+
for (const event of result.sentEvents) {
|
|
330
|
+
console.log("[RampKit] ────────────────────────────────────────");
|
|
331
|
+
console.log("[RampKit] 📦 Product:", event.productId);
|
|
332
|
+
console.log("[RampKit] Transaction ID:", event.transactionId);
|
|
333
|
+
console.log("[RampKit] Original Transaction ID:", event.originalTransactionId);
|
|
334
|
+
console.log("[RampKit] Purchase Date:", event.purchaseDate);
|
|
335
|
+
console.log("[RampKit] Status:", event.status === "sent" ? "✅ SENT" : `❌ ${event.status.toUpperCase()}`);
|
|
336
|
+
if (event.httpStatus) {
|
|
337
|
+
console.log("[RampKit] HTTP Status:", event.httpStatus);
|
|
338
|
+
}
|
|
339
|
+
if (event.error) {
|
|
340
|
+
console.log("[RampKit] Error:", event.error);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// Log skipped transactions
|
|
345
|
+
if (result.skippedReasons && result.skippedReasons.length > 0) {
|
|
346
|
+
console.log("[RampKit] ");
|
|
347
|
+
console.log("[RampKit] ⏭️ SKIPPED TRANSACTIONS:");
|
|
348
|
+
for (const skipped of result.skippedReasons) {
|
|
349
|
+
console.log("[RampKit] - Product:", skipped.productId, "| Reason:", skipped.reason);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (result.error) {
|
|
353
|
+
console.log("[RampKit] ");
|
|
354
|
+
console.log("[RampKit] ⚠️ Error:", result.error);
|
|
355
|
+
}
|
|
356
|
+
console.log("[RampKit] ═══════════════════════════════════════════════════════════");
|
|
357
|
+
console.log("[RampKit] ");
|
|
358
|
+
}
|
|
282
359
|
exports.TransactionObserver = {
|
|
283
360
|
/**
|
|
284
361
|
* Start listening for purchase transactions
|
|
@@ -300,29 +377,7 @@ exports.TransactionObserver = {
|
|
|
300
377
|
console.log("[RampKit] - previouslyTrackedCount:", result.previouslyTrackedCount);
|
|
301
378
|
console.log("[RampKit] - listenerStarted:", result.listenerStarted);
|
|
302
379
|
if (result.entitlementCheck) {
|
|
303
|
-
|
|
304
|
-
console.log("[RampKit] - totalFound:", result.entitlementCheck.totalFound);
|
|
305
|
-
console.log("[RampKit] - alreadyTracked:", result.entitlementCheck.alreadyTracked);
|
|
306
|
-
console.log("[RampKit] - newPurchases:", result.entitlementCheck.newPurchases);
|
|
307
|
-
console.log("[RampKit] - productIds:", result.entitlementCheck.productIds);
|
|
308
|
-
console.log("[RampKit] - newProductIds:", result.entitlementCheck.newProductIds);
|
|
309
|
-
// Log sent events details
|
|
310
|
-
if (result.entitlementCheck.sentEvents && result.entitlementCheck.sentEvents.length > 0) {
|
|
311
|
-
console.log("[RampKit] 📤 Sent events:");
|
|
312
|
-
for (const event of result.entitlementCheck.sentEvents) {
|
|
313
|
-
console.log("[RampKit] - productId:", event.productId);
|
|
314
|
-
console.log("[RampKit] transactionId:", event.transactionId);
|
|
315
|
-
console.log("[RampKit] originalTransactionId:", event.originalTransactionId);
|
|
316
|
-
console.log("[RampKit] status:", event.status);
|
|
317
|
-
console.log("[RampKit] httpStatus:", event.httpStatus);
|
|
318
|
-
if (event.error) {
|
|
319
|
-
console.log("[RampKit] error:", event.error);
|
|
320
|
-
}
|
|
321
|
-
if (event.reason) {
|
|
322
|
-
console.log("[RampKit] reason:", event.reason);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
380
|
+
logEntitlementCheckResult(result.entitlementCheck, "STARTUP");
|
|
326
381
|
}
|
|
327
382
|
if (result.error) {
|
|
328
383
|
console.warn("[RampKit] ⚠️ Error:", result.error);
|
|
@@ -399,4 +454,23 @@ exports.TransactionObserver = {
|
|
|
399
454
|
return 0;
|
|
400
455
|
}
|
|
401
456
|
},
|
|
457
|
+
/**
|
|
458
|
+
* Re-check current entitlements for any new purchases
|
|
459
|
+
* Call this after onboarding finishes or after a paywall is shown
|
|
460
|
+
* to catch any purchases that may have been made
|
|
461
|
+
*
|
|
462
|
+
* @returns The entitlement check result with details of all transactions
|
|
463
|
+
*/
|
|
464
|
+
async recheck() {
|
|
465
|
+
console.log("[RampKit] 🔄 Re-checking entitlements...");
|
|
466
|
+
try {
|
|
467
|
+
const result = await RampKitNativeModule.recheckEntitlements();
|
|
468
|
+
logEntitlementCheckResult(result, "RECHECK");
|
|
469
|
+
return result;
|
|
470
|
+
}
|
|
471
|
+
catch (e) {
|
|
472
|
+
console.warn("[RampKit] ❌ Failed to recheck entitlements:", e);
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
},
|
|
402
476
|
};
|
package/build/index.d.ts
CHANGED
|
@@ -10,6 +10,6 @@ export { collectDeviceInfo, getSessionDurationSeconds, getSessionStartTime, buil
|
|
|
10
10
|
export { default as RampKitNative } from "./RampKitNative";
|
|
11
11
|
export type { NativeDeviceInfo, NativeLaunchData } from "./RampKitNative";
|
|
12
12
|
export { Haptics, StoreReview, Notifications, TransactionObserver, isNativeModuleAvailable } from "./RampKitNative";
|
|
13
|
-
export type { ImpactStyle, NotificationType, NotificationOptions, NotificationPermissionResult, TransactionObserverResult, SentEventResult } from "./RampKitNative";
|
|
13
|
+
export type { ImpactStyle, NotificationType, NotificationOptions, NotificationPermissionResult, TransactionObserverResult, SentEventResult, TrackedTransactionDetail, EntitlementCheckResult } from "./RampKitNative";
|
|
14
14
|
export type { DeviceInfo, RampKitEvent, EventDevice, EventContext, RampKitConfig, RampKitEventName, RampKitContext, RampKitDeviceContext, RampKitUserContext, NavigationData, ScreenPosition, OnboardingResponse, AppSessionStartedProperties, OnboardingStartedProperties, OnboardingCompletedProperties, OnboardingAbandonedProperties, OptionSelectedProperties, NotificationsResponseProperties, PaywallShownProperties, PurchaseStartedProperties, PurchaseCompletedProperties, PurchaseFailedProperties, PurchaseRestoredProperties, } from "./types";
|
|
15
15
|
export { SDK_VERSION, CAPABILITIES } from "./constants";
|
package/ios/RampKitModule.swift
CHANGED
|
@@ -119,6 +119,15 @@ public class RampKitModule: Module {
|
|
|
119
119
|
return count
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
AsyncFunction("recheckEntitlements") { () -> [String: Any] in
|
|
123
|
+
print("[RampKit] 🔄 Re-checking entitlements (called from JS)...")
|
|
124
|
+
if #available(iOS 15.0, *) {
|
|
125
|
+
return await self.checkAndTrackCurrentEntitlements()
|
|
126
|
+
} else {
|
|
127
|
+
return ["error": "iOS 15+ required"]
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
122
131
|
// ============================================================================
|
|
123
132
|
// Manual Purchase Tracking (Fallback for Superwall/RevenueCat)
|
|
124
133
|
// ============================================================================
|
|
@@ -477,9 +486,28 @@ public class RampKitModule: Module {
|
|
|
477
486
|
/// Check all current entitlements and track any we haven't seen before
|
|
478
487
|
/// This catches purchases made by Superwall/RevenueCat before our observer started
|
|
479
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.
|
|
480
493
|
@available(iOS 15.0, *)
|
|
481
494
|
private func checkAndTrackCurrentEntitlements() async -> [String: Any] {
|
|
482
|
-
print("[RampKit]
|
|
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] ")
|
|
508
|
+
|
|
509
|
+
let formatter = ISO8601DateFormatter()
|
|
510
|
+
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
|
483
511
|
|
|
484
512
|
var foundCount = 0
|
|
485
513
|
var trackedCount = 0
|
|
@@ -488,6 +516,7 @@ public class RampKitModule: Module {
|
|
|
488
516
|
var newProductIds: [String] = []
|
|
489
517
|
var sentEvents: [[String: Any]] = []
|
|
490
518
|
var skippedReasons: [[String: Any]] = []
|
|
519
|
+
var alreadyTrackedDetails: [[String: Any]] = [] // NEW: Details of already-tracked transactions
|
|
491
520
|
|
|
492
521
|
for await result in Transaction.currentEntitlements {
|
|
493
522
|
foundCount += 1
|
|
@@ -501,26 +530,57 @@ public class RampKitModule: Module {
|
|
|
501
530
|
let originalId = String(transaction.originalID)
|
|
502
531
|
let transactionId = String(transaction.id)
|
|
503
532
|
productIds.append(transaction.productID)
|
|
504
|
-
|
|
533
|
+
|
|
534
|
+
// Build transaction details for logging
|
|
535
|
+
var txDetails: [String: Any] = [
|
|
536
|
+
"productId": transaction.productID,
|
|
537
|
+
"transactionId": transactionId,
|
|
538
|
+
"originalTransactionId": originalId,
|
|
539
|
+
"purchaseDate": formatter.string(from: transaction.purchaseDate)
|
|
540
|
+
]
|
|
541
|
+
if let expirationDate = transaction.expirationDate {
|
|
542
|
+
txDetails["expirationDate"] = formatter.string(from: expirationDate)
|
|
543
|
+
}
|
|
544
|
+
if #available(iOS 16.0, *) {
|
|
545
|
+
txDetails["environment"] = transaction.environment.rawValue
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
print("[RampKit] 📦 Found entitlement:")
|
|
549
|
+
print("[RampKit] - productId: \(transaction.productID)")
|
|
550
|
+
print("[RampKit] - transactionId: \(transactionId)")
|
|
551
|
+
print("[RampKit] - originalTransactionId: \(originalId)")
|
|
552
|
+
print("[RampKit] - purchaseDate: \(formatter.string(from: transaction.purchaseDate))")
|
|
505
553
|
|
|
506
554
|
// Check if we've already tracked this transaction
|
|
555
|
+
// "Tracked" means we successfully sent a purchase_completed event to the backend
|
|
556
|
+
// and received an HTTP 2xx response. We store the originalTransactionId after success.
|
|
507
557
|
if trackedTransactionIds.contains(originalId) {
|
|
508
558
|
trackedCount += 1
|
|
509
|
-
|
|
559
|
+
txDetails["status"] = "already_sent"
|
|
560
|
+
alreadyTrackedDetails.append(txDetails)
|
|
561
|
+
print("[RampKit] ✅ STATUS: ALREADY SENT TO BACKEND")
|
|
562
|
+
print("[RampKit] (originalTransactionId \(originalId) is in our sent-transactions storage)")
|
|
563
|
+
print("[RampKit] This means we previously sent a purchase_completed event")
|
|
564
|
+
print("[RampKit] and received a successful HTTP 2xx response.")
|
|
565
|
+
print("[RampKit] ")
|
|
510
566
|
continue
|
|
511
567
|
}
|
|
512
568
|
|
|
513
569
|
// Skip renewals and revocations
|
|
514
570
|
guard transaction.originalID == transaction.id,
|
|
515
571
|
transaction.revocationDate == nil else {
|
|
516
|
-
|
|
572
|
+
let reason = transaction.revocationDate != nil ? "revoked" : "renewal"
|
|
573
|
+
txDetails["status"] = "skipped"
|
|
574
|
+
txDetails["reason"] = reason
|
|
575
|
+
skippedReasons.append(txDetails)
|
|
576
|
+
print("[RampKit] ⏭️ STATUS: Skipped (\(reason))")
|
|
517
577
|
continue
|
|
518
578
|
}
|
|
519
579
|
|
|
520
580
|
// NEW transaction we haven't seen!
|
|
521
581
|
newCount += 1
|
|
522
582
|
newProductIds.append(transaction.productID)
|
|
523
|
-
print("[RampKit] 🆕 NEW purchase
|
|
583
|
+
print("[RampKit] 🆕 STATUS: NEW purchase - will send to backend now...")
|
|
524
584
|
|
|
525
585
|
// Track it and get the result
|
|
526
586
|
let sendResult = await self.handleTransactionWithResult(transaction)
|
|
@@ -530,16 +590,28 @@ public class RampKitModule: Module {
|
|
|
530
590
|
if let status = sendResult["status"] as? String, status == "sent" {
|
|
531
591
|
trackedTransactionIds.insert(originalId)
|
|
532
592
|
saveTrackedTransactions()
|
|
533
|
-
print("[RampKit] ✅
|
|
593
|
+
print("[RampKit] ✅ Event sent successfully! HTTP status: \(sendResult["httpStatus"] ?? "unknown")")
|
|
594
|
+
print("[RampKit] ✅ Marked originalTransactionId \(originalId) as tracked")
|
|
534
595
|
} else {
|
|
535
|
-
print("[RampKit]
|
|
596
|
+
print("[RampKit] ❌ Send failed: \(sendResult["error"] ?? "unknown error")")
|
|
597
|
+
print("[RampKit] ⚠️ Will retry on next app launch")
|
|
536
598
|
}
|
|
537
599
|
}
|
|
538
600
|
|
|
539
|
-
print("[RampKit]
|
|
540
|
-
print("[RampKit]
|
|
541
|
-
print("[RampKit]
|
|
542
|
-
print("[RampKit]
|
|
601
|
+
print("[RampKit] ")
|
|
602
|
+
print("[RampKit] ════════════════════════════════════════════════════════════")
|
|
603
|
+
print("[RampKit] 📊 ENTITLEMENT CHECK SUMMARY")
|
|
604
|
+
print("[RampKit] ════════════════════════════════════════════════════════════")
|
|
605
|
+
print("[RampKit] Total entitlements found: \(foundCount)")
|
|
606
|
+
print("[RampKit] Already sent (HTTP 2xx): \(trackedCount) (no action needed)")
|
|
607
|
+
print("[RampKit] Skipped (renewal/revoked): \(skippedReasons.count) (backend gets via S2S)")
|
|
608
|
+
print("[RampKit] NEW events sent this session: \(newCount)")
|
|
609
|
+
print("[RampKit] Total tracked IDs in storage: \(trackedTransactionIds.count)")
|
|
610
|
+
if newCount == 0 && foundCount > 0 {
|
|
611
|
+
print("[RampKit] ")
|
|
612
|
+
print("[RampKit] ℹ️ All purchases already sent or skipped - nothing new to send")
|
|
613
|
+
}
|
|
614
|
+
print("[RampKit] ════════════════════════════════════════════════════════════")
|
|
543
615
|
|
|
544
616
|
return [
|
|
545
617
|
"totalFound": foundCount,
|
|
@@ -548,7 +620,9 @@ public class RampKitModule: Module {
|
|
|
548
620
|
"productIds": productIds,
|
|
549
621
|
"newProductIds": newProductIds,
|
|
550
622
|
"sentEvents": sentEvents,
|
|
551
|
-
"skippedReasons": skippedReasons
|
|
623
|
+
"skippedReasons": skippedReasons,
|
|
624
|
+
"alreadyTrackedDetails": alreadyTrackedDetails, // NEW
|
|
625
|
+
"trackedIdsCount": trackedTransactionIds.count
|
|
552
626
|
]
|
|
553
627
|
}
|
|
554
628
|
|
|
@@ -865,8 +939,8 @@ public class RampKitModule: Module {
|
|
|
865
939
|
}
|
|
866
940
|
}
|
|
867
941
|
|
|
868
|
-
private func sendPurchaseEvent(appId: String, userId: String, eventName: String, properties: [String: Any]) async {
|
|
869
|
-
let _ = await sendPurchaseEventWithResult(appId: appId, userId: userId, eventName: eventName, properties: properties)
|
|
942
|
+
private func sendPurchaseEvent(appId: String, userId: String, eventName: String, properties: [String: Any], paywallId: String? = nil) async {
|
|
943
|
+
let _ = await sendPurchaseEventWithResult(appId: appId, userId: userId, eventName: eventName, properties: properties, paywallId: paywallId)
|
|
870
944
|
}
|
|
871
945
|
|
|
872
946
|
private struct SendEventResult {
|
|
@@ -875,7 +949,16 @@ public class RampKitModule: Module {
|
|
|
875
949
|
let error: String?
|
|
876
950
|
}
|
|
877
951
|
|
|
878
|
-
private func sendPurchaseEventWithResult(appId: String, userId: String, eventName: String, properties: [String: Any]) async -> SendEventResult {
|
|
952
|
+
private func sendPurchaseEventWithResult(appId: String, userId: String, eventName: String, properties: [String: Any], paywallId: String? = nil) async -> SendEventResult {
|
|
953
|
+
// Build context with optional paywallId for attribution
|
|
954
|
+
var context: [String: Any] = [
|
|
955
|
+
"locale": Locale.current.identifier,
|
|
956
|
+
"regionCode": Locale.current.regionCode as Any
|
|
957
|
+
]
|
|
958
|
+
if let paywallId = paywallId {
|
|
959
|
+
context["paywallId"] = paywallId
|
|
960
|
+
}
|
|
961
|
+
|
|
879
962
|
let event: [String: Any] = [
|
|
880
963
|
"appId": appId,
|
|
881
964
|
"appUserId": userId,
|
|
@@ -888,17 +971,53 @@ public class RampKitModule: Module {
|
|
|
888
971
|
"platformVersion": UIDevice.current.systemVersion,
|
|
889
972
|
"deviceModel": getDeviceModelIdentifier(),
|
|
890
973
|
"sdkVersion": "1.0.0",
|
|
891
|
-
"appVersion": Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String,
|
|
892
|
-
"buildNumber": Bundle.main.infoDictionary?["CFBundleVersion"] as? String
|
|
893
|
-
],
|
|
894
|
-
"context": [
|
|
895
|
-
"locale": Locale.current.identifier,
|
|
896
|
-
"regionCode": Locale.current.regionCode
|
|
974
|
+
"appVersion": Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown",
|
|
975
|
+
"buildNumber": Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "unknown"
|
|
897
976
|
],
|
|
977
|
+
"context": context,
|
|
898
978
|
"properties": properties
|
|
899
979
|
]
|
|
900
980
|
|
|
981
|
+
// DETAILED LOGGING: Log the full request body
|
|
982
|
+
print("[RampKit] ")
|
|
983
|
+
print("[RampKit] ════════════════════════════════════════════════════════════")
|
|
984
|
+
print("[RampKit] 📤 SENDING PURCHASE EVENT TO BACKEND")
|
|
985
|
+
print("[RampKit] ════════════════════════════════════════════════════════════")
|
|
986
|
+
print("[RampKit] URL: https://uustlzuvjmochxkxatfx.supabase.co/functions/v1/app-user-events")
|
|
987
|
+
print("[RampKit] Method: POST")
|
|
988
|
+
print("[RampKit] ")
|
|
989
|
+
print("[RampKit] 📋 REQUEST BODY:")
|
|
990
|
+
print("[RampKit] appId: \(appId)")
|
|
991
|
+
print("[RampKit] appUserId: \(userId)")
|
|
992
|
+
print("[RampKit] eventName: \(eventName)")
|
|
993
|
+
if let originalTxId = properties["originalTransactionId"] {
|
|
994
|
+
print("[RampKit] properties.originalTransactionId: \(originalTxId) ✓ (CRITICAL for attribution)")
|
|
995
|
+
} else {
|
|
996
|
+
print("[RampKit] ⚠️ WARNING: properties.originalTransactionId is MISSING!")
|
|
997
|
+
}
|
|
998
|
+
if let productId = properties["productId"] {
|
|
999
|
+
print("[RampKit] properties.productId: \(productId)")
|
|
1000
|
+
}
|
|
1001
|
+
if let amount = properties["amount"], let currency = properties["currency"] {
|
|
1002
|
+
print("[RampKit] properties.amount: \(amount) \(currency)")
|
|
1003
|
+
}
|
|
1004
|
+
if let paywallId = paywallId {
|
|
1005
|
+
print("[RampKit] context.paywallId: \(paywallId) ✓")
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
// Log full JSON for debugging
|
|
1009
|
+
if let jsonData = try? JSONSerialization.data(withJSONObject: event, options: .prettyPrinted),
|
|
1010
|
+
let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
1011
|
+
print("[RampKit] ")
|
|
1012
|
+
print("[RampKit] 📄 FULL JSON PAYLOAD:")
|
|
1013
|
+
for line in jsonString.components(separatedBy: "\n") {
|
|
1014
|
+
print("[RampKit] \(line)")
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
print("[RampKit] ════════════════════════════════════════════════════════════")
|
|
1018
|
+
|
|
901
1019
|
guard let url = URL(string: "https://uustlzuvjmochxkxatfx.supabase.co/functions/v1/app-user-events") else {
|
|
1020
|
+
print("[RampKit] ❌ SEND FAILED: Invalid URL")
|
|
902
1021
|
return SendEventResult(success: false, statusCode: 0, error: "Invalid URL")
|
|
903
1022
|
}
|
|
904
1023
|
|
|
@@ -910,15 +1029,40 @@ public class RampKitModule: Module {
|
|
|
910
1029
|
|
|
911
1030
|
do {
|
|
912
1031
|
request.httpBody = try JSONSerialization.data(withJSONObject: event)
|
|
913
|
-
let (
|
|
1032
|
+
let (data, response) = try await URLSession.shared.data(for: request)
|
|
1033
|
+
|
|
914
1034
|
if let httpResponse = response as? HTTPURLResponse {
|
|
915
|
-
print("[RampKit] Purchase event sent: \(eventName) - Status: \(httpResponse.statusCode)")
|
|
916
1035
|
let success = httpResponse.statusCode >= 200 && httpResponse.statusCode < 300
|
|
917
|
-
|
|
1036
|
+
let responseBody = String(data: data, encoding: .utf8) ?? "(empty)"
|
|
1037
|
+
|
|
1038
|
+
// DETAILED LOGGING: Log the response
|
|
1039
|
+
print("[RampKit] ")
|
|
1040
|
+
print("[RampKit] 📥 RESPONSE FROM BACKEND:")
|
|
1041
|
+
print("[RampKit] HTTP Status: \(httpResponse.statusCode)")
|
|
1042
|
+
print("[RampKit] Success: \(success ? "✅ YES" : "❌ NO")")
|
|
1043
|
+
print("[RampKit] Response Body: \(responseBody)")
|
|
1044
|
+
|
|
1045
|
+
if success {
|
|
1046
|
+
print("[RampKit] ")
|
|
1047
|
+
print("[RampKit] ✅ EVENT SENT SUCCESSFULLY!")
|
|
1048
|
+
print("[RampKit] originalTransactionId \(properties["originalTransactionId"] ?? "unknown") will be marked as SENT")
|
|
1049
|
+
print("[RampKit] ════════════════════════════════════════════════════════════")
|
|
1050
|
+
} else {
|
|
1051
|
+
print("[RampKit] ")
|
|
1052
|
+
print("[RampKit] ❌ EVENT SEND FAILED!")
|
|
1053
|
+
print("[RampKit] Will retry on next app launch")
|
|
1054
|
+
print("[RampKit] ════════════════════════════════════════════════════════════")
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
return SendEventResult(success: success, statusCode: httpResponse.statusCode, error: success ? nil : "HTTP \(httpResponse.statusCode): \(responseBody)")
|
|
918
1058
|
}
|
|
1059
|
+
print("[RampKit] ❌ SEND FAILED: No HTTP response")
|
|
919
1060
|
return SendEventResult(success: false, statusCode: 0, error: "No HTTP response")
|
|
920
1061
|
} catch {
|
|
921
|
-
print("[RampKit]
|
|
1062
|
+
print("[RampKit] ")
|
|
1063
|
+
print("[RampKit] ❌ SEND FAILED: Network error")
|
|
1064
|
+
print("[RampKit] Error: \(error.localizedDescription)")
|
|
1065
|
+
print("[RampKit] ════════════════════════════════════════════════════════════")
|
|
922
1066
|
return SendEventResult(success: false, statusCode: 0, error: error.localizedDescription)
|
|
923
1067
|
}
|
|
924
1068
|
}
|
package/package.json
CHANGED