diff --git a/app/(tabs)/rights.tsx b/app/(tabs)/rights.tsx index 68386b7..3472bde 100644 --- a/app/(tabs)/rights.tsx +++ b/app/(tabs)/rights.tsx @@ -20,6 +20,18 @@ export default function Rights() { const insets = useSafeAreaInsets(); const router = useRouter(); const { t } = useTranslation(); + const { + connected, + products, + subscriptions, + currentPurchase, + currentPurchaseError, + requestProducts, + requestPurchase, + finishTransaction, + validateReceipt, + } = useIAP(); + // 选择购买方式 const [payChoice, setPayChoice] = useState<'weChatPay' | 'apple'>('weChatPay'); // 获取路由参数 @@ -49,16 +61,44 @@ export default function Rights() { useEffect(() => { getPAy(); }, []); + // 处理购买 + const handlePurchase = async (productId: string) => { + console.log(productId); - function TestComponent() { - const { connected } = useIAP(); + try { + // Platform-specific purchase requests (v2.7.0+) + await requestPurchase({ + request: { + ios: { + sku: "MEMBERSHIP_PRO_QUARTERLY", + andDangerouslyFinishTransactionAutomaticallyIOS: false, + }, + }, + }); + } catch (error) { + alert(productId) + console.error('Purchase failed:', error); + } + }; + useEffect(() => { + console.log('connected', connected); - console.log('IAP Connection status:', connected); + if (!connected) return; + + const initializeStore = async () => { + try { + await requestProducts({ skus: ["MEMBERSHIP_PRO_QUARTERLY"], type: 'subs' }); + // await getSubscriptions(["MEMBERSHIP_PRO_QUARTERLY"]) + console.log("products", products); + } catch (error) { + console.error('Failed to initialize store:', error); + } + }; + + initializeStore(); + }, [connected, premiumPay]); - return null; - } - TestComponent() return ( @@ -149,8 +189,7 @@ export default function Rights() { - - + {/* */} {/* 付费按钮 */} { setUserType('premium'); + handlePurchase(payType); }} activeOpacity={0.8} > diff --git a/components/owner/rights/apple.tsx b/components/owner/rights/apple.tsx new file mode 100644 index 0000000..2da2305 --- /dev/null +++ b/components/owner/rights/apple.tsx @@ -0,0 +1,252 @@ +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 ( + + {/* Your purchase UI components */} + Connection Status: {connected ? 'Connected' : 'Disconnected'} + Products Ready: {isReady ? 'Yes' : 'No'} + {/* Add your purchase buttons and UI here */} + + ); +} \ No newline at end of file diff --git a/components/owner/rights/premium.tsx b/components/owner/rights/premium.tsx index 634f993..202e092 100644 --- a/components/owner/rights/premium.tsx +++ b/components/owner/rights/premium.tsx @@ -67,11 +67,11 @@ const Premium = (props: Props) => { {item.product_code?.split('_')[item.product_code?.split('_')?.length - 1]} - $ {item.unit_price.amount} + $ {(Number(item.unit_price.amount) - Number(item.discount_amount.amount)).toFixed(2)} - $ {item.discount_amount.amount} + $ {item.unit_price.amount}