UNPKG

react-native-iap

Version:

React Native In-App Purchases module for iOS and Android using Nitro

326 lines (317 loc) 11.5 kB
"use strict"; /** * Type Bridge Utilities * * Converts the loose Nitro shapes coming from native into the strongly typed * structures that our generated TypeScript types expect. */ const PLATFORM_IOS = 'ios'; const PLATFORM_ANDROID = 'android'; const PRODUCT_TYPE_SUBS = 'subs'; const PRODUCT_TYPE_IN_APP = 'in-app'; const PURCHASE_STATE_DEFERRED = 'deferred'; const PURCHASE_STATE_FAILED = 'failed'; const PURCHASE_STATE_PENDING = 'pending'; const PURCHASE_STATE_PURCHASED = 'purchased'; const PURCHASE_STATE_RESTORED = 'restored'; const PURCHASE_STATE_UNKNOWN = 'unknown'; const PAYMENT_MODE_EMPTY = 'empty'; const PAYMENT_MODE_FREE_TRIAL = 'free-trial'; const PAYMENT_MODE_PAY_AS_YOU_GO = 'pay-as-you-go'; const PAYMENT_MODE_PAY_UP_FRONT = 'pay-up-front'; const SUBSCRIPTION_PERIOD_DAY = 'day'; const SUBSCRIPTION_PERIOD_WEEK = 'week'; const SUBSCRIPTION_PERIOD_MONTH = 'month'; const SUBSCRIPTION_PERIOD_YEAR = 'year'; const SUBSCRIPTION_PERIOD_EMPTY = 'empty'; const DEFAULT_JSON_REPR = '{}'; function normalizePlatform(value) { return value?.toLowerCase() === PLATFORM_IOS ? PLATFORM_IOS : PLATFORM_ANDROID; } function normalizeProductType(value) { return value?.toLowerCase() === PRODUCT_TYPE_SUBS ? PRODUCT_TYPE_SUBS : PRODUCT_TYPE_IN_APP; } function normalizeProductTypeIOS(value) { switch ((value ?? '').toLowerCase()) { case 'consumable': return 'consumable'; case 'nonconsumable': case 'non_consumable': case 'non-consumable': return 'non-consumable'; case 'autorenewablesubscription': case 'auto_renewable_subscription': case 'autorenewable': return 'auto-renewable-subscription'; case 'nonrenewingsubscription': case 'non_renewing_subscription': return 'non-renewing-subscription'; default: if (value) { console.warn(`[react-native-iap] Unknown iOS product type "${value}", defaulting to NonConsumable.`); } return 'non-consumable'; } } function normalizePaymentMode(value) { switch ((value ?? '').toUpperCase()) { case 'FREE_TRIAL': case 'FREETRIAL': return PAYMENT_MODE_FREE_TRIAL; case 'PAY_AS_YOU_GO': case 'PAYASYOUGO': return PAYMENT_MODE_PAY_AS_YOU_GO; case 'PAY_UP_FRONT': case 'PAYUPFRONT': return PAYMENT_MODE_PAY_UP_FRONT; default: return PAYMENT_MODE_EMPTY; } } function normalizeSubscriptionPeriod(value) { switch ((value ?? '').toUpperCase()) { case 'DAY': return SUBSCRIPTION_PERIOD_DAY; case 'WEEK': return SUBSCRIPTION_PERIOD_WEEK; case 'MONTH': return SUBSCRIPTION_PERIOD_MONTH; case 'YEAR': return SUBSCRIPTION_PERIOD_YEAR; default: return SUBSCRIPTION_PERIOD_EMPTY; } } function normalizePurchaseState(state) { if (typeof state === 'string') { switch (state.toLowerCase()) { case PURCHASE_STATE_PURCHASED: return PURCHASE_STATE_PURCHASED; case PURCHASE_STATE_PENDING: return PURCHASE_STATE_PENDING; case PURCHASE_STATE_FAILED: return PURCHASE_STATE_FAILED; case PURCHASE_STATE_RESTORED: return PURCHASE_STATE_RESTORED; case PURCHASE_STATE_DEFERRED: return PURCHASE_STATE_DEFERRED; default: return PURCHASE_STATE_UNKNOWN; } } if (typeof state === 'number') { switch (state) { case 1: return PURCHASE_STATE_PURCHASED; case 2: return PURCHASE_STATE_PENDING; default: return PURCHASE_STATE_UNKNOWN; } } return PURCHASE_STATE_UNKNOWN; } function toNullableString(value) { if (value == null) return null; return String(value); } function toNullableNumber(value) { if (value == null) return null; const num = Number(value); return Number.isFinite(num) ? num : null; } function toNullableBoolean(value) { if (value == null) return null; if (typeof value === 'boolean') return value; if (typeof value === 'number') return value !== 0; if (typeof value === 'string') return value.toLowerCase() === 'true'; return null; } function parseSubscriptionOffers(value) { if (!value) return undefined; try { const parsed = JSON.parse(value); if (Array.isArray(parsed)) { return parsed; } } catch (error) { console.warn('Failed to parse subscriptionOfferDetailsAndroid:', error); } return undefined; } /** * Convert NitroProduct (from native) to generated Product type */ export function convertNitroProductToProduct(nitroProduct) { const platform = normalizePlatform(nitroProduct.platform); const type = normalizeProductType(nitroProduct.type); const base = { id: nitroProduct.id, title: nitroProduct.title, description: nitroProduct.description, type, displayName: nitroProduct.displayName ?? null, displayPrice: nitroProduct.displayPrice ?? '', currency: nitroProduct.currency ?? '', price: toNullableNumber(nitroProduct.price), debugDescription: null, platform }; if (platform === PLATFORM_IOS) { const iosProduct = { ...base, displayNameIOS: nitroProduct.displayName ?? nitroProduct.title, isFamilyShareableIOS: Boolean(nitroProduct.isFamilyShareableIOS ?? false), jsonRepresentationIOS: nitroProduct.jsonRepresentationIOS ?? DEFAULT_JSON_REPR, typeIOS: normalizeProductTypeIOS(nitroProduct.typeIOS), subscriptionInfoIOS: undefined }; iosProduct.introductoryPriceAsAmountIOS = toNullableString(nitroProduct.introductoryPriceAsAmountIOS); iosProduct.introductoryPriceIOS = toNullableString(nitroProduct.introductoryPriceIOS); iosProduct.introductoryPriceNumberOfPeriodsIOS = toNullableString(nitroProduct.introductoryPriceNumberOfPeriodsIOS); iosProduct.introductoryPricePaymentModeIOS = normalizePaymentMode(nitroProduct.introductoryPricePaymentModeIOS); iosProduct.introductoryPriceSubscriptionPeriodIOS = normalizeSubscriptionPeriod(nitroProduct.introductoryPriceSubscriptionPeriodIOS); iosProduct.subscriptionPeriodNumberIOS = toNullableString(nitroProduct.subscriptionPeriodNumberIOS); iosProduct.subscriptionPeriodUnitIOS = normalizeSubscriptionPeriod(nitroProduct.subscriptionPeriodUnitIOS); return iosProduct; } const androidProduct = { ...base, nameAndroid: nitroProduct.nameAndroid ?? nitroProduct.title, oneTimePurchaseOfferDetailsAndroid: nitroProduct.oneTimePurchaseOfferDetailsAndroid, subscriptionOfferDetailsAndroid: parseSubscriptionOffers(nitroProduct.subscriptionOfferDetailsAndroid) }; if (type === PRODUCT_TYPE_SUBS) { if (!Array.isArray(androidProduct.subscriptionOfferDetailsAndroid)) { androidProduct.subscriptionOfferDetailsAndroid = []; } } return androidProduct; } /** * Convert Product to ProductSubscription (type-safe casting helper) */ export function convertProductToProductSubscription(product) { if (product.type !== PRODUCT_TYPE_SUBS) { console.warn('Converting non-subscription product to ProductSubscription:', product.id); } const output = { ...product }; if (output.platform === PLATFORM_ANDROID) { if (!Array.isArray(output.subscriptionOfferDetailsAndroid)) { output.subscriptionOfferDetailsAndroid = []; } } return output; } /** * Convert NitroPurchase (from native) to generated Purchase type */ export function convertNitroPurchaseToPurchase(nitroPurchase) { const platform = normalizePlatform(nitroPurchase.platform); const purchase = { id: nitroPurchase.id, productId: nitroPurchase.productId, transactionDate: nitroPurchase.transactionDate ?? Date.now(), purchaseToken: nitroPurchase.purchaseToken ?? nitroPurchase.purchaseTokenAndroid ?? null, platform, quantity: nitroPurchase.quantity ?? 1, purchaseState: normalizePurchaseState(nitroPurchase.purchaseState ?? nitroPurchase.purchaseStateAndroid), isAutoRenewing: Boolean(nitroPurchase.isAutoRenewing) }; if (purchase.purchaseState === PURCHASE_STATE_UNKNOWN && nitroPurchase.purchaseStateAndroid != null) { purchase.purchaseState = normalizePurchaseState(nitroPurchase.purchaseStateAndroid); } if (platform === PLATFORM_IOS) { const iosPurchase = purchase; iosPurchase.quantityIOS = toNullableNumber(nitroPurchase.quantityIOS); iosPurchase.originalTransactionDateIOS = toNullableNumber(nitroPurchase.originalTransactionDateIOS); iosPurchase.originalTransactionIdentifierIOS = toNullableString(nitroPurchase.originalTransactionIdentifierIOS); iosPurchase.appAccountToken = toNullableString(nitroPurchase.appAccountToken); return iosPurchase; } const androidPurchase = purchase; androidPurchase.autoRenewingAndroid = toNullableBoolean(nitroPurchase.autoRenewingAndroid ?? nitroPurchase.isAutoRenewing); androidPurchase.dataAndroid = toNullableString(nitroPurchase.dataAndroid); androidPurchase.signatureAndroid = toNullableString(nitroPurchase.signatureAndroid); androidPurchase.isAcknowledgedAndroid = toNullableBoolean(nitroPurchase.isAcknowledgedAndroid); androidPurchase.packageNameAndroid = toNullableString(nitroPurchase.packageNameAndroid); androidPurchase.obfuscatedAccountIdAndroid = toNullableString(nitroPurchase.obfuscatedAccountIdAndroid); androidPurchase.obfuscatedProfileIdAndroid = toNullableString(nitroPurchase.obfuscatedProfileIdAndroid); return androidPurchase; } /** * Convert Nitro subscription status (iOS) to generated type */ export function convertNitroSubscriptionStatusToSubscriptionStatusIOS(nitro) { return { state: String(nitro.state ?? ''), renewalInfo: nitro.renewalInfo ? { autoRenewPreference: toNullableString(nitro.renewalInfo.autoRenewPreference), jsonRepresentation: JSON.stringify(nitro.renewalInfo), willAutoRenew: Boolean(nitro.renewalInfo.autoRenewStatus) } : undefined }; } /** * Validate that a NitroProduct has the expected shape */ export function validateNitroProduct(nitroProduct) { if (!nitroProduct || typeof nitroProduct !== 'object') { return false; } const required = ['id', 'title', 'description', 'type', 'platform']; for (const field of required) { if (!(field in nitroProduct) || nitroProduct[field] == null) { console.error(`NitroProduct missing required field: ${field}`, nitroProduct); return false; } } return true; } /** * Validate that a NitroPurchase has the expected shape */ export function validateNitroPurchase(nitroPurchase) { if (!nitroPurchase || typeof nitroPurchase !== 'object') { return false; } const required = ['id', 'productId', 'transactionDate', 'platform']; for (const field of required) { if (!(field in nitroPurchase) || nitroPurchase[field] == null) { console.error(`NitroPurchase missing required field: ${field}`, nitroPurchase); return false; } } return true; } /** * Development helper to check that type conversions stay valid */ export function checkTypeSynchronization() { const issues = []; try { const testNitroProduct = { id: 'test', title: 'Test', description: 'Test product', type: 'inapp', platform: PLATFORM_IOS, displayPrice: '$1.00', currency: 'USD', price: 1 }; const converted = convertNitroProductToProduct(testNitroProduct); if (!converted.id || !converted.title) { issues.push('Type conversion failed'); } } catch (error) { issues.push(`Type conversion error: ${String(error)}`); } return { isSync: issues.length === 0, issues }; } //# sourceMappingURL=type-bridge.js.map