Эх сурвалжийг харах

已接入支付宝版本-需要真机启动-启动命令变更

zbb 3 сар өмнө
parent
commit
72bb21eb90

+ 7 - 4
app.json

@@ -5,11 +5,12 @@
     "version": "1.0.0",
     "orientation": "portrait",
     "icon": "./assets/images/icon.png",
-    "scheme": "asios",
+    "scheme": ["asios", "alipay2021004126636720"],
     "userInterfaceStyle": "automatic",
     "newArchEnabled": true,
     "ios": {
-      "supportsTablet": true
+      "supportsTablet": true,
+      "bundleIdentifier": "com.anonymous.asios"
     },
     "android": {
       "adaptiveIcon": {
@@ -19,7 +20,8 @@
         "monochromeImage": "./assets/images/android-icon-monochrome.png"
       },
       "edgeToEdgeEnabled": true,
-      "predictiveBackGestureEnabled": false
+      "predictiveBackGestureEnabled": false,
+      "package": "com.anonymous.asios"
     },
     "web": {
       "output": "static",
@@ -38,7 +40,8 @@
             "backgroundColor": "#000000"
           }
         }
-      ]
+      ],
+      "expo-native-alipay"
     ],
     "experiments": {
       "typedRoutes": true,

+ 172 - 30
app/award-detail/components/CheckoutModal.tsx

@@ -2,20 +2,23 @@ import { Image } from 'expo-image';
 import { useRouter } from 'expo-router';
 import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react';
 import {
-    ActivityIndicator,
-    Alert,
-    Dimensions,
-    Modal,
-    ScrollView,
-    StyleSheet,
-    Text,
-    TouchableOpacity,
-    View
+  ActivityIndicator,
+  Alert,
+  Dimensions,
+  Modal,
+  ScrollView,
+  StyleSheet,
+  Text,
+  TouchableOpacity,
+  View
 } from 'react-native';
 
 import { applyOrder, getApplyResult, previewOrder } from '@/services/award';
+import Alipay from 'expo-native-alipay';
 import { LotteryResultModal, LotteryResultModalRef } from './LotteryResultModal';
 
+
+
 const { width: SCREEN_WIDTH } = Dimensions.get('window');
 
 // 等级配置
@@ -95,6 +98,13 @@ export const CheckoutModal = forwardRef<CheckoutModalRef, CheckoutModalProps>(
       }
     };
 
