diff --git a/App.tsx b/App.tsx
index 4d58b5d..8e44486 100644
--- a/App.tsx
+++ b/App.tsx
@@ -1,11 +1,32 @@
import React from 'react';
-import { Text } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { SafeAreaProvider } from 'react-native-safe-area-context';
+import Svg, { Path, Circle, Rect } from 'react-native-svg';
import HomeScreen from './screens/HomeScreen';
+import TestScreen from './screens/TestScreen';
import MessageScreen from './screens/MessageScreen';
+const WalletIcon = ({ color, size }: { color: string; size: number }) => (
+
+);
+
+const ToolIcon = ({ color, size }: { color: string; size: number }) => (
+
+);
+
+const MessageIcon = ({ color, size }: { color: string; size: number }) => (
+
+);
+
const Tab = createBottomTabNavigator();
export default function App() {
@@ -30,11 +51,18 @@ export default function App() {
name="Home"
component={HomeScreen}
options={{
- title: '首页',
- tabBarLabel: '首页',
- tabBarIcon: ({ color, size }) => (
- 🏠
- ),
+ title: '钱包列表',
+ tabBarLabel: '钱包列表',
+ tabBarIcon: ({ color, size }) => ,
+ }}
+ />
+ ,
}}
/>
(
- 💬
- ),
+ tabBarIcon: ({ color, size }) => ,
}}
/>
diff --git a/declarations.d.ts b/declarations.d.ts
new file mode 100644
index 0000000..dcae957
--- /dev/null
+++ b/declarations.d.ts
@@ -0,0 +1,6 @@
+declare module '*.svg' {
+ import React from 'react';
+ import { SvgProps } from 'react-native-svg';
+ const content: React.FC;
+ export default content;
+}
diff --git a/metro.config.js b/metro.config.js
index ad8f87b..87f8ee2 100644
--- a/metro.config.js
+++ b/metro.config.js
@@ -1,11 +1,16 @@
-const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
+const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
-/**
- * Metro configuration
- * https://facebook.github.io/metro/docs/configuration
- *
- * @type {import('metro-config').MetroConfig}
- */
-const config = {};
+const defaultConfig = getDefaultConfig(__dirname);
+const { assetExts, sourceExts } = defaultConfig.resolver;
-module.exports = mergeConfig(getDefaultConfig(__dirname), config);
+const config = {
+ transformer: {
+ babelTransformerPath: require.resolve('react-native-svg-transformer'),
+ },
+ resolver: {
+ assetExts: assetExts.filter(ext => ext !== 'svg'),
+ sourceExts: [...sourceExts, 'svg'],
+ },
+};
+
+module.exports = mergeConfig(defaultConfig, config);
diff --git a/package.json b/package.json
index 119a351..342480d 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,8 @@
"react-native-gesture-handler": "~2.9.0",
"react-native-safe-area-context": "^4.4.1",
"react-native-screens": "~3.36.0",
+ "react-native-svg": "^14.1.0",
+ "react-native-svg-transformer": "^1.5.3",
"react-native-tcp-socket": "^6.4.1",
"react-native-webview": "13.6.2",
"rnauto": "./libs/rnauto",
diff --git a/res/bharatpe-business.webp b/res/bharatpe-business.webp
new file mode 100644
index 0000000..729871b
Binary files /dev/null and b/res/bharatpe-business.webp differ
diff --git a/res/freecharge.png b/res/freecharge.png
new file mode 100644
index 0000000..08fc60b
Binary files /dev/null and b/res/freecharge.png differ
diff --git a/res/googlepay-business.webp b/res/googlepay-business.webp
new file mode 100644
index 0000000..04165e9
Binary files /dev/null and b/res/googlepay-business.webp differ
diff --git a/res/mobikwik.png b/res/mobikwik.png
new file mode 100644
index 0000000..32908f5
Binary files /dev/null and b/res/mobikwik.png differ
diff --git a/res/paytm-business.png b/res/paytm-business.png
new file mode 100644
index 0000000..b8bd773
Binary files /dev/null and b/res/paytm-business.png differ
diff --git a/res/paytm.png b/res/paytm.png
new file mode 100644
index 0000000..1803ce3
Binary files /dev/null and b/res/paytm.png differ
diff --git a/res/phonepe-business.webp b/res/phonepe-business.webp
new file mode 100644
index 0000000..9cb44f4
Binary files /dev/null and b/res/phonepe-business.webp differ
diff --git a/res/phonepe.webp b/res/phonepe.webp
new file mode 100644
index 0000000..c6111c8
Binary files /dev/null and b/res/phonepe.webp differ
diff --git a/screens/HomeScreen.tsx b/screens/HomeScreen.tsx
index a4c7144..41f8440 100644
--- a/screens/HomeScreen.tsx
+++ b/screens/HomeScreen.tsx
@@ -1,5 +1,9 @@
import React, { Component } from "react";
-import { Alert, AppState, AppStateStatus, Modal, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View } from "react-native";
+import {
+ Alert, AppState, AppStateStatus, FlatList, Image, Modal,
+ ScrollView, StyleSheet, Text, TextInput,
+ TouchableOpacity, View, ActivityIndicator,
+} from "react-native";
import DeviceInfo from 'react-native-device-info';
import {
PhonePeBusinessBind,
@@ -22,9 +26,6 @@ import {
PhonePePersonalBind,
SmsMessage,
proxyBackgroundService,
- proxySendMessage,
- onProxyMessage,
- paytmPay
} from "rnwalletman";
import {
@@ -36,17 +37,79 @@ import {
PaytmBusinessOTPBind,
} from '../components/WalletBindComponents';
-import Api, { loadServerDomain, saveServerDomain, getServerDomain } from '../services/api';
-import { WalletmanAppState } from '../types';
+import Api, { WalletItem, loadServerDomain, saveServerDomain, getServerDomain } from '../services/api';
-interface HomeScreenState extends WalletmanAppState {}
+// key 与服务端 WalletType 字符串一致(见 types.go)
+const WALLET_ICONS: Record = {
+ 'paytm': require('../res/paytm.png'),
+ 'paytm business': require('../res/paytm-business.png'),
+ 'phonepe': require('../res/phonepe.webp'),
+ 'phonepe business': require('../res/phonepe-business.webp'),
+ 'googlepay business': require('../res/googlepay-business.webp'),
+ 'bharatpe business': require('../res/bharatpe-business.webp'),
+ 'mobikwik': require('../res/mobikwik.png'),
+ 'freecharge': require('../res/freecharge.png'),
+};
+
+const WALLET_TYPE_COLORS: Record = {
+ 'paytm': '#002970',
+ 'paytm business': '#002970',
+ 'phonepe': '#5a2d9c',
+ 'phonepe business': '#5a2d9c',
+ 'googlepay business': '#4285f4',
+ 'bharatpe business': '#e91e63',
+ 'mobikwik': '#00bcd4',
+ 'freecharge': '#ff5722',
+};
+
+// 钱包类型展示信息(walletType 与服务端一致)
+const WALLET_TYPE_OPTIONS = [
+ { key: 'paytm_personal_otp', walletType: 'paytm', label: 'Paytm Personal (OTP)', mode: 'otp' },
+ { key: 'paytm_personal_token', walletType: 'paytm', label: 'Paytm Personal (Token)', mode: 'token' },
+ { key: 'paytm_business', walletType: 'paytm business', label: 'Paytm Business (OTP)', mode: 'otp' },
+ { key: 'phonepe_personal_otp', walletType: 'phonepe', label: 'PhonePe Personal (OTP)', mode: 'otp' },
+ { key: 'phonepe_personal_token', walletType: 'phonepe', label: 'PhonePe Personal (Token)', mode: 'token' },
+ { key: 'phonepe_business', walletType: 'phonepe business', label: 'PhonePe Business (OTP-WEB)', mode: 'otp' },
+ { key: 'googlepay_business', walletType: 'googlepay business', label: 'GooglePay Business', mode: 'token' },
+ { key: 'bharatpe_business', walletType: 'bharatpe business', label: 'BharatPe Business (OTP)', mode: 'otp' },
+ { key: 'mobikwik_personal', walletType: 'mobikwik', label: 'Mobikwik Personal (OTP)', mode: 'otp' },
+ { key: 'freecharge_personal', walletType: 'freecharge', label: 'Freecharge Personal (OTP)', mode: 'otp' },
+];
+
+interface HomeScreenState {
+ // bind modals
+ showPaytmPersonalBind: boolean;
+ paytmPersonalBindType: 'otpMode' | 'tokenMode';
+ showPaytmBusinessBind: boolean;
+ showPhonePePersonalBind: boolean;
+ phonePePersonalBindType: 'otpMode' | 'tokenMode';
+ showPhonePeBusinessBind: boolean;
+ showGooglePayBusinessBind: boolean;
+ showBharatPeBusinessBind: boolean;
+ showMobikwikPersonalBind: boolean;
+ showFreechargePersonalBind: boolean;
+ // proxy
+ proxyStatus: 'idle' | 'connecting' | 'connected' | 'disconnected' | 'error';
+ proxyError?: string;
+ // server settings
+ showServerSettings: boolean;
+ settingsHost: string;
+ settingsPort: string;
+ // wallet list
+ wallets: WalletItem[];
+ loadingWallets: boolean;
+ expandedWalletId: string | null;
+ walletVpas: Record;
+ loadingVpas: Record;
+ // add wallet
+ showAddWallet: boolean;
+}
export default class HomeScreen extends Component {
private deviceId: string;
private tuneUserId: string;
private clientId: string = '';
private appStateSubscription?: any;
- private onProxyMessageSub?: ReturnType;
constructor(props: any) {
super(props);
@@ -65,8 +128,13 @@ export default class HomeScreen extends Component {
showServerSettings: false,
settingsHost: '',
settingsPort: '',
+ wallets: [],
+ loadingWallets: false,
+ expandedWalletId: null,
+ walletVpas: {},
+ loadingVpas: {},
+ showAddWallet: false,
};
-
this.deviceId = DeviceInfo.getUniqueIdSync();
this.tuneUserId = Math.random().toString(36).substring(2, 15);
}
@@ -75,21 +143,11 @@ export default class HomeScreen extends Component {
await loadServerDomain();
await this.setupPermissions();
- this.onProxyMessageSub = onProxyMessage((msg) => {
- switch (msg.type) {
- case 'echo':
- Alert.alert('Echo 回来了', JSON.stringify(msg.data));
- break;
- default:
- break;
- }
- });
-
const doLogin = () => {
Api.instance.login('test123', '123456')
- .then(async (userId) => {
- console.log('[Login] userId:', userId);
+ .then(async () => {
await this.startProxyClient();
+ this.fetchWallets();
})
.catch((error) => {
console.log('[Login] retry in 3s:', error);
@@ -103,20 +161,12 @@ export default class HomeScreen extends Component {
componentWillUnmount() {
this.stopProxyClient();
stopSmsListener();
- this.onProxyMessageSub?.remove();
this.appStateSubscription?.remove();
}
- sendEcho = () => {
- const text = `hello_${Date.now()}`;
- proxySendMessage({ type: 'echo', messageId: `echo_${Date.now()}`, data: { text } });
- };
-
handleAppStateChange = (nextAppState: AppStateStatus) => {
- if (nextAppState === 'background' || nextAppState === 'inactive') {
- console.log('[AppState] 应用进入后台,代理服务继续运行');
- } else if (nextAppState === 'active') {
- console.log('[AppState] 应用回到前台');
+ if (nextAppState === 'active') {
+ this.fetchWallets();
}
}
@@ -133,28 +183,18 @@ export default class HomeScreen extends Component {
try {
this.clientId = DeviceInfo.getUniqueIdSync();
const userId = Api.instance.getUserId();
- console.log('[Proxy] 初始化后台服务:', this.clientId, 'userId:', userId);
this.setState({ proxyStatus: 'connecting' });
await proxyBackgroundService.start({
wsUrl: Api.WS_URL,
clientId: this.clientId || '',
- userId: userId,
+ userId,
debug: true,
heartbeatInterval: 10000,
reconnectInterval: 5000,
reconnectMaxAttempts: Infinity,
- onConnected: () => {
- console.log('[Proxy] 后台服务已连接');
- this.setState({ proxyStatus: 'connected' });
- },
- onDisconnected: () => {
- console.log('[Proxy] 后台服务已断开');
- this.setState({ proxyStatus: 'disconnected' });
- },
- onError: (error: string) => {
- console.log('[Proxy] 错误:', error);
- this.setState({ proxyStatus: 'error', proxyError: error });
- },
+ onConnected: () => this.setState({ proxyStatus: 'connected' }),
+ onDisconnected: () => this.setState({ proxyStatus: 'disconnected' }),
+ onError: (error: string) => this.setState({ proxyStatus: 'error', proxyError: error }),
});
} catch (error) {
console.error('[Proxy] 初始化失败:', error);
@@ -162,423 +202,228 @@ export default class HomeScreen extends Component {
}
stopProxyClient() {
- try {
- proxyBackgroundService.stop();
- console.log('[Proxy] 后台服务已停止');
- } catch (error) {
- console.error('[Proxy] 停止失败:', error);
- }
+ try { proxyBackgroundService.stop(); } catch {}
}
- handleUploadPaytmPersonalToken = async (result: PaytmPersonalBindResult) => {
+ fetchWallets = async () => {
+ this.setState({ loadingWallets: true });
try {
- await Api.instance.register(WalletType.PAYTM_PERSONAL, result);
- this.setState({ showPaytmPersonalBind: false });
- Alert.alert('绑定成功', 'Paytm Personal Token 绑定成功');
+ const wallets = await Api.instance.listWallets();
+ this.setState({ wallets });
+ } catch (e) {
+ console.log('[fetchWallets]', e);
+ } finally {
+ this.setState({ loadingWallets: false });
+ }
+ };
+
+ handleToggleExpand = async (walletId: string) => {
+ const { expandedWalletId, walletVpas } = this.state;
+ if (expandedWalletId === walletId) {
+ this.setState({ expandedWalletId: null });
+ return;
+ }
+ this.setState({ expandedWalletId: walletId });
+ if (!walletVpas[walletId]) {
+ this.setState(s => ({ loadingVpas: { ...s.loadingVpas, [walletId]: true } }));
+ try {
+ const vpas = await Api.instance.getWalletVpas(walletId);
+ this.setState(s => ({ walletVpas: { ...s.walletVpas, [walletId]: vpas } }));
+ } catch {}
+ this.setState(s => ({ loadingVpas: { ...s.loadingVpas, [walletId]: false } }));
+ }
+ };
+
+ handleSetVpa = async (walletId: string, vpaIndex: number) => {
+ try {
+ const vpa = await Api.instance.setCurrentVpa(walletId, vpaIndex);
+ Alert.alert('已设置', `当前 VPA: ${vpa}`);
+ this.fetchWallets();
+ } catch (e) {
+ Alert.alert('设置失败', (e as Error).message);
+ }
+ };
+
+ // ---- bind handlers ----
+
+ handleBindSuccess = (key: keyof HomeScreenState, msg: string) => async (result: any) => {
+ try {
+ const typeMap: Record = {
+ showPaytmPersonalBind: WalletType.PAYTM_PERSONAL,
+ showPaytmBusinessBind: WalletType.PAYTM_BUSINESS,
+ showPhonePePersonalBind: WalletType.PHONEPE_PERSONAL,
+ showPhonePeBusinessBind: WalletType.PHONEPE_BUSINESS,
+ showGooglePayBusinessBind: WalletType.GOOGLEPAY_BUSINESS,
+ showBharatPeBusinessBind: WalletType.BHARATPE_BUSINESS,
+ showMobikwikPersonalBind: WalletType.MOBIKWIK_PERSONAL,
+ showFreechargePersonalBind: WalletType.FREECHARGE_PERSONAL,
+ };
+ const wt = typeMap[key as string];
+ if (wt) await Api.instance.register(wt as WalletType, result);
+ this.setState({ [key]: false } as any);
+ Alert.alert('绑定成功', msg);
+ this.fetchWallets();
} catch (error) {
+ this.setState({ [key]: false } as any);
Alert.alert('绑定失败', (error as Error).message);
- this.setState({ showPaytmPersonalBind: false });
}
- }
+ };
- handleUploadPhonePePersonalToken = async (result: PhonePePersonalBindResult) => {
- try {
- await Api.instance.register(WalletType.PHONEPE_PERSONAL, result);
- this.setState({ showPhonePePersonalBind: false });
- Alert.alert('绑定成功', 'PhonePe Personal Token 绑定成功');
- } catch (error) {
- Alert.alert('绑定失败', (error as Error).message);
- this.setState({ showPhonePePersonalBind: false });
- }
- }
-
- handleUploadPaytmBusiness = async (result: PaytmBusinessBindResult) => {
- try {
- console.log(result);
- this.setState({ showPaytmBusinessBind: false });
- Alert.alert('绑定成功', 'Paytm Business 绑定成功');
- } catch (error) {
- Alert.alert('绑定失败', (error as Error).message);
- this.setState({ showPaytmBusinessBind: false });
- }
- }
-
- handleUploadPhonePeBusiness = async (result: PhonePeBusinessBindResult) => {
- try {
- await Api.instance.register(WalletType.PHONEPE_BUSINESS, result);
- this.setState({ showPhonePeBusinessBind: false });
- Alert.alert('绑定成功', 'PhonePe Business 绑定成功');
- } catch (error) {
- Alert.alert('绑定失败', (error as Error).message);
- this.setState({ showPhonePeBusinessBind: false });
- }
- }
-
- handleUploadGooglePayBusiness = async (result: GooglePayBusinessBindResult) => {
- try {
- await Api.instance.register(WalletType.GOOGLEPAY_BUSINESS, result);
- this.setState({ showGooglePayBusinessBind: false });
- Alert.alert('绑定成功', 'Google Pay Business 绑定成功');
- } catch (error) {
- Alert.alert('绑定失败', (error as Error).message);
- this.setState({ showGooglePayBusinessBind: false });
- }
- }
-
- handleUploadBharatPeBusiness = async (result: BharatPeBusinessBindResult) => {
- try {
- console.log(JSON.stringify(result));
- this.setState({ showBharatPeBusinessBind: false });
- Alert.alert('绑定成功', 'BharatPe Business 绑定成功');
- } catch (error) {
- Alert.alert('绑定失败', (error as Error).message);
- this.setState({ showBharatPeBusinessBind: false });
- }
- }
-
- handleUploadMobikwikPersonal = async (result: MobikwikPersonalBindResult) => {
- try {
- console.log(JSON.stringify(result));
- this.setState({ showMobikwikPersonalBind: false });
- Alert.alert('绑定成功', 'Mobikwik Personal 绑定成功');
- } catch (error) {
- Alert.alert('绑定失败', (error as Error).message);
- this.setState({ showMobikwikPersonalBind: false });
- }
- }
-
- handleUploadFreechargePersonal = async (result: FreechargePersonalBindResult) => {
- try {
- console.log(JSON.stringify(result));
- this.setState({ showFreechargePersonalBind: false });
- Alert.alert('绑定成功', 'Freecharge Personal 绑定成功');
- } catch (error) {
- Alert.alert('绑定失败', (error as Error).message);
- this.setState({ showFreechargePersonalBind: false });
- }
- }
-
- renderPaytmPersonalTokenBind = () => (
- this.setState({ showPaytmPersonalBind: false })}>
- {
- Alert.alert('绑定失败', error);
- this.setState({ showPaytmPersonalBind: false });
- }}
- />
-
- )
-
- renderPaytmPersonalOTPBind = () => (
- this.setState({ showPaytmPersonalBind: false })}>
- {
- try {
- return await Api.instance.requestOTP(walletType, params.mobile, {});
- } catch (error) {
- return { success: false, message: (error as Error).message };
- }
- }}
- onVerifyOTP={async (walletType, params) => {
- try {
- return await Api.instance.verifyOTP(walletType, params.mobile, params.otp, { sessionId: params.sessionId });
- } catch (error) {
- return { success: false, message: (error as Error).message };
- }
- }}
- onSuccess={(result: PaytmPersonalBindResult) => {
- Alert.alert('绑定成功', 'Paytm Personal OTP 绑定成功');
- this.setState({ showPaytmPersonalBind: false });
- }}
- onError={(error: string) => {
- Alert.alert('绑定失败', error);
- this.setState({ showPaytmPersonalBind: false });
- }}
- />
-
- )
-
- renderPhonePePersonalTokenBind = () => (
- this.setState({ showPhonePePersonalBind: false })}>
- {
- Alert.alert('绑定失败', error);
- this.setState({ showPhonePePersonalBind: false });
- }}
- />
-
- )
-
- renderPhonePePersonalOTPBind = () => (
- this.setState({ showPhonePePersonalBind: false })}>
- {
- try {
- return await Api.instance.requestOTP(walletType, params.mobile, {});
- } catch (error) {
- return { success: false, message: (error as Error).message };
- }
- }}
- onVerifyOTP={async (walletType, params) => {
- try {
- return await Api.instance.verifyOTP(walletType, params.mobile, params.otp, { sessionId: params.sessionId });
- } catch (error) {
- return { success: false, message: (error as Error).message };
- }
- }}
- onSuccess={(result: PhonePePersonalBindResult) => {
- Alert.alert('绑定成功', 'PhonePe Personal OTP 绑定成功');
- this.setState({ showPhonePePersonalBind: false });
- }}
- onError={(error: string) => {
- Alert.alert('绑定失败', error);
- this.setState({ showPhonePePersonalBind: false });
- }}
- />
-
- )
-
- renderPaytmBusinessBind = () => (
- this.setState({ showPaytmBusinessBind: false })}>
- {
- try {
- return await Api.instance.requestOTP(walletType, params.mobile, { password: params.password });
- } catch (error) {
- return { success: false, message: (error as Error).message };
- }
- }}
- onVerifyOTP={async (walletType, params) => {
- try {
- return await Api.instance.verifyOTP(walletType, params.mobile, params.otp, { sessionId: params.sessionId });
- } catch (error) {
- return { success: false, message: (error as Error).message };
- }
- }}
- onSuccess={this.handleUploadPaytmBusiness}
- onError={(error: string) => {
- Alert.alert('绑定失败', error);
- this.setState({ showPaytmBusinessBind: false });
- }}
- />
-
- )
-
- renderPhonePeBusinessBind = () => (
- this.setState({ showPhonePeBusinessBind: false })}>
- {
- Alert.alert('绑定失败', error);
- this.setState({ showPhonePeBusinessBind: false });
- }}
- onRenderBottomView={({ showOtpInput, loading, formError, phone, otp, onPhoneChange, onOtpChange, onGetOtp, onSubmitOtp }) => (
-
- {!showOtpInput ? (
- <>
-
- {!!formError && {formError}}
-
- {loading ? 'Loading...' : 'GET OTP'}
-
- >
- ) : (
- <>
-
- {!!formError && {formError}}
-
- {loading ? 'Loading...' : 'Verify OTP'}
-
- >
- )}
-
- )}
- />
-
- )
-
- renderGooglePayBusinessBind = () => (
- this.setState({ showGooglePayBusinessBind: false })}>
- {
- Alert.alert('绑定失败', error);
- this.setState({ showGooglePayBusinessBind: false });
- }}
- />
-
- )
-
- renderBharatPeBusinessBind = () => (
- this.setState({ showBharatPeBusinessBind: false })}>
- {
- try {
- return await Api.instance.requestOTP(walletType, params.mobile);
- } catch (error) {
- return { success: false, message: (error as Error).message };
- }
- }}
- onVerifyOTP={async (walletType, params) => {
- try {
- return await Api.instance.verifyOTP(walletType, params.mobile, params.otp, { sessionId: params.sessionId });
- } catch (error) {
- return { success: false, message: (error as Error).message };
- }
- }}
- onSuccess={this.handleUploadBharatPeBusiness}
- onError={(error: string) => {
- Alert.alert('绑定失败', error);
- this.setState({ showBharatPeBusinessBind: false });
- }}
- />
-
- )
-
- renderMobikwikPersonalOTPBind = () => (
- this.setState({ showMobikwikPersonalBind: false })}>
- {
- try {
- return await Api.instance.requestOTP(walletType, params.mobile, {
- deviceId: params.deviceId,
- tuneUserId: params.tuneUserId
- });
- } catch (error) {
- return { success: false, message: (error as Error).message };
- }
- }}
- onVerifyOTP={async (walletType, params) => {
- try {
- return await Api.instance.verifyOTP(walletType, params.mobile, params.otp, {
- sessionId: params.sessionId,
- deviceId: params.deviceId,
- tuneUserId: params.tuneUserId,
- nid: params.nid
- });
- } catch (error) {
- return { success: false, message: (error as Error).message };
- }
- }}
- onSuccess={this.handleUploadMobikwikPersonal}
- onError={(error: string) => {
- Alert.alert('绑定失败', error);
- this.setState({ showMobikwikPersonalBind: false });
- }}
- />
-
- )
-
- renderFreechargePersonalOTPBind = () => (
- this.setState({ showFreechargePersonalBind: false })}>
- {
- try {
- return await Api.instance.requestOTP(walletType, params.mobile);
- } catch (error) {
- return { success: false, message: (error as Error).message };
- }
- }}
- onVerifyOTP={async (walletType, params) => {
- try {
- return await Api.instance.verifyOTP(walletType, params.mobile, params.otp, {
- otpId: params.otpId,
- deviceId: params.deviceId,
- csrfId: params.csrfId,
- appFc: params.appFc
- });
- } catch (error) {
- return { success: false, message: (error as Error).message };
- }
- }}
- onSuccess={this.handleUploadFreechargePersonal}
- onError={(error: string) => {
- Alert.alert('绑定失败', error);
- this.setState({ showFreechargePersonalBind: false });
- }}
- />
-
- )
-
- async handlerPaytmPay () {
- paytmPay('100', 'Gurvir singh', '296001000405', 'ICIC0002960', 'ABCDEF').then(result => {
- console.log(result);
- }).catch(error => {
- console.log(error);
- });
- }
+ // ---- modals ----
renderBindModal = () => {
- if (this.state.showPaytmPersonalBind) {
- return this.state.paytmPersonalBindType === 'tokenMode'
- ? this.renderPaytmPersonalTokenBind()
- : this.renderPaytmPersonalOTPBind();
- }
- if (this.state.showPhonePePersonalBind) {
- return this.state.phonePePersonalBindType === 'tokenMode'
- ? this.renderPhonePePersonalTokenBind()
- : this.renderPhonePePersonalOTPBind();
- }
- if (this.state.showPaytmBusinessBind) return this.renderPaytmBusinessBind();
- if (this.state.showPhonePeBusinessBind) return this.renderPhonePeBusinessBind();
- if (this.state.showGooglePayBusinessBind) return this.renderGooglePayBusinessBind();
- if (this.state.showBharatPeBusinessBind) return this.renderBharatPeBusinessBind();
- if (this.state.showMobikwikPersonalBind) return this.renderMobikwikPersonalOTPBind();
- if (this.state.showFreechargePersonalBind) return this.renderFreechargePersonalOTPBind();
+ const { showPaytmPersonalBind, paytmPersonalBindType, showPhonePePersonalBind,
+ phonePePersonalBindType, showPaytmBusinessBind, showPhonePeBusinessBind,
+ showGooglePayBusinessBind, showBharatPeBusinessBind, showMobikwikPersonalBind,
+ showFreechargePersonalBind } = this.state;
+
+ const close = (key: keyof HomeScreenState) => () => this.setState({ [key]: false } as any);
+
+ if (showPaytmPersonalBind && paytmPersonalBindType === 'tokenMode') return (
+
+ { Alert.alert('绑定失败', e); close('showPaytmPersonalBind')(); }} />
+
+ );
+ if (showPaytmPersonalBind) return (
+
+ { try { return await Api.instance.requestOTP(wt, p.mobile, {}); } catch (e) { return { success: false, message: (e as Error).message }; } }}
+ onVerifyOTP={async (wt, p) => { try { return await Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionId: p.sessionId }); } catch (e) { return { success: false, message: (e as Error).message }; } }}
+ onSuccess={(r: PaytmPersonalBindResult) => { Alert.alert('绑定成功', 'Paytm Personal OTP'); this.setState({ showPaytmPersonalBind: false }); this.fetchWallets(); }}
+ onError={(e: string) => { Alert.alert('绑定失败', e); close('showPaytmPersonalBind')(); }}
+ />
+
+ );
+ if (showPhonePePersonalBind && phonePePersonalBindType === 'tokenMode') return (
+
+ { Alert.alert('绑定失败', e); close('showPhonePePersonalBind')(); }} />
+
+ );
+ if (showPhonePePersonalBind) return (
+
+ { try { return await Api.instance.requestOTP(wt, p.mobile, {}); } catch (e) { return { success: false, message: (e as Error).message }; } }}
+ onVerifyOTP={async (wt, p) => { try { return await Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionId: p.sessionId }); } catch (e) { return { success: false, message: (e as Error).message }; } }}
+ onSuccess={(r: PhonePePersonalBindResult) => { Alert.alert('绑定成功', 'PhonePe Personal OTP'); this.setState({ showPhonePePersonalBind: false }); this.fetchWallets(); }}
+ onError={(e: string) => { Alert.alert('绑定失败', e); close('showPhonePePersonalBind')(); }}
+ />
+
+ );
+ if (showPaytmBusinessBind) return (
+
+ { try { return await Api.instance.requestOTP(wt, p.mobile, { password: p.password }); } catch (e) { return { success: false, message: (e as Error).message }; } }}
+ onVerifyOTP={async (wt, p) => { try { return await Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionId: p.sessionId }); } catch (e) { return { success: false, message: (e as Error).message }; } }}
+ onSuccess={this.handleBindSuccess('showPaytmBusinessBind', 'Paytm Business 绑定成功') as any}
+ onError={(e: string) => { Alert.alert('绑定失败', e); close('showPaytmBusinessBind')(); }}
+ />
+
+ );
+ if (showPhonePeBusinessBind) return (
+
+ { Alert.alert('绑定失败', e); close('showPhonePeBusinessBind')(); }}
+ onRenderBottomView={({ showOtpInput, loading, formError, phone, otp, onPhoneChange, onOtpChange, onGetOtp, onSubmitOtp }) => (
+
+ {!showOtpInput ? (<>
+
+ {!!formError && {formError}}
+ {loading ? 'Loading...' : 'GET OTP'}
+ >) : (<>
+
+ {!!formError && {formError}}
+ {loading ? 'Loading...' : 'Verify OTP'}
+ >)}
+
+ )}
+ />
+
+ );
+ if (showGooglePayBusinessBind) return (
+
+ { Alert.alert('绑定失败', e); close('showGooglePayBusinessBind')(); }} />
+
+ );
+ if (showBharatPeBusinessBind) return (
+
+ { try { return await Api.instance.requestOTP(wt, p.mobile); } catch (e) { return { success: false, message: (e as Error).message }; } }}
+ onVerifyOTP={async (wt, p) => { try { return await Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionId: p.sessionId }); } catch (e) { return { success: false, message: (e as Error).message }; } }}
+ onSuccess={this.handleBindSuccess('showBharatPeBusinessBind', 'BharatPe Business 绑定成功') as any}
+ onError={(e: string) => { Alert.alert('绑定失败', e); close('showBharatPeBusinessBind')(); }}
+ />
+
+ );
+ if (showMobikwikPersonalBind) return (
+
+ { try { return await Api.instance.requestOTP(wt, p.mobile, { deviceId: p.deviceId, tuneUserId: p.tuneUserId }); } catch (e) { return { success: false, message: (e as Error).message }; } }}
+ onVerifyOTP={async (wt, p) => { try { return await Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionId: p.sessionId, deviceId: p.deviceId, tuneUserId: p.tuneUserId, nid: p.nid }); } catch (e) { return { success: false, message: (e as Error).message }; } }}
+ onSuccess={this.handleBindSuccess('showMobikwikPersonalBind', 'Mobikwik 绑定成功') as any}
+ onError={(e: string) => { Alert.alert('绑定失败', e); close('showMobikwikPersonalBind')(); }}
+ />
+
+ );
+ if (showFreechargePersonalBind) return (
+
+ { try { return await Api.instance.requestOTP(wt, p.mobile); } catch (e) { return { success: false, message: (e as Error).message }; } }}
+ onVerifyOTP={async (wt, p) => { try { return await Api.instance.verifyOTP(wt, p.mobile, p.otp, { otpId: p.otpId, deviceId: p.deviceId, csrfId: p.csrfId, appFc: p.appFc }); } catch (e) { return { success: false, message: (e as Error).message }; } }}
+ onSuccess={this.handleBindSuccess('showFreechargePersonalBind', 'Freecharge 绑定成功') as any}
+ onError={(e: string) => { Alert.alert('绑定失败', e); close('showFreechargePersonalBind')(); }}
+ />
+
+ );
return null;
+ };
+
+ openWalletBind = (key: string) => {
+ this.setState({ showAddWallet: false });
+ setTimeout(() => {
+ switch (key) {
+ case 'paytm_personal_otp': this.setState({ showPaytmPersonalBind: true, paytmPersonalBindType: 'otpMode' }); break;
+ case 'paytm_personal_token': this.setState({ showPaytmPersonalBind: true, paytmPersonalBindType: 'tokenMode' }); break;
+ case 'paytm_business': this.setState({ showPaytmBusinessBind: true }); break;
+ case 'phonepe_personal_otp': this.setState({ showPhonePePersonalBind: true, phonePePersonalBindType: 'otpMode' }); break;
+ case 'phonepe_personal_token': this.setState({ showPhonePePersonalBind: true, phonePePersonalBindType: 'tokenMode' }); break;
+ case 'phonepe_business': this.setState({ showPhonePeBusinessBind: true }); break;
+ case 'googlepay_business': this.setState({ showGooglePayBusinessBind: true }); break;
+ case 'bharatpe_business': this.setState({ showBharatPeBusinessBind: true }); break;
+ case 'mobikwik_personal': this.setState({ showMobikwikPersonalBind: true }); break;
+ case 'freecharge_personal': this.setState({ showFreechargePersonalBind: true }); break;
+ }
+ }, 300);
+ };
+
+ renderAddWalletModal() {
+ return (
+ this.setState({ showAddWallet: false })}>
+
+
+
+ 选择钱包类型
+ this.setState({ showAddWallet: false })}>
+ ✕
+
+
+
+ {WALLET_TYPE_OPTIONS.map(opt => (
+ this.openWalletBind(opt.key)}>
+ {WALLET_ICONS[opt.walletType]
+ ?
+ :
+ }
+ {opt.label}
+
+ ))}
+
+
+
+
+ );
}
- openServerSettings = () => {
- const domain = getServerDomain();
- const colonIdx = domain.lastIndexOf(':');
- const host = colonIdx > 0 ? domain.substring(0, colonIdx) : domain;
- const port = colonIdx > 0 ? domain.substring(colonIdx + 1) : '';
- this.setState({ showServerSettings: true, settingsHost: host, settingsPort: port });
- };
-
- saveDomain = async () => {
- const { settingsHost, settingsPort } = this.state;
- const domain = settingsPort ? `${settingsHost}:${settingsPort}` : settingsHost;
- const useHttps = settingsPort === '443';
- await saveServerDomain(domain, useHttps);
- this.setState({ showServerSettings: false });
- Alert.alert('已保存', '重启 App 后生效');
- };
-
renderServerSettingsModal() {
const { showServerSettings, settingsHost, settingsPort } = this.state;
const presets = [
@@ -587,42 +432,32 @@ export default class HomeScreen extends Component {
];
return (
-
-
- 服务器设置
+
+
+ 服务器设置
{presets.map(p => (
- this.setState({ settingsHost: p.host, settingsPort: p.port })}
- style={{ paddingHorizontal: 10, paddingVertical: 5, borderRadius: 6, backgroundColor: settingsHost === p.host && settingsPort === p.port ? '#3498db' : '#f0f0f0' }}
- >
+ this.setState({ settingsHost: p.host, settingsPort: p.port })}
+ style={[s.presetBtn, settingsHost === p.host && settingsPort === p.port && s.presetBtnActive]}>
{p.label}
))}
- Host
- this.setState({ settingsHost: t })}
- placeholder="192.168.1.198"
- autoCapitalize="none"
- keyboardType="default"
- />
- Port
- this.setState({ settingsPort: t })}
- placeholder="16000"
- keyboardType="number-pad"
- />
+ Host
+ this.setState({ settingsHost: t })} placeholder="192.168.1.198" autoCapitalize="none" />
+ Port
+ this.setState({ settingsPort: t })} placeholder="16000" keyboardType="number-pad" />
this.setState({ showServerSettings: false })} style={{ paddingHorizontal: 16, paddingVertical: 8, marginRight: 10 }}>
取消
-
+ {
+ const { settingsHost, settingsPort } = this.state;
+ const domain = settingsPort ? `${settingsHost}:${settingsPort}` : settingsHost;
+ await saveServerDomain(domain, settingsPort === '443');
+ this.setState({ showServerSettings: false });
+ Alert.alert('已保存', '重启 App 后生效');
+ }} style={s.saveBtn}>
保存
@@ -632,137 +467,214 @@ export default class HomeScreen extends Component {
);
}
+ renderWalletItem = ({ item }: { item: WalletItem }) => {
+ const { expandedWalletId, walletVpas, loadingVpas } = this.state;
+ const isExpanded = expandedWalletId === item.id;
+ const color = WALLET_TYPE_COLORS[item.walletType] ?? '#888';
+ const vpas = walletVpas[item.id] ?? [];
+ const loadingV = loadingVpas[item.id];
+ const isActive = item.status === 'ACTIVE';
+
+ return (
+ this.handleToggleExpand(item.id)} activeOpacity={0.8}>
+
+
+ {WALLET_ICONS[item.walletType]
+ ?
+ :
+ {item.walletType.split('_')[0]}
+
+ }
+
+
+ {item.phone || '—'}
+ {item.upi || 'No UPI'}
+
+
+
+ {isExpanded ? '▲' : '▼'}
+
+
+
+ {isExpanded && (
+
+ VPAs
+ {loadingV ? (
+
+ ) : vpas.length === 0 ? (
+ 无 VPA 数据
+ ) : (
+ vpas.map((vpa, idx) => (
+ { e.stopPropagation?.(); this.handleSetVpa(item.id, idx); }}>
+ {vpa}
+ {item.upi === vpa && ✓}
+
+ ))
+ )}
+
+ )}
+
+ );
+ };
+
render() {
- const { proxyStatus, proxyError } = this.state;
- const cfg: Record = {
+ const { proxyStatus, proxyError, wallets, loadingWallets } = this.state;
+ const proxyCfg: Record = {
idle: { label: '未连接', color: '#95a5a6' },
connecting: { label: '连接中…', color: '#f39c12' },
connected: { label: '已连接', color: '#2ecc71' },
disconnected: { label: '已断开', color: '#e74c3c' },
error: { label: '连接失败', color: '#e74c3c' },
};
- const { label, color } = cfg[proxyStatus];
+ const { label, color } = proxyCfg[proxyStatus];
return (
-
-
-
-
- Proxy {label}{proxyStatus === 'error' && proxyError ? `:${proxyError}` : ''}
-
+
+ {/* 顶栏 */}
+
+
+
+
+ Proxy {label}{proxyStatus === 'error' && proxyError ? `:${proxyError}` : ''}
+
+
+
+ {
+ const domain = getServerDomain();
+ const colonIdx = domain.lastIndexOf(':');
+ const host = colonIdx > 0 ? domain.substring(0, colonIdx) : domain;
+ const port = colonIdx > 0 ? domain.substring(colonIdx + 1) : '';
+ this.setState({ showServerSettings: true, settingsHost: host, settingsPort: port });
+ }}>
+ ⚙
+
+ this.setState({ showAddWallet: true })} style={s.addBtn}>
+ + Add
+
+
+
+ {/* 钱包列表 */}
+
+ 已绑定钱包
+
+ {loadingWallets ? '刷新中…' : '刷新'}
+
+
+
+ item.id}
+ renderItem={this.renderWalletItem}
+ ListEmptyComponent={
+
+ {loadingWallets ? '加载中…' : '暂无绑定钱包,点右上角 + Add 添加'}
+
+ }
+ />
+
{this.renderBindModal()}
{this.renderServerSettingsModal()}
-
- ⚙ 服务器设置
-
-
-
- {
- this.setState({ showPaytmPersonalBind: true, paytmPersonalBindType: 'otpMode' });
- }}>
- 绑定 Paytm Personal(OTP)
-
- {
- this.setState({ showPaytmPersonalBind: true, paytmPersonalBindType: 'tokenMode' });
- }}>
- 绑定 Paytm Personal(Token)
-
-
- this.setState({ showPaytmBusinessBind: true })}>
- 绑定 Paytm Business (OTP)
-
-
- {
- this.setState({ showPhonePePersonalBind: true, phonePePersonalBindType: 'otpMode' });
- }}>
- 绑定 PhonePe Personal(OTP)
-
- {
- this.setState({ showPhonePePersonalBind: true, phonePePersonalBindType: 'tokenMode' });
- }}>
- 绑定 PhonePe Personal(Token)
-
-
- this.setState({ showPhonePeBusinessBind: true })}>
- 绑定 PhonePe Business (OTP-WEB)
-
- this.setState({ showGooglePayBusinessBind: true })}>
- 绑定 GooglePay Business
-
- this.setState({ showBharatPeBusinessBind: true })}>
- 绑定 BharatPe Business (OTP)
-
- this.setState({ showMobikwikPersonalBind: true })}>
- 绑定 Mobikwik Personal (OTP)
-
- this.setState({ showFreechargePersonalBind: true })}>
- 绑定 Freecharge Personal (OTP)
-
-
- Paytm Pay 代付测试
-
-
- Echo 测试
-
-
+ {this.renderAddWalletModal()}
);
}
}
-
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- justifyContent: "center",
- alignItems: "center",
- backgroundColor: "#f0f0f0",
+const s = StyleSheet.create({
+ container: { flex: 1, backgroundColor: '#f0f0f0' },
+ topBar: {
+ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between',
+ paddingHorizontal: 14, paddingVertical: 8, backgroundColor: '#fff',
+ borderBottomWidth: 1, borderBottomColor: '#e0e0e0',
},
- scrollView: {
- flex: 1,
- // backgroundColor: "lightblue",
- width: "100%",
+ addBtn: {
+ backgroundColor: '#3498db', borderRadius: 6,
+ paddingHorizontal: 10, paddingVertical: 4,
},
- scrollViewContent: {
- flexGrow: 1,
- justifyContent: "center",
- alignItems: "center",
+ addBtnText: { color: '#fff', fontSize: 13, fontWeight: '600' },
+ listHeader: {
+ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center',
+ paddingHorizontal: 14, paddingVertical: 10,
},
- button: {
- padding: 10,
- backgroundColor: "lightblue",
- borderRadius: 5,
- width: 200,
- height: 55,
+ listHeaderText: { fontSize: 14, fontWeight: '600', color: '#333' },
+ walletCard: {
+ backgroundColor: '#fff', borderRadius: 10, padding: 14,
+ marginBottom: 10, elevation: 1,
+ shadowColor: '#000', shadowOpacity: 0.06, shadowRadius: 4, shadowOffset: { width: 0, height: 2 },
},
- text: {
- fontSize: 20,
- fontWeight: "bold",
+ walletBadge: {
+ width: 52, height: 52, borderRadius: 10, overflow: 'hidden',
+ alignItems: 'center', justifyContent: 'center',
+ backgroundColor: '#f5f5f5',
},
- modal: {
- flex: 1,
- justifyContent: "center",
- alignItems: "center",
+ walletIcon: { width: 52, height: 52, borderRadius: 10 },
+ walletIconInactive: { opacity: 0.3 },
+ walletBadgeInactive: { backgroundColor: '#f0f0f0' },
+ walletIconFallback: {
+ width: 52, height: 52, borderRadius: 10,
+ alignItems: 'center', justifyContent: 'center',
},
- modalContent: {
- flex: 1,
- justifyContent: "center",
- alignItems: "center",
+ walletBadgeText: { color: '#fff', fontSize: 9, fontWeight: '700', textAlign: 'center' },
+ walletPhone: { fontSize: 15, fontWeight: '600', color: '#222' },
+ walletUpi: { fontSize: 12, color: '#888', marginTop: 2 },
+ statusDot: { width: 8, height: 8, borderRadius: 4 },
+ vpaSection: { marginTop: 12, paddingTop: 10, borderTopWidth: 1, borderTopColor: '#f0f0f0' },
+ vpaSectionTitle: { fontSize: 12, color: '#999', marginBottom: 6, fontWeight: '600' },
+ vpaRow: {
+ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center',
+ paddingVertical: 8, paddingHorizontal: 10, borderRadius: 6,
+ backgroundColor: '#f5f5f5', marginBottom: 4,
},
- bindButton: {
- marginTop: 10,
- marginBottom: 10,
- backgroundColor: "#007AFF",
- borderRadius: 5,
- width: "90%",
- height: 45,
- alignItems: "center",
- justifyContent: "center",
+ vpaRowActive: { backgroundColor: '#3498db' },
+ vpaText: { fontSize: 13, color: '#333' },
+ modalOverlay: {
+ flex: 1, backgroundColor: 'rgba(0,0,0,0.5)',
+ justifyContent: 'center', alignItems: 'center',
},
- bindButtonText: {
- fontSize: 14,
- // fontWeight: "bold",
- color: "#fff",
+ addModalBox: {
+ backgroundColor: '#fff', borderRadius: 12, padding: 20,
+ width: '88%', maxHeight: '70%',
},
-});
\ No newline at end of file
+ addModalTitle: { fontSize: 16, fontWeight: '700', color: '#222' },
+ walletTypeRow: {
+ flexDirection: 'row', alignItems: 'center',
+ paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: '#f0f0f0',
+ },
+ walletTypeIcon: { width: 32, height: 32, borderRadius: 6, marginRight: 12 },
+ walletTypeDot: { width: 10, height: 10, borderRadius: 5, marginRight: 12 },
+ walletTypeLabel: { fontSize: 14, color: '#333' },
+ settingsBox: {
+ backgroundColor: '#fff', borderRadius: 10, padding: 20, width: '85%',
+ },
+ settingsTitle: { fontSize: 16, fontWeight: 'bold', marginBottom: 12 },
+ presetBtn: {
+ paddingHorizontal: 10, paddingVertical: 5, borderRadius: 6, backgroundColor: '#f0f0f0',
+ },
+ presetBtnActive: { backgroundColor: '#3498db' },
+ inputLabel: { fontSize: 13, color: '#666', marginBottom: 4 },
+ textInput: {
+ borderWidth: 1, borderColor: '#ddd', borderRadius: 6,
+ paddingHorizontal: 10, paddingVertical: 8, marginBottom: 12, fontSize: 14,
+ },
+ saveBtn: {
+ backgroundColor: '#3498db', paddingHorizontal: 16, paddingVertical: 8, borderRadius: 6,
+ },
+ otpBar: {
+ position: 'absolute', bottom: 0, left: 0, right: 0,
+ backgroundColor: '#fff', padding: 16, borderTopWidth: 1,
+ borderTopColor: '#e0e0e0', gap: 10,
+ },
+ otpInput: {
+ borderWidth: 1, borderColor: '#ccc', borderRadius: 8,
+ paddingHorizontal: 12, paddingVertical: 10, fontSize: 15, color: '#333',
+ },
+ otpBtn: {
+ backgroundColor: '#5a2d9c', borderRadius: 8, paddingVertical: 12, alignItems: 'center',
+ },
+ otpBtnText: { color: '#fff', fontSize: 15, fontWeight: '600' },
+ errText: { color: '#e53935', fontSize: 13 },
+});
diff --git a/screens/TestScreen.tsx b/screens/TestScreen.tsx
new file mode 100644
index 0000000..af96af3
--- /dev/null
+++ b/screens/TestScreen.tsx
@@ -0,0 +1,69 @@
+import React, { useEffect, useRef } from 'react';
+import { Alert, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
+import { onProxyMessage, proxySendMessage, paytmPay } from 'rnwalletman';
+
+export default function TestScreen() {
+ const subRef = useRef | null>(null);
+
+ useEffect(() => {
+ subRef.current = onProxyMessage((msg) => {
+ if (msg.type === 'echo') {
+ Alert.alert('Echo 回来了', JSON.stringify(msg.data));
+ }
+ });
+ return () => {
+ subRef.current?.remove();
+ };
+ }, []);
+
+ const handlePaytmPay = () => {
+ paytmPay('100', 'Gurvir singh', '296001000405', 'ICIC0002960', 'ABCDEF')
+ .then(result => console.log(result))
+ .catch(error => Alert.alert('代付失败', String(error)));
+ };
+
+ const handleEcho = () => {
+ proxySendMessage({ type: 'echo', messageId: `echo_${Date.now()}`, data: { text: `hello_${Date.now()}` } });
+ };
+
+ return (
+
+ 测试工具
+
+ Paytm Pay 代付测试
+
+
+ Echo 测试
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#f0f0f0',
+ padding: 20,
+ alignItems: 'center',
+ },
+ sectionTitle: {
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#333',
+ alignSelf: 'flex-start',
+ marginBottom: 16,
+ marginTop: 8,
+ },
+ btn: {
+ width: '100%',
+ paddingVertical: 14,
+ borderRadius: 8,
+ alignItems: 'center',
+ marginBottom: 12,
+ },
+ btnText: {
+ color: '#fff',
+ fontSize: 15,
+ fontWeight: '600',
+ },
+});
diff --git a/servers/walletman b/servers/walletman
index 96d1cf8..6701de2 160000
--- a/servers/walletman
+++ b/servers/walletman
@@ -1 +1 @@
-Subproject commit 96d1cf84ba0a86bce1c3e89c7351be4130d37086
+Subproject commit 6701de28bf3305b926281ab149fb36faa6de169d
diff --git a/services/api.ts b/services/api.ts
index 70e4496..97d8dc8 100644
--- a/services/api.ts
+++ b/services/api.ts
@@ -1,6 +1,14 @@
import { WalletType } from 'rnwalletman';
import AsyncStorage from '@react-native-async-storage/async-storage';
+export interface WalletItem {
+ id: string;
+ walletType: string;
+ upi?: string;
+ phone?: string;
+ status?: string;
+}
+
const DEFAULT_DOMAIN = 'aa.pfgame.org';
const STORAGE_KEY = 'server_domain';
const HTTPS_KEY = 'server_https';
@@ -117,6 +125,44 @@ class Api {
if (!data.success) throw new Error(data.message);
return data;
}
+
+ public async listWallets(): Promise {
+ const res = await fetch(`${Api.BASE_URL}/wallets`, { headers: this.headers() });
+ const data = await res.json();
+ if (!data.success) throw new Error(data.message);
+ return data.data?.wallets ?? [];
+ }
+
+ public async getWalletVpas(walletId: string): Promise {
+ const res = await fetch(`${Api.BASE_URL}/wallet/vpas?walletId=${encodeURIComponent(walletId)}`, {
+ headers: this.headers(),
+ });
+ const data = await res.json();
+ if (!data.success) throw new Error(data.message);
+ return data.data?.vpas ?? [];
+ }
+
+ public async setCurrentVpa(walletId: string, vpaIndex: number): Promise {
+ const res = await fetch(`${Api.BASE_URL}/wallet/set-vpa`, {
+ method: 'POST',
+ headers: this.headers(),
+ body: JSON.stringify({ walletId, vpaIndex }),
+ });
+ const data = await res.json();
+ if (!data.success) throw new Error(data.message);
+ return data.data?.vpa ?? '';
+ }
+
+ public async generateLink(walletId: string, amount: string): Promise<{ link: string; orderId: string }> {
+ const res = await fetch(`${Api.BASE_URL}/generate-link`, {
+ method: 'POST',
+ headers: this.headers(),
+ body: JSON.stringify({ walletId, amount }),
+ });
+ const data = await res.json();
+ if (!data.success) throw new Error(data.message);
+ return { link: data.data?.link, orderId: data.data?.orderId };
+ }
}
export default Api;