feat: push
This commit is contained in:
parent
9c5d7a53a0
commit
1d9f80f975
@ -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 (
|
||||
<View style={{ flex: 1 }}>
|
||||
<ScrollView style={[styles.container, { paddingTop: insets.top, paddingBottom: insets.bottom + 80 }]}>
|
||||
@ -149,8 +189,7 @@ export default function Rights() {
|
||||
<View style={{ flex: 1, marginBottom: 80 }}>
|
||||
<ProRights style={{ display: userType === 'normal' ? "none" : "flex" }} />
|
||||
</View>
|
||||
|
||||
|
||||
{/* <ApplePay /> */}
|
||||
</ScrollView>
|
||||
{/* 付费按钮 */}
|
||||
<View style={{
|
||||
@ -168,6 +207,7 @@ export default function Rights() {
|
||||
style={styles.goPay}
|
||||
onPress={async () => {
|
||||
setUserType('premium');
|
||||
handlePurchase(payType);
|
||||
}}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
|
||||
252
components/owner/rights/apple.tsx
Normal file
252
components/owner/rights/apple.tsx
Normal file
@ -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 (
|
||||
<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>
|
||||
);
|
||||
}
|
||||
@ -67,11 +67,11 @@ const Premium = (props: Props) => {
|
||||
{item.product_code?.split('_')[item.product_code?.split('_')?.length - 1]}
|
||||
</ThemedText>
|
||||
<ThemedText style={[styles.titleText, { fontSize: 32, lineHeight: 32 }]}>
|
||||
$ {item.unit_price.amount}
|
||||
$ {(Number(item.unit_price.amount) - Number(item.discount_amount.amount)).toFixed(2)}
|
||||
</ThemedText>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||
<ThemedText style={[styles.titleText, { fontSize: 12, color: "#AC7E35", textDecorationLine: 'line-through' }]}>
|
||||
$ {item.discount_amount.amount}
|
||||
$ {item.unit_price.amount}
|
||||
</ThemedText>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user