+    // ... (Imports handled via separate edit or assume existing)
+    
+    const [payConfig, setPayConfig] = useState<any>(null);
+    const [paymentMethod, setPaymentMethod] = useState<'ALIPAY' | 'WXPAY'>('ALIPAY');
+    
+    // ...
+
     useImperativeHandle(ref, () => ({
       show: (n: number, previewData: any = {}, bNum?: string, seats?: number[], pack?: boolean) => {
         setNum(n);
@@ -103,6 +113,7 @@ export const CheckoutModal = forwardRef<CheckoutModalRef, CheckoutModalProps>(
         setPackFlag(pack || undefined);
         setPreviewData(previewData);
         setVisible(true);
+        fetchPayConfig();
       },
       showFreedom: () => {
         setFreedomNum(10);
@@ -125,6 +136,7 @@ export const CheckoutModal = forwardRef<CheckoutModalRef, CheckoutModalProps>(
           setFreedomNum(selectedNum);
           setPreviewData(preview);
           setVisible(true);
+          fetchPayConfig();
         }
       } catch (error: any) {
         Alert.alert('提示', error?.message || '获取订单信息失败');
@@ -144,7 +156,19 @@ export const CheckoutModal = forwardRef<CheckoutModalRef, CheckoutModalProps>(
       onSuccess({ tradeNo: '', num });
     };
 
-    // 支付
+    const fetchPayConfig = async () => {
+        try {
+            const res = await import('@/services/user').then(m => m.getParamConfig('wxpay_alipay'));
+            if (res && res.data) {
+                setPayConfig(JSON.parse(res.data));
+            }
+        } catch (e) {
+            console.log('Fetch Pay Config Error', e);
+        }
+    };
+    
+    // ...
+
     const pay = async () => {
       if (loading) return;
       if (!checked) {
@@ -154,25 +178,48 @@ export const CheckoutModal = forwardRef<CheckoutModalRef, CheckoutModalProps>(
 
       setLoading(true);
       try {
-        const paymentType = 'WALLET';
+        let paymentType = '';
+        
+        // Prioritize Wallet if checked
+        if (cashChecked) {
+            paymentType = 'WALLET';
+        } else if (paymentMethod === 'ALIPAY') {
+            // APP端固定使用 ALIPAY_APP,忽略后端配置的 CUSTOMER_SERVICE_LINK
+            paymentType = 'ALIPAY_APP';
+        } else {
+            // 微信支付也固定使用 WXPAY_APP
+            paymentType = 'WXPAY_APP';
+        }
+
         const payNum = packFlag ? 1 : num;
 
+        console.log('Submit Order Params:', {
+            poolId,
+            quantity: payNum,
+            paymentType,
+            boxNum,
+            seatNumbers,
+            packFlag,
+            payConfig
+        });
+
         const res = await applyOrder(poolId, payNum, paymentType, boxNum, seatNumbers, packFlag);
+        
+        console.log('Apply Order Result:', res);
 
-        if (res?.paySuccess || res?.bizTradeNo || res?.tradeNo) {
-          const tradeNo = res.bizTradeNo || res.tradeNo;
-          setVisible(false);
-          
-          // Navigation to Lottery Animation
-          router.push({
-            pathname: '/lottery' as any,
-            params: { tradeNo, num, poolId }
-          });
-          
-          // Trigger success callback (e.g. to refresh pool data)
-          onSuccess({ tradeNo, num });
+        if (!res) {
+             Alert.alert('提示', '订单创建失败');
+             return;
+        }
+
+        if (res.paySuccess) {
+           // Direct Success (Wallet)
+           handleSuccess(res.bizTradeNo || res.tradeNo);
+        } else if (res.payInfo) {
+           // Handle Native Payment
+           handleNativePay(res.payInfo, paymentType, res.bizTradeNo || res.tradeNo);
         } else {
-          Alert.alert('提示', res?.message || '支付失败,请重试');
+            Alert.alert('提示', res?.message || '支付失败,请重试');
         }
       } catch (error: any) {
         Alert.alert('支付失败', error?.message || '请稍后重试');
@@ -181,6 +228,45 @@ export const CheckoutModal = forwardRef<CheckoutModalRef, CheckoutModalProps>(
       }
     };
 
+    const handleSuccess = (tradeNo: string) => {
+        setVisible(false);
+        router.push({
+            pathname: '/lottery' as any,
+            params: { tradeNo, num, poolId }
+        });
+        onSuccess({ tradeNo, num });
+    };
+
+    const handleNativePay = async (payInfo: string, type: string, tradeNo: string) => {
+        if (type === 'ALIPAY' || type.includes('ALIPAY')) {
+             try {
+                // 设置支付宝 URL Scheme(用于支付完成后返回APP)
+                Alipay.setAlipayScheme('alipay2021004126636720');
+                
+                // 使用 expo-native-alipay 调用支付宝
+                const result = await Alipay.pay(payInfo);
+                console.log('Alipay Result:', result);
+                
+                // resultStatus: '9000' 表示支付成功
+                const status = result?.resultStatus;
+                
+                if (status === '9000') {
+                    // 支付成功,跳转到抽奖页面
+                    handleSuccess(tradeNo);
+                } else if (status === '6001') {
+                    Alert.alert('提示', '用户取消支付');
+                } else {
+                    Alert.alert('支付中断', `状态码: ${status}`);
+                }
+             } catch (e: any) {
+                 console.log('Alipay Error:', e);
+                 Alert.alert('支付异常', e.message || '调用支付宝失败');
+             }
+        } else {
+            Alert.alert('提示', '微信支付暂未实现');
+        }
+    };
+
     // 获取抽奖结果(10发以下用弹窗)
     const fetchLotteryResult = async (tradeNo: string) => {
       setResultLoading(true);
@@ -202,9 +288,7 @@ export const CheckoutModal = forwardRef<CheckoutModalRef, CheckoutModalProps>(
             setTimeout(poll, 1000);
           } else {
             setResultLoading(false);
-            if (typeof window !== 'undefined') {
-              window.alert('获取结果超时,请在仓库中查看');
-            }
+            Alert.alert('提示', '获取结果超时,请在仓库中查看');
           }
         } catch {
           if (attempts < maxAttempts) {
@@ -212,9 +296,7 @@ export const CheckoutModal = forwardRef<CheckoutModalRef, CheckoutModalProps>(
             setTimeout(poll, 1000);
           } else {
             setResultLoading(false);
-            if (typeof window !== 'undefined') {
-              window.alert('获取结果失败,请在仓库中查看');
-            }
+             Alert.alert('提示', '获取结果失败,请在仓库中查看');
           }
         }
       };
@@ -331,6 +413,41 @@ export const CheckoutModal = forwardRef<CheckoutModalRef, CheckoutModalProps>(
                     </TouchableOpacity>
                   </View>
                 )}
+                
+                {/* Payment Methods Section */}
+                {(!cashChecked || (cash && cash.balance < (lastPrice || (data?.price || 0) * num))) && payConfig ? (
+                    <View style={styles.paymentSection}>
+                         <Text style={styles.sectionTitle}>支付方式</Text>
+                         
+                         {payConfig?.alipay?.enabled ? (
+                             <TouchableOpacity 
+                                style={styles.payOption}
+                                onPress={() => setPaymentMethod('ALIPAY')}
+                             >
+                                <View style={styles.rowLeft}>
+                                   <Text style={styles.payLabel}>支付宝支付</Text>
+                                </View>
+                                <View style={[styles.radio, paymentMethod === 'ALIPAY' && styles.radioChecked]}>
+                                    {paymentMethod === 'ALIPAY' ? <View style={styles.radioInner} /> : null}
+                                </View>
+                             </TouchableOpacity>
+                         ) : null}
+
+                         {payConfig?.wxpay?.enabled ? (
+                             <TouchableOpacity 
+                                style={styles.payOption}
+                                onPress={() => setPaymentMethod('WXPAY')}
+                             >
+                                <View style={styles.rowLeft}>
+                                   <Text style={styles.payLabel}>微信支付</Text>
+                                </View>
+                                <View style={[styles.radio, paymentMethod === 'WXPAY' && styles.radioChecked]}>
+                                    {paymentMethod === 'WXPAY' ? <View style={styles.radioInner} /> : null}
+                                </View>
+                             </TouchableOpacity>
+                         ) : null}
+                    </View>
+                ) : null}
 
                 <View style={styles.agreementRow}>
                   <View style={styles.agreementLeft}>
@@ -658,4 +775,29 @@ const styles = StyleSheet.create({
     fontSize: 16,
     fontWeight: '600',
   },
+  paymentSection: {
+      marginTop: 20,
+      borderTopWidth: 1,
+      borderTopColor: '#f0f0f0',
+      paddingTop: 10,
+  },
+  sectionTitle: {
+      fontSize: 14,
+      fontWeight: 'bold',
+      marginBottom: 10,
+      color: '#333',
+  },
+  payOption: {
+      flexDirection: 'row',
+      justifyContent: 'space-between',
+      alignItems: 'center',
+      paddingVertical: 12,
+      borderBottomWidth: 1,
+      borderBottomColor: '#f9f9f9',
+  },
+  payLabel: {
+      fontSize: 14,
+      color: '#333',
+      marginLeft: 10,
+  },
 });

+ 36 - 5
package-lock.json

@@ -14,6 +14,7 @@
         "@react-navigation/elements": "^2.6.3",
         "@react-navigation/native": "^7.1.8",
         "expo": "~54.0.30",
+        "expo-av": "~16.0.8",
         "expo-clipboard": "~8.0.8",
         "expo-constants": "~18.0.12",
         "expo-font": "~14.0.10",
@@ -21,6 +22,7 @@
         "expo-image": "~3.0.11",
         "expo-image-picker": "~17.0.10",
         "expo-linking": "~8.0.11",
+        "expo-native-alipay": "^0.1.1",
         "expo-router": "~6.0.21",
         "expo-splash-screen": "~31.0.13",
         "expo-status-bar": "~3.0.9",
@@ -36,7 +38,7 @@
         "react-native-reanimated": "~4.1.1",
         "react-native-safe-area-context": "~5.6.0",
         "react-native-screens": "~4.16.0",
-        "react-native-svg": "^13.4.0",
+        "react-native-svg": "^15.15.1",
         "react-native-web": "~0.21.0",
         "react-native-webview": "^13.16.0",
         "react-native-worklets": "0.5.1"
@@ -7515,6 +7517,23 @@
         "react-native": "*"
       }
     },
+    "node_modules/expo-av": {
+      "version": "16.0.8",
+      "resolved": "https://registry.npmjs.org/expo-av/-/expo-av-16.0.8.tgz",
+      "integrity": "sha512-cmVPftGR/ca7XBgs7R6ky36lF3OC0/MM/lpgX/yXqfv0jASTsh7AYX9JxHCwFmF+Z6JEB1vne9FDx4GiLcGreQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "expo": "*",
+        "react": "*",
+        "react-native": "*",
+        "react-native-web": "*"
+      },
+      "peerDependenciesMeta": {
+        "react-native-web": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/expo-clipboard": {
       "version": "8.0.8",
       "resolved": "https://registry.npmmirror.com/expo-clipboard/-/expo-clipboard-8.0.8.tgz",
@@ -7663,6 +7682,17 @@
         "react-native": "*"
       }
     },
+    "node_modules/expo-native-alipay": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/expo-native-alipay/-/expo-native-alipay-0.1.1.tgz",
+      "integrity": "sha512-kaVv9fT6nobcD/FQDeOxC86pPT8vemeOA46Tw+q4HYOiyil8jZrOUKbtvIgvPo9rI+ecGudC0a1JUHkkt19H2Q==",
+      "license": "MIT",
+      "peerDependencies": {
+        "expo": "*",
+        "react": "*",
+        "react-native": "*"
+      }
+    },
     "node_modules/expo-router": {
       "version": "6.0.21",
       "resolved": "https://registry.npmjs.org/expo-router/-/expo-router-6.0.21.tgz",
@@ -12166,13 +12196,14 @@
       }
     },
     "node_modules/react-native-svg": {
-      "version": "13.4.0",
-      "resolved": "https://registry.npmmirror.com/react-native-svg/-/react-native-svg-13.4.0.tgz",
-      "integrity": "sha512-B3TwK+H0+JuRhYPzF21AgqMt4fjhCwDZ9QUtwNstT5XcslJBXC0FoTkdZo8IEb1Sv4suSqhZwlAY6lwOv3tHag==",
+      "version": "15.15.1",
+      "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.15.1.tgz",
+      "integrity": "sha512-ZUD1xwc3Hwo4cOmOLumjJVoc7lEf9oQFlHnLmgccLC19fNm6LVEdtB+Cnip6gEi0PG3wfvVzskViEtrySQP8Fw==",
       "license": "MIT",
       "dependencies": {
         "css-select": "^5.1.0",
-        "css-tree": "^1.1.3"
+        "css-tree": "^1.1.3",
+        "warn-once": "0.1.1"
       },
       "peerDependencies": {
         "react": "*",

+ 5 - 3
package.json

@@ -5,8 +5,8 @@
   "scripts": {
     "start": "expo start",
     "reset-project": "node ./scripts/reset-project.js",
-    "android": "expo start --android",
-    "ios": "expo start --ios",
+    "android": "expo run:android",
+    "ios": "expo run:ios",
     "web": "expo start --web",
     "lint": "expo lint"
   },
@@ -17,6 +17,7 @@
     "@react-navigation/elements": "^2.6.3",
     "@react-navigation/native": "^7.1.8",
     "expo": "~54.0.30",
+    "expo-av": "~16.0.8",
     "expo-clipboard": "~8.0.8",
     "expo-constants": "~18.0.12",
     "expo-font": "~14.0.10",
@@ -24,6 +25,7 @@
     "expo-image": "~3.0.11",
     "expo-image-picker": "~17.0.10",
     "expo-linking": "~8.0.11",
+    "expo-native-alipay": "^0.1.1",
     "expo-router": "~6.0.21",
     "expo-splash-screen": "~31.0.13",
     "expo-status-bar": "~3.0.9",
@@ -39,7 +41,7 @@
     "react-native-reanimated": "~4.1.1",
     "react-native-safe-area-context": "~5.6.0",
     "react-native-screens": "~4.16.0",
-    "react-native-svg": "^13.4.0",
+    "react-native-svg": "^15.15.1",
     "react-native-web": "~0.21.0",
     "react-native-webview": "^13.16.0",
     "react-native-worklets": "0.5.1"

+ 67 - 0
plugins/withAlipay.js

@@ -0,0 +1,67 @@
+const { withInfoPlist, withAndroidManifest } = require('expo/config-plugins');
+
+/**
+ * Local Expo Config Plugin for @uiw/react-native-alipay
+ * Configures iOS URL Schemes and Android queries for Alipay SDK
+ */
+const withAlipay = (config, { alipayScheme = 'alipay' } = {}) => {
+  // iOS: Add URL Scheme for Alipay callback
+  config = withInfoPlist(config, (config) => {
+    if (!config.modResults.CFBundleURLTypes) {
+      config.modResults.CFBundleURLTypes = [];
+    }
+
+    // Check if alipay scheme already exists
+    const existingScheme = config.modResults.CFBundleURLTypes.find(
+      (urlType) => urlType.CFBundleURLSchemes && urlType.CFBundleURLSchemes.includes(alipayScheme)
+    );
+
+    if (!existingScheme) {
+      config.modResults.CFBundleURLTypes.push({
+        CFBundleURLName: 'alipay',
+        CFBundleURLSchemes: [alipayScheme],
+      });
+    }
+
+    // Add LSApplicationQueriesSchemes for Alipay
+    if (!config.modResults.LSApplicationQueriesSchemes) {
+      config.modResults.LSApplicationQueriesSchemes = [];
+    }
+
+    const alipaySchemes = ['alipay', 'alipays', 'alipayshare'];
+    alipaySchemes.forEach((scheme) => {
+      if (!config.modResults.LSApplicationQueriesSchemes.includes(scheme)) {
+        config.modResults.LSApplicationQueriesSchemes.push(scheme);
+      }
+    });
+
+    return config;
+  });
+
+  // Android: Add queries for Alipay package
+  config = withAndroidManifest(config, (config) => {
+    const manifest = config.modResults.manifest;
+
+    if (!manifest.queries) {
+      manifest.queries = [];
+    }
+
+    // Add package query for Alipay
+    const alipayPackage = { package: [{ $: { 'android:name': 'com.eg.android.AlipayGphone' } }] };
+    
+    // Check if already exists
+    const hasAlipayQuery = manifest.queries.some(
+      (q) => q.package && q.package.some((p) => p.$ && p.$['android:name'] === 'com.eg.android.AlipayGphone')
+    );
+
+    if (!hasAlipayQuery) {
+      manifest.queries.push(alipayPackage);
+    }
+
+    return config;
+  });
+
+  return config;
+};
+
+module.exports = withAlipay;