Selaa lähdekoodia

feat: apple登录联调

victor.zhou 4 kuukautta sitten
vanhempi
commit
d459794249
3 muutettua tiedostoa jossa 165 lisäystä ja 71 poistoa
  1. 156 68
      src/App.tsx
  2. 3 3
      src/config/auth.ts
  3. 6 0
      src/utils/h5.ts

+ 156 - 68
src/App.tsx

@@ -19,14 +19,14 @@ import '@/translations';
 
 import * as RNIap from 'react-native-iap';
 import type { Purchase, PurchaseError } from 'react-native-iap';
+import { eventTypeEnum } from './utils/h5';
 
 const itemSubs = ['com.recipemuse.vip.monthly']; // 月度会员订阅ID
 
 
-// 在 DEFAULT_URL 常量下方添加以下常量
-const MESSAGE_PREFIX = 'NATIVE_LOGIN_';
-const LOGIN_SUCCESS = `${MESSAGE_PREFIX}SUCCESS`;
-const LOGIN_REQUEST = `${MESSAGE_PREFIX}REQUEST`;
+// 订阅相关消息常量
+const SUBSCRIPTION_REQUEST = 'NATIVE_SUBSCRIPTION_REQUEST';
+const RESTORE_PURCHASE_REQUEST = 'NATIVE_RESTORE_PURCHASE_REQUEST';
 
 export const queryClient = new QueryClient({
   defaultOptions: {
@@ -61,7 +61,8 @@ export const useWebViewContext = () => {
 // 配置常量
 // const DEFAULT_URL = 'http://localhost:5173';
 
-const DEFAULT_URL = 'http://10.13.51.57:5173';
+const DEFAULT_URL = 'http://10.15.49.43:5173';
+
 
 // WebView用户代理
 const USER_AGENT = Platform.select({
@@ -97,27 +98,43 @@ function App() {
     webViewRef: React.RefObject<WebView | null>,
   ) => {
     try {
-      const data = event.nativeEvent.data;
+      const messageData = event.nativeEvent.data;
+      const { type, data } = messageData ? JSON.parse(messageData) : {};
+      console.log('Received message:', type, data);
+      
       // 检查是否为登录请求
-      if (data === LOGIN_REQUEST) {
+      if (type === eventTypeEnum.NATIVE_LOGIN_REQUEST) {
         // 延迟执行登录以确保 WebView 已完全显示
         handleLogin()
-            .then(() => {
-              // 登录成功后发送成功消息到 WebView
-              sendMessageToWebView(webViewRef, {
-                type: LOGIN_SUCCESS,
-                status: 'success',
-                message: '登录成功'
-              });
-            })
-            .catch((error) => {
-              // 登录失败时发送失败消息到 WebView
-              sendMessageToWebView(webViewRef, {
-                type: `${MESSAGE_PREFIX}ERROR`,
-                status: 'error',
-                message: error.message || '登录失败'
-              });
-            })
+      } else if (type === SUBSCRIPTION_REQUEST) {
+         // 检查是否为订阅请求
+        console.log('收到订阅请求');
+        handleInAppPurchase()
+          .then(() => {
+            console.log('订阅流程已启动');
+          })
+          .catch((error) => {
+            console.error('启动订阅失败:', error);
+            sendMessageToWebView(webViewRef, {
+              type: 'SUBSCRIPTION_ERROR',
+            });
+          });
+      }
+      // 检查是否为恢复购买请求
+      else if (type === RESTORE_PURCHASE_REQUEST) {
+        console.log('收到恢复购买请求');
+        restorePurchases()
+          .then(() => {
+            console.log('恢复购买流程已完成');
+          })
+          .catch((error) => {
+            console.error('恢复购买失败:', error);
+            sendMessageToWebView(webViewRef, {
+              type: 'RESTORE_PURCHASE_ERROR',
+              status: 'error',
+              message: error.message || '恢复购买失败'
+            });
+          });
       }
     } catch (error) {
       console.error('处理 WebView 消息时出错:', error);
@@ -130,16 +147,14 @@ function App() {
       console.log('设备ID:', deviceId);
 
 
-      let reqData = null
       if (Platform.OS === 'ios') {
         // iOS使用Apple登录
-        reqData = await handleAppleLogin();
+        await handleAppleLogin();
       } else {
         // Android使用Google登录
-        reqData = await handleGoogleLogin();
+        await handleGoogleLogin();
       }
       
-      console.log('reqData 请求参数',reqData)
     } catch (error) {
       console.error('登录失败:', error);
       Alert.alert('登录失败', '请稍后重试');
@@ -159,59 +174,74 @@ function App() {
       const credentialState = await appleAuth.getCredentialStateForUser(
         appleAuthRequestResponse.user,
       );
-
-      if (credentialState === appleAuth.State.AUTHORIZED) {
+      
+      console.log('凭证状态:', credentialState, '(0=REVOKED, 1=AUTHORIZED, 2=NOT_FOUND)');
+
+      // 如果performRequest成功返回,说明登录已完成
+      // 模拟器中credentialState可能为0(REVOKED),但不影响登录流程
+      console.log('appleAuthRequestResponse.user',appleAuthRequestResponse.user)
+      debugger
+      if (appleAuthRequestResponse.user) {
+        debugger
         console.log('Apple登录成功:', appleAuthRequestResponse);
-        // Alert.alert('登录成功', `欢迎 ${appleAuthRequestResponse.fullName?.givenName || '用户'}`);
+        
+        // 仅在真机且状态为REVOKED时警告
+        if (credentialState === appleAuth.State.REVOKED && !__DEV__) {
+          console.warn('⚠️ 凭证状态为REVOKED,但登录已完成');
+        }
         sendMessageToWebView(webViewRef, {
-          type: 'LOGIN_SUCCESS_IOS',
-          status: 'success',
+          type: eventTypeEnum.H5_LOGIN_SUCCESS_IOS,
           message: '登录成功',
           data: appleAuthRequestResponse
         })
-        // return appleAuthRequestResponse
-        // appleAuthRequestResponse : { 
-        //   user: '001746.b066d194b82f46eaac8f3f26b9a915fc.0901',
-        //   realUserStatus: 2,
-        //   authorizedScopes: [],
-        //   identityToken: 'eyJraWQiOiJIdlZJNkVzWlhKIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmlwZWFraW5nLnJlY2lwZW11c2UiLCJleHAiOjE3NjIzMzMyNzcsImlhdCI6MTc2MjI0Njg3Nywic3ViIjoiMDAxNzQ2LmIwNjZkMTk0YjgyZjQ2ZWFhYzhmM2YyNmI5YTkxNWZjLjA5MDEiLCJub25jZSI6IjYyYTliNmQzMWI4M2EyMjdhYzA3MjI2ZDZiMmQxYTc1MTRhZTRiZjZjMjhlMjJjMmU4OTkwNTkzYTVkYzE5N2MiLCJjX2hhc2giOiIyZ0hUN1M1NHBWUExfeW52aHVrRGF3IiwiZW1haWwiOiIyNzYyMDM4NzZAcXEuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF1dGhfdGltZSI6MTc2MjI0Njg3Nywibm9uY2Vfc3VwcG9ydGVkIjp0cnVlLCJyZWFsX3VzZXJfc3RhdHVzIjoyfQ.Tt2X4fBYYY5xMKSDZW8DoLsmDFzVw4AaTkMVdvruKgEOlYnBmW5MJAgCIz8iqMSFwq1008YOcpcdnAAd1J43J9HdDV-Rpg_ryEP76ph0MiynbWPPPMexz3ndxe4_ZS3s7BZdweWJuwJ9x6SqoFl01tEqs8jGEaJ13Z3byYZgsbzQxtqLzof4W1RcjLGrgj_xagjqimbex3PAgD7bIh2SUBJbhNjIK_RcNPNLQTQ4EGFwNmfG22sFUFqY5W6ipyZvk6gXGegXgnTYZpeAS1bc8UL4KBz4T4zRQMkN3uJezPD6A_IfJKSkHmwsXgPYfLNhScuO2wSBc4Q-UvuS_Y3nhg',
-        //   authorizationCode: 'c9da4feae2b0248bea11861ca60088eb1.0.rrxuw.hLfeCQRQVY12lVyUyHIbtw',
-        //   fullName:  { 
-        //      namePrefix: null,
-        //      givenName: '凯',
-        //      nameSuffix: null,
-        //      middleName: null,
-        //      familyName: '周',
-        //      nickname: null 
-        //    },
-        //   email: '276203876@qq.com',
-        //   state: null,
-        //   nonce: '202K2RJL4_8SJmIS9AThnL6PGbMGzAz8'
-        //  }
-
+      } else {
+        console.log('未获取到用户信息');
+        // throw new Error('未获取到用户信息');
       }
     } catch (error) {
       console.error('Apple登录失败:', error);
-      throw error;
+      // throw error;
     }
   };
 
   const handleGoogleLogin = async () => {
     try {
+      // 检查是否有Google Play Services
+      const hasPlayServices = await GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: false });
+      
+      if (!hasPlayServices) {
+        console.log('Google Play Services不可用,使用Mock数据');
+        // Mock登录数据(仅用于开发测试)
+        const mockUserInfo = {
+          data: {
+            user: {
+              id: 'mock_user_123',
+              name: '测试用户',
+              email: 'test@example.com',
+              photo: null,
+            },
+          },
+        };
+        Alert.alert('开发模式', '当前使用Mock登录数据(Google服务不可用)');
+        sendMessageToWebView(webViewRef, {
+          type: eventTypeEnum.H5_LOGIN_SUCCESS_ANDROID,
+          status: 'success',
+          message: '登录成功(Mock模式)',
+          data: mockUserInfo,
+        });
+        return;
+      }
+
       // 配置Google登录
       await GoogleSignin.configure({
         webClientId: AUTH_CONFIG.GOOGLE.WEB_CLIENT_ID,
         offlineAccess: true,
       });
-
-      // 检查是否已登录
-      await GoogleSignin.hasPlayServices();
       
       // 执行登录
       const userInfo = await GoogleSignin.signIn();
       console.log('Google登录成功:', userInfo);
       Alert.alert('登录成功', `欢迎 ${userInfo.data?.user.name || '用户'}`);  
-      // return userInfo
       sendMessageToWebView(webViewRef, {
         type: 'LOGIN_SUCCESS_ANDROID',
         status: 'success',
@@ -227,21 +257,55 @@ function App() {
   /**
    * 初始化IAP
    */
-  useEffect(() => {
-    initIAP();
-    return () => {
-      // 清理IAP连接
-      RNIap.endConnection();
-    };
-  }, []);
+  // 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连接初始化成功');
+      console.log('IAP连接初始化成功');
       setIsIAPInitialized(true);
 
       // 获取可用的订阅产品
@@ -325,9 +389,33 @@ function App() {
         purchaseUpdateSubscription.remove();
         purchaseErrorSubscription.remove();
       };
-    } catch (error) {
-      console.error('IAP初始化失败:', error);
-      Alert.alert('初始化失败', '无法连接到应用商店');
+    } 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: '知道了' }]
+      );
     }
   };
 

+ 3 - 3
src/config/auth.ts

@@ -3,11 +3,11 @@ export const AUTH_CONFIG = {
   // Google登录配置
   GOOGLE: {
     // Web客户端ID - React Native必需,用于身份验证和获取ID Token
-    WEB_CLIENT_ID: 'YOUR_WEB_CLIENT_ID_HERE',
+    WEB_CLIENT_ID: '617980926051-dpthr05ej6eas3u4sg3ao0kpt14snjlf.apps.googleusercontent.com',
     // Android客户端ID(可选,用于原生功能)
-    ANDROID_CLIENT_ID: 'YOUR_ANDROID_CLIENT_ID_HERE',
+    ANDROID_CLIENT_ID: '617980926051-ob7kmrb9qktpq95cv7sqbau8vlki2g69.apps.googleusercontent.com',
     // iOS客户端ID(可选,用于原生功能)
-    IOS_CLIENT_ID: 'YOUR_IOS_CLIENT_ID_HERE',
+    // IOS_CLIENT_ID: 'YOUR_IOS_CLIENT_ID_HERE',
   },
   
   // Apple登录配置

+ 6 - 0
src/utils/h5.ts

@@ -0,0 +1,6 @@
+export enum eventTypeEnum {
+  // 发起原生登录
+  'NATIVE_LOGIN_REQUEST' = 'NATIVE_LOGIN_REQUEST',
+  'H5_LOGIN_SUCCESS_IOS' = 'H5_LOGIN_SUCCESS_IOS',
+  'H5_LOGIN_SUCCESS_ANDROID' = 'H5_LOGIN_SUCCESS_ANDROID'
+}