|
|
@@ -1,6 +1,6 @@
|
|
|
import 'react-native-gesture-handler';
|
|
|
|
|
|
-import React, { createContext, useContext, useRef, useState, useEffect } from 'react';
|
|
|
+import React, { createContext, useContext, useRef, useState, useEffect, useCallback } from 'react';
|
|
|
import { Platform, StyleSheet, View, Alert } from 'react-native';
|
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
|
@@ -17,16 +17,13 @@ import ApplicationNavigator from '@/navigation/Application';
|
|
|
import { ThemeProvider } from '@/theme';
|
|
|
import '@/translations';
|
|
|
|
|
|
-import * as RNIap from 'react-native-iap';
|
|
|
-import type { Purchase, PurchaseError } from 'react-native-iap';
|
|
|
+
|
|
|
import { eventTypeEnum } from './utils/h5';
|
|
|
+import useSubscription from './hooks/useSub';
|
|
|
|
|
|
-const itemSubs = ['com.recipemuse.vip.monthly']; // 月度会员订阅ID
|
|
|
|
|
|
+import { useRequest, setToken } from '@/hooks';
|
|
|
|
|
|
-// 订阅相关消息常量
|
|
|
-const SUBSCRIPTION_REQUEST = 'NATIVE_SUBSCRIPTION_REQUEST';
|
|
|
-const RESTORE_PURCHASE_REQUEST = 'NATIVE_RESTORE_PURCHASE_REQUEST';
|
|
|
|
|
|
export const queryClient = new QueryClient({
|
|
|
defaultOptions: {
|
|
|
@@ -61,7 +58,7 @@ export const useWebViewContext = () => {
|
|
|
// 配置常量
|
|
|
// const DEFAULT_URL = 'http://localhost:5173';
|
|
|
|
|
|
-const DEFAULT_URL = 'http://10.15.49.43:5173';
|
|
|
+const DEFAULT_URL = 'http://10.13.51.63:5173/';
|
|
|
|
|
|
|
|
|
// WebView用户代理
|
|
|
@@ -73,16 +70,14 @@ const USER_AGENT = Platform.select({
|
|
|
function App() {
|
|
|
const webViewRef = useRef<WebView>(null);
|
|
|
const [showWebView, setShowWebView] = useState(false);
|
|
|
- const [isIAPInitialized, setIsIAPInitialized] = useState(false);
|
|
|
- const [availableProducts, setAvailableProducts] = useState<any[]>([]);
|
|
|
- const [isSubscribed, setIsSubscribed] = useState(false);
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 发送消息到 WebView
|
|
|
*/
|
|
|
- const sendMessageToWebView = (webViewRef: React.RefObject<WebView | null>, message: any) => {
|
|
|
+ const sendMessageToWebView = (message: any) => {
|
|
|
if (webViewRef.current) {
|
|
|
const messageString = typeof message === 'string' ? message : JSON.stringify(message);
|
|
|
// 使用更可靠的通信方式
|
|
|
@@ -95,7 +90,6 @@ function App() {
|
|
|
*/
|
|
|
const handleWebViewMessage = (
|
|
|
event: any,
|
|
|
- webViewRef: React.RefObject<WebView | null>,
|
|
|
) => {
|
|
|
try {
|
|
|
const messageData = event.nativeEvent.data;
|
|
|
@@ -103,25 +97,40 @@ function App() {
|
|
|
console.log('Received message:', type, data);
|
|
|
|
|
|
// 检查是否为登录请求
|
|
|
- if (type === eventTypeEnum.NATIVE_LOGIN_REQUEST) {
|
|
|
+ if (type === eventTypeEnum.login2Native) {
|
|
|
// 延迟执行登录以确保 WebView 已完全显示
|
|
|
- handleLogin()
|
|
|
- } else if (type === SUBSCRIPTION_REQUEST) {
|
|
|
- // 检查是否为订阅请求
|
|
|
- console.log('收到订阅请求');
|
|
|
- handleInAppPurchase()
|
|
|
+ handleLogin();
|
|
|
+ }
|
|
|
+ // 检查是否为订阅请求
|
|
|
+ else if (type === eventTypeEnum.SUBSCRIPTION_REQUEST) {
|
|
|
+ console.log('📱 收到订阅请求:', data);
|
|
|
+
|
|
|
+ // 从 H5 传来的数据中获取订阅类型
|
|
|
+ const { productId, subscriptionType } = data || {};
|
|
|
+
|
|
|
+ // 根据订阅类型选择产品ID
|
|
|
+ let targetProductId = productId;
|
|
|
+ if (!targetProductId && subscriptionType) {
|
|
|
+ targetProductId = subscriptionType === 'yearly'
|
|
|
+ ? SUBSCRIPTION_PRODUCT_IDS.YEARLY
|
|
|
+ : SUBSCRIPTION_PRODUCT_IDS.MONTHLY;
|
|
|
+ }
|
|
|
+
|
|
|
+ handleInAppPurchase(targetProductId)
|
|
|
.then(() => {
|
|
|
- console.log('订阅流程已启动');
|
|
|
+ console.log('✅ 订阅流程已启动');
|
|
|
})
|
|
|
.catch((error) => {
|
|
|
- console.error('启动订阅失败:', error);
|
|
|
- sendMessageToWebView(webViewRef, {
|
|
|
+ console.error('❌ 启动订阅失败:', error);
|
|
|
+ sendMessageToWebView({
|
|
|
type: 'SUBSCRIPTION_ERROR',
|
|
|
+ status: 'error',
|
|
|
+ message: error.message || '订阅失败',
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
// 检查是否为恢复购买请求
|
|
|
- else if (type === RESTORE_PURCHASE_REQUEST) {
|
|
|
+ else if (type === eventTypeEnum.RESTORE_PURCHASE_REQUEST) {
|
|
|
console.log('收到恢复购买请求');
|
|
|
restorePurchases()
|
|
|
.then(() => {
|
|
|
@@ -129,7 +138,7 @@ function App() {
|
|
|
})
|
|
|
.catch((error) => {
|
|
|
console.error('恢复购买失败:', error);
|
|
|
- sendMessageToWebView(webViewRef, {
|
|
|
+ sendMessageToWebView({
|
|
|
type: 'RESTORE_PURCHASE_ERROR',
|
|
|
status: 'error',
|
|
|
message: error.message || '恢复购买失败'
|
|
|
@@ -189,7 +198,7 @@ function App() {
|
|
|
if (credentialState === appleAuth.State.REVOKED && !__DEV__) {
|
|
|
console.warn('⚠️ 凭证状态为REVOKED,但登录已完成');
|
|
|
}
|
|
|
- sendMessageToWebView(webViewRef, {
|
|
|
+ sendMessageToWebView({
|
|
|
type: eventTypeEnum.H5_LOGIN_SUCCESS_IOS,
|
|
|
message: '登录成功',
|
|
|
data: appleAuthRequestResponse
|
|
|
@@ -223,7 +232,7 @@ function App() {
|
|
|
},
|
|
|
};
|
|
|
Alert.alert('开发模式', '当前使用Mock登录数据(Google服务不可用)');
|
|
|
- sendMessageToWebView(webViewRef, {
|
|
|
+ sendMessageToWebView({
|
|
|
type: eventTypeEnum.H5_LOGIN_SUCCESS_ANDROID,
|
|
|
status: 'success',
|
|
|
message: '登录成功(Mock模式)',
|
|
|
@@ -242,7 +251,7 @@ function App() {
|
|
|
const userInfo = await GoogleSignin.signIn();
|
|
|
console.log('Google登录成功:', userInfo);
|
|
|
Alert.alert('登录成功', `欢迎 ${userInfo.data?.user.name || '用户'}`);
|
|
|
- sendMessageToWebView(webViewRef, {
|
|
|
+ sendMessageToWebView({
|
|
|
type: 'LOGIN_SUCCESS_ANDROID',
|
|
|
status: 'success',
|
|
|
message: '登录成功',
|
|
|
@@ -254,363 +263,98 @@ function App() {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- /**
|
|
|
- * 初始化IAP
|
|
|
- */
|
|
|
- // useEffect(() => {
|
|
|
- // // 检查Google Play Services后再初始化IAP
|
|
|
- // checkAndInitIAP();
|
|
|
- // return () => {
|
|
|
- // // 清理IAP连接
|
|
|
- // if (isIAPInitialized) {
|
|
|
- // RNIap.endConnection();
|
|
|
- // }
|
|
|
- // };
|
|
|
- // }, []);
|
|
|
-
|
|
|
- /**
|
|
|
- * 检查Google服务并初始化IAP
|
|
|
- */
|
|
|
- const checkAndInitIAP = async () => {
|
|
|
- try {
|
|
|
- // Android平台检查Google Play Services
|
|
|
- Alert.alert('Platform.OS ', Platform.OS);
|
|
|
- if (Platform.OS === 'android') {
|
|
|
- const hasPlayServices = await GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: false });
|
|
|
- if (!hasPlayServices) {
|
|
|
- console.log('⚠️ Google Play Services不可用,IAP功能将被禁用');
|
|
|
- console.log('💡 建议:使用Android模拟器或配置系统代理进行测试');
|
|
|
- Alert.alert(
|
|
|
- '提示',
|
|
|
- 'Google Play服务不可用\n\n建议使用以下方式测试:\n1. Android Studio模拟器(带Google Play)\n2. 真机配置系统代理\n3. 使用Mock模式开发',
|
|
|
- [{ text: '知道了' }]
|
|
|
- );
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 有Google服务才初始化IAP
|
|
|
- await initIAP();
|
|
|
- } catch (error) {
|
|
|
- console.log('Google服务检查失败,跳过IAP初始化:', error);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- /**
|
|
|
- * 初始化应用内购买
|
|
|
- */
|
|
|
- const initIAP = async () => {
|
|
|
- try {
|
|
|
- console.log('🚀 开始初始化IAP...');
|
|
|
- console.log('📱 平台:', Platform.OS);
|
|
|
-
|
|
|
- await RNIap.initConnection();
|
|
|
- console.log('✅ IAP连接初始化成功');
|
|
|
- setIsIAPInitialized(true);
|
|
|
-
|
|
|
- // 获取可用的订阅产品
|
|
|
- const products = await RNIap.fetchProducts({ skus: itemSubs, type: 'subs' });
|
|
|
- console.log('可用订阅产品:', products);
|
|
|
- setAvailableProducts(products || []);
|
|
|
-
|
|
|
- // 检查现有订阅状态
|
|
|
- await checkSubscriptionStatus();
|
|
|
-
|
|
|
- // 监听购买更新
|
|
|
- const purchaseUpdateSubscription = RNIap.purchaseUpdatedListener(
|
|
|
- async (purchase: Purchase) => {
|
|
|
- console.log('购买更新:', purchase);
|
|
|
- console.log('购买状态:', purchase.purchaseState);
|
|
|
- console.log('平台:', purchase.platform);
|
|
|
-
|
|
|
- const receipt = purchase.purchaseToken || purchase.transactionId;
|
|
|
-
|
|
|
- if (receipt) {
|
|
|
- try {
|
|
|
- // 验证收据(这里应该发送到你的后端服务器验证)
|
|
|
- const isValid = await validateReceipt(receipt, purchase);
|
|
|
-
|
|
|
- if (!isValid) {
|
|
|
- Alert.alert('订阅失败', '收据验证失败,请联系客服');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 完成交易 - iOS和Android处理方式不同
|
|
|
- if (Platform.OS === 'ios') {
|
|
|
- // iOS: 完成交易,订阅类产品不消耗
|
|
|
- await RNIap.finishTransaction({ purchase, isConsumable: false });
|
|
|
- console.log('iOS交易已完成');
|
|
|
- } else if (Platform.OS === 'android') {
|
|
|
- // Android: 需要确认购买(acknowledge)
|
|
|
- if (purchase.purchaseToken) {
|
|
|
- await RNIap.acknowledgePurchaseAndroid(purchase.purchaseToken);
|
|
|
- console.log('Android购买已确认');
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 更新订阅状态
|
|
|
- setIsSubscribed(true);
|
|
|
-
|
|
|
- Alert.alert('订阅成功', '感谢您订阅会员服务!');
|
|
|
-
|
|
|
- // 发送订阅成功消息到WebView
|
|
|
- sendMessageToWebView(webViewRef, {
|
|
|
- type: 'SUBSCRIPTION_SUCCESS',
|
|
|
- status: 'success',
|
|
|
- message: '订阅成功',
|
|
|
- data: {
|
|
|
- productId: purchase.productId,
|
|
|
- transactionId: purchase.transactionId,
|
|
|
- purchaseToken: purchase.purchaseToken,
|
|
|
- platform: purchase.platform,
|
|
|
- transactionDate: purchase.transactionDate,
|
|
|
- }
|
|
|
- });
|
|
|
- } catch (error: any) {
|
|
|
- console.error('验证收据失败:', error);
|
|
|
- Alert.alert('订阅失败', `验证收据时出错: ${error.message || '未知错误'}`);
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
- );
|
|
|
-
|
|
|
- // 监听购买错误
|
|
|
- const purchaseErrorSubscription = RNIap.purchaseErrorListener(
|
|
|
- (error: PurchaseError) => {
|
|
|
- console.error('购买错误:', error);
|
|
|
- if (error.code !== RNIap.ErrorCode.UserCancelled) {
|
|
|
- Alert.alert('购买失败', error.message || '请稍后重试');
|
|
|
- }
|
|
|
- },
|
|
|
- );
|
|
|
|
|
|
- // 保存订阅以便清理
|
|
|
- return () => {
|
|
|
- purchaseUpdateSubscription.remove();
|
|
|
- purchaseErrorSubscription.remove();
|
|
|
- };
|
|
|
- } catch (error: any) {
|
|
|
- debugger
|
|
|
- console.error('❌ IAP初始化失败 - 详细信息:');
|
|
|
- console.error('错误对象:', error);
|
|
|
- console.error('错误消息:', error.message);
|
|
|
- console.error('错误代码:', error.code);
|
|
|
- console.error('错误栈:', error.stack);
|
|
|
-
|
|
|
- let errorMessage = '无法连接到应用商店';
|
|
|
-
|
|
|
- if (error.message) {
|
|
|
- if (error.message.includes('BILLING_UNAVAILABLE')) {
|
|
|
- errorMessage = 'Google Play Billing服务不可用\n\n可能原因:\n1. 模拟器未安装Google Play\n2. 未登录Google账号\n3. 国内网络限制';
|
|
|
- } else if (error.message.includes('SERVICE_DISCONNECTED')) {
|
|
|
- errorMessage = 'Google Play服务连接断开\n请检查网络和代理配置';
|
|
|
- } else if (error.message.includes('SERVICE_UNAVAILABLE')) {
|
|
|
- errorMessage = 'Google Play服务暂时不可用\n请稍后重试';
|
|
|
- } else {
|
|
|
- errorMessage = `连接失败: ${error.message}`;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- Alert.alert(
|
|
|
- 'IAP初始化失败',
|
|
|
- errorMessage + '\n\n技术信息: ' + (error.code || '未知错误'),
|
|
|
- [{ text: '知道了' }]
|
|
|
- );
|
|
|
- }
|
|
|
+ // 定义订阅产品ID(根据你的App Store/Google Play配置修改)
|
|
|
+ const SUBSCRIPTION_PRODUCT_IDS = {
|
|
|
+ MONTHLY: Platform.select({
|
|
|
+ ios: 'com.recipemuse.premium.monthly',
|
|
|
+ android: 'com.recipemuse.premium.monthly',
|
|
|
+ }) || 'com.recipemuse.premium.monthly',
|
|
|
+ YEARLY: Platform.select({
|
|
|
+ ios: 'com.recipemuse.premium.yearly',
|
|
|
+ android: 'com.recipemuse.premium.yearly',
|
|
|
+ }) || 'com.recipemuse.premium.yearly',
|
|
|
};
|
|
|
|
|
|
- /**
|
|
|
- * 验证收据(示例实现,实际应该发送到后端服务器)
|
|
|
- */
|
|
|
- const validateReceipt = async (
|
|
|
- receipt: string,
|
|
|
- purchase: Purchase,
|
|
|
- ) => {
|
|
|
- // TODO: 将收据发送到你的后端服务器进行验证
|
|
|
- // 示例代码:
|
|
|
- // const response = await fetch('https://your-api.com/validate-receipt', {
|
|
|
- // method: 'POST',
|
|
|
- // headers: { 'Content-Type': 'application/json' },
|
|
|
- // body: JSON.stringify({ receipt, productId: purchase.productId }),
|
|
|
- // });
|
|
|
- // const result = await response.json();
|
|
|
- // return result.valid;
|
|
|
-
|
|
|
- console.log('验证收据:', receipt);
|
|
|
- console.log('购买信息:', purchase);
|
|
|
- return true;
|
|
|
- };
|
|
|
-
|
|
|
- /**
|
|
|
- * 处理应用内购买 - 购买月度会员订阅
|
|
|
- */
|
|
|
- const handleInAppPurchase = async () => {
|
|
|
- if (!isIAPInitialized) {
|
|
|
- Alert.alert('提示', '应用商店连接未就绪,请稍后重试');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 检查是否已订阅
|
|
|
- if (isSubscribed) {
|
|
|
- Alert.alert('提示', '您已经是会员了');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- // 构建购买请求参数
|
|
|
- const purchaseRequest: any = {
|
|
|
- type: 'subs',
|
|
|
- request: {
|
|
|
- ios: {
|
|
|
- sku: itemSubs[0],
|
|
|
- },
|
|
|
- android: {
|
|
|
- skus: [itemSubs[0]],
|
|
|
- },
|
|
|
- },
|
|
|
- };
|
|
|
-
|
|
|
- // Android需要提供subscriptionOffers(从产品信息中获取)
|
|
|
- if (Platform.OS === 'android' && availableProducts.length > 0) {
|
|
|
- const product = availableProducts.find(p => p.id === itemSubs[0]);
|
|
|
- if (product?.subscriptionOfferDetailsAndroid?.[0]) {
|
|
|
- const offer = product.subscriptionOfferDetailsAndroid[0];
|
|
|
- purchaseRequest.request.android.subscriptionOffers = [
|
|
|
- {
|
|
|
- sku: itemSubs[0],
|
|
|
- offerToken: offer.offerToken,
|
|
|
- },
|
|
|
- ];
|
|
|
- console.log('Android订阅offer:', offer);
|
|
|
- } else {
|
|
|
- console.warn('未找到Android订阅offer,将尝试不带offer购买');
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- console.log('发起购买请求:', purchaseRequest);
|
|
|
-
|
|
|
- // 请求购买订阅
|
|
|
- await RNIap.requestPurchase(purchaseRequest);
|
|
|
- } catch (error: any) {
|
|
|
- console.error('购买订阅失败:', error);
|
|
|
-
|
|
|
- // 区分不同的错误类型
|
|
|
- if (error.code === RNIap.ErrorCode.UserCancelled) {
|
|
|
- console.log('用户取消购买');
|
|
|
- } else if (error.code === RNIap.ErrorCode.AlreadyOwned) {
|
|
|
- Alert.alert('提示', '您已经拥有此订阅');
|
|
|
- setIsSubscribed(true);
|
|
|
- } else if (error.code === RNIap.ErrorCode.ItemUnavailable) {
|
|
|
- Alert.alert('购买失败', '此商品暂时不可用,请稍后重试');
|
|
|
- } else {
|
|
|
- Alert.alert('购买失败', error.message || '请稍后重试');
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
+ // 使用订阅 hook
|
|
|
+ const {
|
|
|
+ connected,
|
|
|
+ products,
|
|
|
+ loading,
|
|
|
+ error,
|
|
|
+ purchasing,
|
|
|
+ handlePurchase,
|
|
|
+ restorePurchases,
|
|
|
+ getProduct,
|
|
|
+ clearError,
|
|
|
+ } = useSubscription({
|
|
|
+ productIds: [SUBSCRIPTION_PRODUCT_IDS.MONTHLY, SUBSCRIPTION_PRODUCT_IDS.YEARLY],
|
|
|
+ productType: 'subs',
|
|
|
+ sendMessageToWebView,
|
|
|
+ onPurchaseComplete: (purchase) => {
|
|
|
+ console.log('✅ 订阅购买成功:', purchase);
|
|
|
+ // 可以在这里添加额外的成功处理逻辑
|
|
|
+ // 例如:更新用户状态、记录分析事件等
|
|
|
+ },
|
|
|
+ onPurchaseFailed: (error) => {
|
|
|
+ console.error('❌ 订阅购买失败:', error);
|
|
|
+ // 可以在这里添加额外的失败处理逻辑
|
|
|
+ // 例如:记录错误日志、发送错误报告等
|
|
|
+ },
|
|
|
+ });
|
|
|
|
|
|
- /**
|
|
|
- * 恢复购买
|
|
|
- */
|
|
|
- const restorePurchases = async () => {
|
|
|
+ // 处理订阅请求
|
|
|
+ const handleInAppPurchase = useCallback(async (productId?: string) => {
|
|
|
try {
|
|
|
- // iOS调用系统恢复购买API
|
|
|
- if (Platform.OS === 'ios') {
|
|
|
- await RNIap.restorePurchases();
|
|
|
- console.log('iOS恢复购买请求已发送');
|
|
|
- }
|
|
|
+ // 如果没有传入 productId,默认使用月度订阅
|
|
|
+ const targetProductId = productId || SUBSCRIPTION_PRODUCT_IDS.MONTHLY;
|
|
|
|
|
|
- // 获取可用购买记录
|
|
|
- const availablePurchases = await RNIap.getAvailablePurchases();
|
|
|
- console.log('可恢复的购买:', availablePurchases);
|
|
|
-
|
|
|
- if (availablePurchases.length === 0) {
|
|
|
- Alert.alert('提示', '没有找到可恢复的购买记录');
|
|
|
+ console.log('🛒 开始订阅流程:', targetProductId);
|
|
|
+ console.log('📦 可用产品:', products);
|
|
|
+ console.log('🔌 IAP连接状态:', connected);
|
|
|
+
|
|
|
+ // 检查连接状态
|
|
|
+ if (!connected) {
|
|
|
+ const errorMsg = '应用商店未连接,请稍后重试';
|
|
|
+ Alert.alert('错误', errorMsg);
|
|
|
+ sendMessageToWebView({
|
|
|
+ type: 'SUBSCRIPTION_ERROR',
|
|
|
+ status: 'error',
|
|
|
+ message: errorMsg,
|
|
|
+ });
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- let hasSubscription = false;
|
|
|
-
|
|
|
- // 处理恢复的购买
|
|
|
- for (const purchase of availablePurchases) {
|
|
|
- if (itemSubs.includes(purchase.productId)) {
|
|
|
- hasSubscription = true;
|
|
|
-
|
|
|
- // 验证并激活订阅
|
|
|
- const receipt = purchase.purchaseToken || purchase.transactionId || '';
|
|
|
- const isValid = await validateReceipt(receipt, purchase);
|
|
|
-
|
|
|
- if (isValid) {
|
|
|
- // Android需要确认购买
|
|
|
- if (Platform.OS === 'android' && purchase.purchaseToken) {
|
|
|
- try {
|
|
|
- await RNIap.acknowledgePurchaseAndroid(purchase.purchaseToken);
|
|
|
- console.log('Android购买已确认');
|
|
|
- } catch (error) {
|
|
|
- console.log('购买可能已经被确认过:', error);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- setIsSubscribed(true);
|
|
|
- console.log('订阅已恢复:', purchase.productId);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (hasSubscription) {
|
|
|
- Alert.alert('恢复成功', '您的订阅已恢复');
|
|
|
-
|
|
|
- // 发送恢复成功消息到WebView
|
|
|
- sendMessageToWebView(webViewRef, {
|
|
|
- type: 'SUBSCRIPTION_RESTORED',
|
|
|
- status: 'success',
|
|
|
- message: '订阅已恢复',
|
|
|
+ // 检查产品是否已加载
|
|
|
+ const product = getProduct(targetProductId);
|
|
|
+ if (!product) {
|
|
|
+ const errorMsg = '订阅产品不可用,请稍后重试';
|
|
|
+ console.warn('⚠️ 产品未找到:', targetProductId);
|
|
|
+ Alert.alert('错误', errorMsg);
|
|
|
+ sendMessageToWebView({
|
|
|
+ type: 'SUBSCRIPTION_ERROR',
|
|
|
+ status: 'error',
|
|
|
+ message: errorMsg,
|
|
|
});
|
|
|
- } else {
|
|
|
- Alert.alert('提示', '没有找到会员订阅记录');
|
|
|
+ return;
|
|
|
}
|
|
|
- } catch (error: any) {
|
|
|
- console.error('恢复购买失败:', error);
|
|
|
- Alert.alert('恢复失败', error.message || '无法恢复购买记录');
|
|
|
- }
|
|
|
- };
|
|
|
|
|
|
- /**
|
|
|
- * 检查订阅状态
|
|
|
- */
|
|
|
- const checkSubscriptionStatus = async () => {
|
|
|
- try {
|
|
|
- const availablePurchases = await RNIap.getAvailablePurchases();
|
|
|
- console.log('所有可用购买记录:', availablePurchases);
|
|
|
+ console.log('💰 产品信息:', product);
|
|
|
|
|
|
- const activeSubscription = availablePurchases.find(
|
|
|
- (purchase) => itemSubs.includes(purchase.productId),
|
|
|
- );
|
|
|
-
|
|
|
- if (activeSubscription) {
|
|
|
- console.log('当前订阅状态:', activeSubscription);
|
|
|
-
|
|
|
- // 检查订阅是否过期(Android)
|
|
|
- if (Platform.OS === 'android') {
|
|
|
- // Android的自动续订状态
|
|
|
- const androidPurchase = activeSubscription as any;
|
|
|
- const isActive = androidPurchase.autoRenewingAndroid !== false;
|
|
|
- setIsSubscribed(isActive);
|
|
|
- return isActive;
|
|
|
- } else {
|
|
|
- // iOS的订阅状态
|
|
|
- setIsSubscribed(true);
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- setIsSubscribed(false);
|
|
|
- return false;
|
|
|
+ // 发起购买
|
|
|
+ await handlePurchase(targetProductId);
|
|
|
} catch (error) {
|
|
|
- console.error('检查订阅状态失败:', error);
|
|
|
- setIsSubscribed(false);
|
|
|
- return false;
|
|
|
+ console.error('订阅流程错误:', error);
|
|
|
+ const errorMessage = error instanceof Error ? error.message : '订阅失败';
|
|
|
+ sendMessageToWebView({
|
|
|
+ type: 'SUBSCRIPTION_ERROR',
|
|
|
+ status: 'error',
|
|
|
+ message: errorMessage,
|
|
|
+ });
|
|
|
+ throw error;
|
|
|
}
|
|
|
- };
|
|
|
+ }, [connected, products, handlePurchase, getProduct, sendMessageToWebView, SUBSCRIPTION_PRODUCT_IDS]);
|
|
|
+
|
|
|
|
|
|
return (
|
|
|
<GestureHandlerRootView style={styles.container}>
|
|
|
@@ -636,7 +380,7 @@ function App() {
|
|
|
startInLoadingState={true}
|
|
|
scalesPageToFit={true}
|
|
|
allowsBackForwardNavigationGestures={Platform.OS === 'ios'}
|
|
|
- onMessage={(event) => handleWebViewMessage(event, webViewRef)}
|
|
|
+ onMessage={(event) => handleWebViewMessage(event)}
|
|
|
onError={(error) => console.error('WebView 错误:', error)}
|
|
|
onHttpError={(error) => console.error('WebView HTTP 错误:', error)}
|
|
|
/>
|