new mobikwik

This commit is contained in:
2026-07-29 22:41:33 +08:00
parent 14d0ef42b3
commit 4086b64fc0
8 changed files with 835 additions and 69 deletions
+340 -65
View File
@@ -1,5 +1,14 @@
import React, { useEffect, useRef } from 'react';
import { Alert, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import React, { useEffect, useMemo, useState } from 'react';
import {
Alert,
ActivityIndicator,
Modal,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import {
onProxyMessage,
proxySendMessage,
@@ -8,89 +17,275 @@ import {
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 subRef = useRef<ReturnType<typeof onProxyMessage> | null>(null);
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(() => {
subRef.current = onProxyMessage((msg) => {
(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 () => {
subRef.current?.remove();
};
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()}` } });
proxySendMessage({
type: 'echo',
messageId: `echo_${Date.now()}`,
data: { text: `hello_${Date.now()}` },
});
};
const handlePaytmPayToBank = () => {
openPaytmPayToBank('Harshpreet singh', '01601000068180', 'PSIB0000160', '2', '66666')
.then(result => console.log('Paytm Pay To Bank', result ? 'Success' : 'Failed'))
.catch(error => Alert.alert('Transfer Failed', String(error)));
};
const handlePaytmPayToBank2 = () => {
openPaytmPayToBank2('Harshpreet singh', '01601000068180', 'PSIB0000160', '3', '1234')
.then((result: boolean) => console.log('Paytm Pay To Bank2', result ? 'Success' : 'Failed'))
.catch((error: unknown) => Alert.alert('Transfer Failed', String(error)));
};
const handleMobikwikPayToBank = () => {
openMobikwikPayToBank('Anmol', '5521101002938', 'CNRB0005521', '12')
.then(result => console.log('Mobikwik Pay To Bank', result ? 'Success' : 'Failed'))
.catch(err => Alert.alert('Error', String(err)));
};
const handlePhonePePayToBank = () => {
openPhonePePayToBank('Harshpreet singh', '110140729840', 'CNRB0016365', '12', 'payment')
.then((ok: boolean) => console.log('PhonePe Pay To Bank', ok ? 'opened' : 'failed'))
.catch((err: unknown) => Alert.alert('Error', String(err)));
};
const handleFreechargePayToBank = () => {
openFreechargePayToBank('', '8284919464', 'PSIB0000160', '2', 'test transfer')
.then((result: boolean) => console.log('Freecharge Pay To Bank', result ? 'Success' : 'Failed'))
.catch((err: unknown) => Alert.alert('Error', String(err)));
};
const handleAmazonPayPayToBank = () => {
openAmazonPayPayToBank()
.then((result: boolean) => console.log('Amazon Pay To Bank', result ? 'Success' : 'Failed'))
.catch((err: unknown) => Alert.alert('Error', String(err)));
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 (
<View style={styles.container}>
<Text style={styles.sectionTitle}>Test Tools</Text>
<ScrollView style={styles.container} contentContainerStyle={styles.content}>
<Text style={styles.serverUrl}>Server: {apiBaseUrl || PROD_API}</Text>
<TouchableOpacity style={[styles.btn, { backgroundColor: '#2ecc71' }]} onPress={handlePaytmPayToBank}>
<Text style={styles.btnText}>Paytm Pay To Bank Test</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>
<TouchableOpacity style={[styles.btn, { backgroundColor: '#27ae60' }]} onPress={handlePaytmPayToBank2}>
<Text style={styles.btnText}>Paytm Pay To Bank2 (deeplink)</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.btn, { backgroundColor: '#2ecc33' }]} onPress={handleMobikwikPayToBank}>
<Text style={styles.btnText}>Mobikwik Pay To Bank Test</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.btn, { backgroundColor: '#5a2d9c' }]} onPress={handlePhonePePayToBank}>
<Text style={styles.btnText}>PhonePe Pay To Bank Test</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.btn, { backgroundColor: '#5468db' }]} onPress={handleFreechargePayToBank}>
<Text style={styles.btnText}>Freecharge Pay To Bank Test</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.btn, { backgroundColor: '#ff9900' }]} onPress={handleAmazonPayPayToBank}>
<Text style={styles.btnText}>Amazon Pay To Bank Test</Text>
<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>
</View>
<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>
);
}
@@ -98,17 +293,71 @@ const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f0f0f0',
padding: 20,
alignItems: 'center',
},
content: {
padding: 16,
paddingBottom: 40,
},
serverUrl: {
fontSize: 12,
color: '#1565c0',
marginBottom: 8,
fontWeight: '600',
},
sectionTitle: {
fontSize: 16,
fontWeight: '600',
color: '#333',
alignSelf: 'flex-start',
marginBottom: 16,
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,
@@ -120,5 +369,31 @@ const styles = StyleSheet.create({
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,
},
});