400 lines
12 KiB
TypeScript
400 lines
12 KiB
TypeScript
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,
|
||
},
|
||
});
|