252 lines
8.5 KiB
TypeScript
252 lines
8.5 KiB
TypeScript
import { ThemedText } from '@/components/ThemedText';
|
|
import { useIAP } from 'expo-iap';
|
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
import { Alert, InteractionManager, Platform, View } from 'react-native';
|
|
|
|
// Define your product SKUs
|
|
const bulbPackSkus = ['dev.hyo.martie.10bulbs', 'dev.hyo.martie.30bulbs'];
|
|
const subscriptionSkus = ['dev.hyo.martie.premium'];
|
|
|
|
export default function PurchaseScreen() {
|
|
const {
|
|
connected,
|
|
products,
|
|
subscriptions,
|
|
currentPurchase,
|
|
currentPurchaseError,
|
|
requestProducts,
|
|
requestPurchase,
|
|
finishTransaction,
|
|
validateReceipt,
|
|
} = useIAP();
|
|
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [isReady, setIsReady] = useState(false);
|
|
|
|
// Initialize products when IAP connection is established
|
|
useEffect(() => {
|
|
if (!connected) return;
|
|
|
|
const initializeIAP = async () => {
|
|
try {
|
|
// Get both products and subscriptions
|
|
await requestProducts({ skus: bulbPackSkus, type: 'inapp' });
|
|
await requestProducts({ skus: subscriptionSkus, type: 'subs' });
|
|
setIsReady(true);
|
|
} catch (error) {
|
|
console.error('Error initializing IAP:', error);
|
|
}
|
|
};
|
|
|
|
initializeIAP();
|
|
}, [connected, requestProducts]);
|
|
|
|
// Validate receipt helper
|
|
const handleValidateReceipt = useCallback(
|
|
async (sku: string, purchase: any) => {
|
|
try {
|
|
if (Platform.OS === 'ios') {
|
|
return await validateReceipt(sku);
|
|
} else if (Platform.OS === 'android') {
|
|
const purchaseToken = purchase.purchaseTokenAndroid;
|
|
const packageName =
|
|
purchase.packageNameAndroid || 'your.package.name';
|
|
const isSub = subscriptionSkus.includes(sku);
|
|
|
|
return await validateReceipt(sku, {
|
|
packageName,
|
|
productToken: purchaseToken,
|
|
isSub,
|
|
});
|
|
}
|
|
return { isValid: true }; // Default for unsupported platforms
|
|
} catch (error) {
|
|
console.error('Receipt validation failed:', error);
|
|
return { isValid: false };
|
|
}
|
|
},
|
|
[validateReceipt],
|
|
);
|
|
|
|
// Handle successful purchases
|
|
useEffect(() => {
|
|
if (currentPurchase) {
|
|
handlePurchaseUpdate(currentPurchase);
|
|
}
|
|
}, [currentPurchase]);
|
|
|
|
// Handle purchase errors
|
|
useEffect(() => {
|
|
if (currentPurchaseError) {
|
|
setIsLoading(false);
|
|
|
|
// Don't show error for user cancellation
|
|
if (currentPurchaseError.code === 'E_USER_CANCELLED') {
|
|
return;
|
|
}
|
|
|
|
Alert.alert(
|
|
'Purchase Error',
|
|
'Failed to complete purchase. Please try again.',
|
|
);
|
|
console.error('Purchase error:', currentPurchaseError);
|
|
}
|
|
}, [currentPurchaseError]);
|
|
|
|
const handlePurchaseUpdate = async (purchase: any) => {
|
|
try {
|
|
setIsLoading(true);
|
|
console.log('Processing purchase:', purchase);
|
|
|
|
const productId = purchase.id;
|
|
|
|
// Validate receipt on your server
|
|
const validationResult = await handleValidateReceipt(productId, purchase);
|
|
|
|
if (validationResult.isValid) {
|
|
// Determine if this is a consumable product
|
|
const isConsumable = bulbPackSkus.includes(productId);
|
|
|
|
// Finish the transaction
|
|
await finishTransaction({
|
|
purchase,
|
|
isConsumable, // Set to true for consumable products
|
|
});
|
|
|
|
// Record purchase in your database
|
|
await recordPurchaseInDatabase(purchase, productId);
|
|
|
|
// Update local state (e.g., add bulbs, enable premium features)
|
|
await updateLocalState(productId);
|
|
|
|
// Show success message
|
|
showSuccessMessage(productId);
|
|
} else {
|
|
Alert.alert(
|
|
'Validation Error',
|
|
'Purchase could not be validated. Please contact support.',
|
|
);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error handling purchase:', error);
|
|
Alert.alert('Error', 'Failed to process purchase.');
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
// Request purchase for products
|
|
const handlePurchaseBulbs = async (productId: string) => {
|
|
if (!connected) {
|
|
Alert.alert(
|
|
'Not Connected',
|
|
'Store connection unavailable. Please try again later.',
|
|
);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setIsLoading(true);
|
|
|
|
// Platform-specific purchase request (v2.7.0+)
|
|
await requestPurchase({
|
|
request: {
|
|
ios: {
|
|
sku: productId,
|
|
andDangerouslyFinishTransactionAutomaticallyIOS: false,
|
|
},
|
|
android: {
|
|
skus: [productId],
|
|
},
|
|
},
|
|
});
|
|
} catch (error) {
|
|
setIsLoading(false);
|
|
console.error('Purchase request failed:', error);
|
|
}
|
|
};
|
|
|
|
// Request purchase for subscriptions
|
|
const handlePurchaseSubscription = async (subscriptionId: string) => {
|
|
if (!connected) {
|
|
Alert.alert(
|
|
'Not Connected',
|
|
'Store connection unavailable. Please try again later.',
|
|
);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setIsLoading(true);
|
|
|
|
// Find subscription to get offer details
|
|
const subscription = subscriptions.find((s) => s.id === subscriptionId);
|
|
const subscriptionOffers = subscription?.subscriptionOfferDetails?.map(
|
|
(offer) => ({
|
|
sku: subscriptionId,
|
|
offerToken: offer.offerToken,
|
|
}),
|
|
) || [{ sku: subscriptionId, offerToken: '' }];
|
|
|
|
// Platform-specific subscription request (v2.7.0+)
|
|
await requestPurchase({
|
|
request: {
|
|
ios: {
|
|
sku: subscriptionId,
|
|
},
|
|
android: {
|
|
skus: [subscriptionId],
|
|
subscriptionOffers,
|
|
},
|
|
},
|
|
type: 'subs',
|
|
});
|
|
} catch (error) {
|
|
setIsLoading(false);
|
|
console.error('Subscription request failed:', error);
|
|
}
|
|
};
|
|
|
|
const recordPurchaseInDatabase = async (purchase: any, productId: string) => {
|
|
// Implement your database recording logic here
|
|
console.log('Recording purchase in database:', { purchase, productId });
|
|
};
|
|
|
|
const updateLocalState = async (productId: string) => {
|
|
// Update your local app state based on the purchase
|
|
if (bulbPackSkus.includes(productId)) {
|
|
// Add bulbs to user's account
|
|
const bulbCount = productId.includes('10bulbs') ? 10 : 30;
|
|
console.log(`Adding ${bulbCount} bulbs to user account`);
|
|
} else if (subscriptionSkus.includes(productId)) {
|
|
// Enable premium features
|
|
console.log('Enabling premium features');
|
|
}
|
|
};
|
|
|
|
const showSuccessMessage = (productId: string) => {
|
|
InteractionManager.runAfterInteractions(() => {
|
|
if (bulbPackSkus.includes(productId)) {
|
|
const bulbCount = productId.includes('10bulbs') ? 10 : 30;
|
|
Alert.alert(
|
|
'Thank You!',
|
|
`${bulbCount} bulbs have been added to your account.`,
|
|
);
|
|
} else if (subscriptionSkus.includes(productId)) {
|
|
Alert.alert(
|
|
'Thank You!',
|
|
'Premium subscription activated successfully.',
|
|
);
|
|
}
|
|
});
|
|
};
|
|
|
|
return (
|
|
<View>
|
|
{/* Your purchase UI components */}
|
|
<ThemedText>Connection Status: {connected ? 'Connected' : 'Disconnected'}</ThemedText>
|
|
<ThemedText>Products Ready: {isReady ? 'Yes' : 'No'}</ThemedText>
|
|
{/* Add your purchase buttons and UI here */}
|
|
</View>
|
|
);
|
|
} |