a0-purchases
Version:
Lightweight subscription management for AI apps with auto-detecting providers
132 lines • 5.94 kB
JavaScript
import AsyncStorage from "@react-native-async-storage/async-storage";
import { USER_ID_STORAGE_KEY } from "./constants";
import { backendClient } from "./api/backendClient";
// Debug flag (set by PurchasesService)
let debugEnabled = false;
export const setDebugEnabled = (enabled) => {
debugEnabled = enabled;
};
const log = (...args) => {
if (debugEnabled) {
console.log(...args);
}
};
// Anonymous user ID prefix
export const ANONYMOUS_USER_PREFIX = "$AnonymousUser:";
// Generates a simple anonymous ID
const generateAnonymousId = () => {
return `${ANONYMOUS_USER_PREFIX}${Math.random().toString(36).substring(2, 15)}${Math.random().toString(36).substring(2, 15)}`;
};
// Check if a user ID is anonymous
export const isAnonymousUserId = (userId) => {
return !!userId && userId.startsWith(ANONYMOUS_USER_PREFIX);
};
// Gets the user ID from storage, or uses the provided custom ID, or generates a new anonymous one
export const getOrCreateUserId = async (customUserId) => {
// If custom user ID provided, we may need to link the previous ID (if any)
if (customUserId) {
try {
log('[IAP Storage] getOrCreateUserId called with customUserId:', customUserId);
const existingId = await AsyncStorage.getItem(USER_ID_STORAGE_KEY);
log('[IAP Storage] Existing ID from storage:', existingId);
// Persist the new ID immediately
await AsyncStorage.setItem(USER_ID_STORAGE_KEY, customUserId);
log('[IAP Storage] Saved new customUserId to storage');
// Ensure new user exists on backend
log('[IAP Storage] Logging in new user:', customUserId);
await backendClient.login(customUserId);
log('[IAP Storage] New user login successful');
if (existingId && existingId !== customUserId) {
// Only link if the old ID was anonymous
if (isAnonymousUserId(existingId)) {
log('[IAP Storage] Previous ID was anonymous, will link to new user:', {
oldId: existingId,
newId: customUserId,
isOldAnonymous: true
});
// Ensure old ID exists (idempotent)
try {
log('[IAP Storage] Ensuring old user exists:', existingId);
await backendClient.login(existingId);
log('[IAP Storage] Old user login successful');
}
catch (e) {
console.warn('[IAP Storage] Failed ensuring old user exists (non-blocking)', e);
}
// Link / alias the two IDs
try {
log('[IAP Storage] Calling linkAlias:', { from: existingId, to: customUserId });
await backendClient.linkAlias(existingId, customUserId);
log('[IAP Storage] LinkAlias successful');
}
catch (e) {
console.error('[IAP Storage] Failed to link alias', e);
}
}
else {
log('[IAP Storage] Previous ID was not anonymous, skipping link:', {
oldId: existingId,
newId: customUserId,
reason: 'Only anonymous users should be linked'
});
}
}
else {
log('[IAP Storage] No linking needed:', {
existingId,
customUserId,
reason: !existingId ? 'No existing ID' : 'Same ID'
});
}
}
catch (error) {
console.error('[IAP Storage] Error logging in user:', error);
}
return customUserId;
}
try {
// No custom ID provided - check what's in storage
log('[IAP Storage] No customUserId provided, checking storage');
let userId = await AsyncStorage.getItem(USER_ID_STORAGE_KEY);
log('[IAP Storage] ID from storage:', userId);
// If stored ID is NOT anonymous, we need to reset to anonymous
if (userId && !isAnonymousUserId(userId)) {
log('[IAP Storage] Stored ID is not anonymous, resetting to new anonymous user:', {
oldId: userId,
reason: 'App initialized without customUserId but had non-anonymous ID in storage'
});
// Generate new anonymous ID
userId = generateAnonymousId();
log('[IAP Storage] Generated new anonymous ID:', userId);
await AsyncStorage.setItem(USER_ID_STORAGE_KEY, userId);
// Note: We do NOT link the old custom ID to the new anonymous ID for security
}
else if (!userId) {
// No ID in storage, generate anonymous
userId = generateAnonymousId();
log('[IAP Storage] Generated new anonymous ID:', userId);
await AsyncStorage.setItem(USER_ID_STORAGE_KEY, userId);
}
log('[IAP Storage] Logging in user:', userId);
await backendClient.login(userId);
log('[IAP Storage] User login successful');
return userId;
}
catch (error) {
console.error('[IAP Storage] Error logging in user:', error);
return generateAnonymousId();
}
};
// Clears the stored user ID (e.g., on logout)
export const clearUserId = async () => {
await AsyncStorage.removeItem(USER_ID_STORAGE_KEY);
};
// Persist a provided user ID (e.g., after login)
export const setUserId = async (newUserId) => {
if (!newUserId) {
console.warn("[IAP Storage] setUserId called with empty value – ignored.");
return;
}
await AsyncStorage.setItem(USER_ID_STORAGE_KEY, newUserId);
};
//# sourceMappingURL=storage.js.map