Files
rnpay/screens/TestScreen.tsx
T
2026-07-29 22:41:33 +08:00

400 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useEffect, useMemo, useState } from 'react';
import {
Alert,
ActivityIndicator,
Modal,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import {
onProxyMessage,
proxySendMessage,
openPaytmPayToBank,
openPaytmPayToBank2,
openMobikwikPayToBank,
openPhonePePayToBank,
openFreechargePayToBank,
buildFreechargePayToBankDeeplink,
openAmazonPayPayToBank,
FreechargePersonalBind,
FreechargePersonalBindResult,
WalletType,
} from 'rnwalletman';
import Api, { loadServerDomain, saveServerDomain } from '../services/api';
const PROD_API = 'https://aa.pfgame.org';
type PayProvider = 'paytm' | 'paytm2' | 'mobikwik' | 'phonepe' | 'freecharge' | 'amazonpay';
type Payee = {
id: string;
label: string;
name: string;
account: string;
ifsc: string;
amount: string;
comment: string;
};
const PAYEES: Payee[] = [
{
id: 'harsh-psib',
label: 'Harshpreet · PSIB',
name: 'Harshpreet singh',
account: '01601000068180',
ifsc: 'PSIB0000160',
amount: '2',
comment: '66666',
},
{
id: 'harsh-cnrb',
label: 'Harshpreet · CNRB',
name: 'Harshpreet singh',
account: '110140729840',
ifsc: 'CNRB0016365',
amount: '12',
comment: 'payment',
},
{
id: 'anmol-cnrb',
label: 'Anmol · CNRB',
name: 'Anmol',
account: '5521101002938',
ifsc: 'CNRB0005521',
amount: '12',
comment: 'transfer',
},
{
id: 'fc-test',
label: 'Freecharge test · PSIB',
name: 'Test User',
account: '8284919464',
ifsc: 'PSIB0000160',
amount: '2',
comment: 'test transfer',
},
{
id: 'paytm2-demo',
label: 'Paytm deeplink demo',
name: 'Harshpreet singh',
account: '01601000068180',
ifsc: 'PSIB0000160',
amount: '3',
comment: '1234',
},
];
const PAY_PROVIDERS: { id: PayProvider; label: string; color: string }[] = [
{ id: 'paytm', label: 'Paytm', color: '#2ecc71' },
{ id: 'paytm2', label: 'Paytm DL', color: '#27ae60' },
{ id: 'mobikwik', label: 'Mobikwik', color: '#2ecc33' },
{ id: 'phonepe', label: 'PhonePe', color: '#5a2d9c' },
{ id: 'freecharge', label: 'Freecharge', color: '#5468db' },
{ id: 'amazonpay', label: 'Amazon Pay', color: '#ff9900' },
];
async function launchPay(provider: PayProvider, payee: Payee): Promise<boolean> {
const { name, account, ifsc, amount, comment } = payee;
switch (provider) {
case 'paytm':
return openPaytmPayToBank(name, account, ifsc, amount, comment);
case 'paytm2':
return openPaytmPayToBank2(name, account, ifsc, amount, comment);
case 'mobikwik':
return openMobikwikPayToBank(name, account, ifsc, amount);
case 'phonepe':
return openPhonePePayToBank(name, account, ifsc, amount, comment);
case 'freecharge':
return openFreechargePayToBank(name, account, ifsc, amount, comment);
case 'amazonpay':
return openAmazonPayPayToBank();
default:
return false;
}
}
export default function TestScreen() {
const [selectedPayeeId, setSelectedPayeeId] = useState(PAYEES[0].id);
const [selectedProvider, setSelectedProvider] = useState<PayProvider>('freecharge');
const [showFcAuth, setShowFcAuth] = useState(false);
const [apiBaseUrl, setApiBaseUrl] = useState('');
const [fcAuthResult, setFcAuthResult] = useState<FreechargePersonalBindResult | null>(null);
const selectedPayee = useMemo(
() => PAYEES.find(p => p.id === selectedPayeeId) ?? PAYEES[0],
[selectedPayeeId],
);
useEffect(() => {
(async () => {
await saveServerDomain('aa.pfgame.org', true);
await loadServerDomain();
setApiBaseUrl(Api.BASE_URL);
})();
const sub = onProxyMessage(msg => {
if (msg.type === 'echo') {
Alert.alert('Echo Response', JSON.stringify(msg.data));
}
});
return () => sub.remove();
}, []);
const handlePay = async () => {
try {
if (selectedProvider === 'freecharge') {
const deeplink = buildFreechargePayToBankDeeplink(
selectedPayee.account,
selectedPayee.ifsc,
);
console.log('[TestScreen] Freecharge deeplink:', deeplink);
}
const ok = await launchPay(selectedProvider, selectedPayee);
if (!ok) {
Alert.alert('Pay', 'Failed to open payment app');
}
} catch (e) {
Alert.alert('Transfer Failed', String(e));
}
};
const handleEcho = () => {
proxySendMessage({
type: 'echo',
messageId: `echo_${Date.now()}`,
data: { text: `hello_${Date.now()}` },
});
};
const handleFcAuthSuccess = async (result: FreechargePersonalBindResult) => {
setFcAuthResult(result);
setShowFcAuth(false);
try {
await Api.instance.register(
WalletType.FREECHARGE_PERSONAL,
{
type: WalletType.FREECHARGE_PERSONAL,
success: true,
mobile: result.mobile,
token: result.token,
userId: result.userId || result.imsId,
extend: { appVersion: result.appVersion || '' },
},
);
Alert.alert('Register OK', 'Freecharge 钱包已注册到服务端');
} catch (e) {
Alert.alert('Register failed', String(e));
}
};
return (
<ScrollView style={styles.container} contentContainerStyle={styles.content}>
<Text style={styles.serverUrl}>Server: {apiBaseUrl || PROD_API}</Text>
<Text style={styles.sectionTitle}>收款人</Text>
{PAYEES.map(payee => {
const active = payee.id === selectedPayeeId;
return (
<TouchableOpacity
key={payee.id}
style={[styles.payeeCard, active && styles.payeeCardActive]}
onPress={() => setSelectedPayeeId(payee.id)}
>
<Text style={[styles.payeeLabel, active && styles.payeeLabelActive]}>{payee.label}</Text>
<Text style={styles.payeeMeta}>
{payee.name} · {payee.account} · {payee.ifsc}
</Text>
<Text style={styles.payeeMeta}>{payee.amount} · {payee.comment}</Text>
</TouchableOpacity>
);
})}
<Text style={styles.sectionTitle}>支付方式</Text>
<View style={styles.providerRow}>
{PAY_PROVIDERS.map(p => {
const active = p.id === selectedProvider;
return (
<TouchableOpacity
key={p.id}
style={[styles.providerChip, active && { backgroundColor: p.color, borderColor: p.color }]}
onPress={() => setSelectedProvider(p.id)}
>
<Text style={[styles.providerChipText, active && styles.providerChipTextActive]}>
{p.label}
</Text>
</TouchableOpacity>
);
})}
</View>
<TouchableOpacity style={[styles.btn, { backgroundColor: '#e74c3c' }]} onPress={handlePay}>
<Text style={styles.btnText}>
付款 · {PAY_PROVIDERS.find(p => p.id === selectedProvider)?.label} {selectedPayee.label}
</Text>
</TouchableOpacity>
<Text style={styles.sectionTitle}>Freecharge Inject 授权</Text>
<TouchableOpacity
style={[styles.btn, { backgroundColor: '#5468db' }]}
onPress={() => setShowFcAuth(true)}
>
<Text style={styles.btnText}>打开 Inject 授权(官方 App WebView</Text>
</TouchableOpacity>
{fcAuthResult ? (
<View style={styles.resultBox}>
<Text style={styles.resultTitle}>最近授权</Text>
<Text style={styles.resultText}>mobile: {fcAuthResult.mobile}</Text>
<Text style={styles.resultText}>imsId: {fcAuthResult.imsId}</Text>
<Text style={styles.resultText}>
upi: {(fcAuthResult.vpas || []).map(v => v.vpa).join(', ')}
</Text>
</View>
) : null}
<TouchableOpacity style={[styles.btn, { backgroundColor: '#3498db' }]} onPress={handleEcho}>
<Text style={styles.btnText}>Echo Test</Text>
</TouchableOpacity>
<Modal
visible={showFcAuth}
transparent
animationType="fade"
statusBarTranslucent
onRequestClose={() => setShowFcAuth(false)}
>
<View style={styles.modalRoot}>
{apiBaseUrl ? (
<FreechargePersonalBind
apiBaseUrl={apiBaseUrl}
isDebug
userId={Api.instance.getUserId()}
processString="Waiting for Freecharge authorization…"
onClose={() => setShowFcAuth(false)}
onSuccess={handleFcAuthSuccess}
onError={msg => {
Alert.alert('Freecharge Auth Failed', msg);
}}
/>
) : (
<View style={styles.loadingBox}>
<ActivityIndicator size="large" color="#fff" />
<Text style={[styles.btnText, { marginTop: 12 }]}>Loading server URL</Text>
</View>
)}
</View>
</Modal>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f0f0f0',
},
content: {
padding: 16,
paddingBottom: 40,
},
serverUrl: {
fontSize: 12,
color: '#1565c0',
marginBottom: 8,
fontWeight: '600',
},
sectionTitle: {
fontSize: 16,
fontWeight: '600',
color: '#333',
marginBottom: 10,
marginTop: 8,
},
payeeCard: {
backgroundColor: '#fff',
borderRadius: 10,
padding: 14,
marginBottom: 8,
borderWidth: 2,
borderColor: 'transparent',
},
payeeCardActive: {
borderColor: '#3498db',
backgroundColor: '#eef6ff',
},
payeeLabel: {
fontSize: 15,
fontWeight: '600',
color: '#333',
},
payeeLabelActive: {
color: '#1565c0',
},
payeeMeta: {
fontSize: 12,
color: '#666',
marginTop: 4,
},
providerRow: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 8,
marginBottom: 16,
},
providerChip: {
paddingHorizontal: 12,
paddingVertical: 8,
borderRadius: 20,
borderWidth: 1,
borderColor: '#ccc',
backgroundColor: '#fff',
},
providerChipText: {
fontSize: 13,
color: '#444',
fontWeight: '500',
},
providerChipTextActive: {
color: '#fff',
},
btn: {
width: '100%',
paddingVertical: 14,
borderRadius: 8,
alignItems: 'center',
marginBottom: 12,
},
btnText: {
color: '#fff',
fontSize: 15,
fontWeight: '600',
textAlign: 'center',
},
resultBox: {
backgroundColor: '#fff',
borderRadius: 10,
padding: 12,
marginBottom: 12,
},
resultTitle: {
fontWeight: '600',
marginBottom: 6,
color: '#333',
},
resultText: {
fontSize: 12,
color: '#555',
marginBottom: 2,
},
loadingBox: {
flex: 1,
backgroundColor: 'rgba(0,0,0,0.7)',
justifyContent: 'center',
alignItems: 'center',
},
modalRoot: {
flex: 1,
},
});