Compare commits
10 Commits
2a6fd5149f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c42d89d585 | |||
| cc6a4443ee | |||
| 9477c1fed0 | |||
| a113805b51 | |||
| 2f4241951d | |||
| 57928cd97d | |||
| 0db8a9254a | |||
| d0f60b5ece | |||
| bb215fd492 | |||
| 01e597ac93 |
1
android/.idea/vcs.xml
generated
1
android/.idea/vcs.xml
generated
@@ -7,6 +7,7 @@
|
|||||||
<mapping directory="$PROJECT_DIR$/../libs/rnwalletman" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$/../libs/rnwalletman" vcs="Git" />
|
||||||
<mapping directory="$PROJECT_DIR$/../node_modules/rnauto" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$/../node_modules/rnauto" vcs="Git" />
|
||||||
<mapping directory="$PROJECT_DIR$/../node_modules/rnwalletman" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$/../node_modules/rnwalletman" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/../servers/usdtman" vcs="Git" />
|
||||||
<mapping directory="$PROJECT_DIR$/../servers/walletman" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$/../servers/walletman" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".MainApplication"
|
android:name=".MainApplication"
|
||||||
|
|||||||
206
components/OTPBindUI.tsx
Normal file
206
components/OTPBindUI.tsx
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, TextInput, TouchableOpacity, Text, StyleSheet, ActivityIndicator } from 'react-native';
|
||||||
|
import { WalletType } from 'rnwalletman';
|
||||||
|
import { useOTPBind } from '../hooks/useOTPBind';
|
||||||
|
|
||||||
|
interface OTPBindUIProps {
|
||||||
|
walletType: WalletType;
|
||||||
|
title: string;
|
||||||
|
otpLength?: number;
|
||||||
|
mobileLength?: number;
|
||||||
|
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
|
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
|
onSuccess: (result: any) => void;
|
||||||
|
onError: (error: string) => void;
|
||||||
|
isDebug: boolean;
|
||||||
|
additionalParams?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OTPBindUI: React.FC<OTPBindUIProps> = ({
|
||||||
|
walletType,
|
||||||
|
title,
|
||||||
|
otpLength = 6,
|
||||||
|
mobileLength = 10,
|
||||||
|
onRequestOTP,
|
||||||
|
onVerifyOTP,
|
||||||
|
onSuccess,
|
||||||
|
onError,
|
||||||
|
isDebug,
|
||||||
|
additionalParams = {},
|
||||||
|
}) => {
|
||||||
|
const [state, actions] = useOTPBind(
|
||||||
|
walletType,
|
||||||
|
{
|
||||||
|
onRequestOTP,
|
||||||
|
onVerifyOTP,
|
||||||
|
onSuccess,
|
||||||
|
onError,
|
||||||
|
isDebug,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
otpLength,
|
||||||
|
mobileLength,
|
||||||
|
additionalParams,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (state.step === 'processing') {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<ActivityIndicator size="large" color="#fff" />
|
||||||
|
<Text style={styles.processingText}>处理中...</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<View style={styles.form}>
|
||||||
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
|
||||||
|
{state.step === 'mobile' && (
|
||||||
|
<>
|
||||||
|
{state.errorMessage ? (
|
||||||
|
<Text style={styles.errorText}>{state.errorMessage}</Text>
|
||||||
|
) : null}
|
||||||
|
<TextInput
|
||||||
|
style={[styles.input, state.errorMessage ? styles.inputError : {}]}
|
||||||
|
placeholder="请输入手机号"
|
||||||
|
placeholderTextColor="#999"
|
||||||
|
keyboardType="phone-pad"
|
||||||
|
maxLength={mobileLength}
|
||||||
|
value={state.mobile}
|
||||||
|
onChangeText={(text) => {
|
||||||
|
actions.setMobile(text);
|
||||||
|
if (state.errorMessage) actions.clearError();
|
||||||
|
}}
|
||||||
|
editable={!state.loading}
|
||||||
|
/>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.button, state.loading && styles.buttonDisabled]}
|
||||||
|
onPress={actions.requestOTP}
|
||||||
|
disabled={state.loading}
|
||||||
|
>
|
||||||
|
{state.loading ? (
|
||||||
|
<ActivityIndicator color="#fff" />
|
||||||
|
) : (
|
||||||
|
<Text style={styles.buttonText}>获取验证码</Text>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{state.step === 'otp' && (
|
||||||
|
<>
|
||||||
|
<Text style={styles.hint}>验证码已发送至 {state.mobile}</Text>
|
||||||
|
{state.errorMessage ? (
|
||||||
|
<Text style={styles.errorText}>{state.errorMessage}</Text>
|
||||||
|
) : null}
|
||||||
|
<TextInput
|
||||||
|
style={[styles.input, state.errorMessage ? styles.inputError : {}]}
|
||||||
|
placeholder={`请输入 ${otpLength} 位验证码`}
|
||||||
|
placeholderTextColor="#999"
|
||||||
|
keyboardType="number-pad"
|
||||||
|
maxLength={otpLength}
|
||||||
|
value={state.otp}
|
||||||
|
onChangeText={(text) => {
|
||||||
|
actions.setOtp(text);
|
||||||
|
if (state.errorMessage) actions.clearError();
|
||||||
|
}}
|
||||||
|
editable={!state.loading}
|
||||||
|
/>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.button, state.loading && styles.buttonDisabled]}
|
||||||
|
onPress={actions.verifyOTP}
|
||||||
|
disabled={state.loading}
|
||||||
|
>
|
||||||
|
{state.loading ? (
|
||||||
|
<ActivityIndicator color="#fff" />
|
||||||
|
) : (
|
||||||
|
<Text style={styles.buttonText}>验证并绑定</Text>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.linkButton}
|
||||||
|
onPress={actions.resetToMobile}
|
||||||
|
disabled={state.loading}
|
||||||
|
>
|
||||||
|
<Text style={styles.linkText}>重新输入手机号</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.8)',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
width: '80%',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderRadius: 10,
|
||||||
|
padding: 20,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
textAlign: 'center',
|
||||||
|
marginBottom: 20,
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#ddd',
|
||||||
|
borderRadius: 5,
|
||||||
|
padding: 12,
|
||||||
|
fontSize: 16,
|
||||||
|
marginBottom: 15,
|
||||||
|
},
|
||||||
|
inputError: {
|
||||||
|
borderColor: '#ff3b30',
|
||||||
|
},
|
||||||
|
errorText: {
|
||||||
|
color: '#ff3b30',
|
||||||
|
fontSize: 14,
|
||||||
|
marginBottom: 10,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
backgroundColor: '#007AFF',
|
||||||
|
borderRadius: 5,
|
||||||
|
padding: 15,
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
buttonDisabled: {
|
||||||
|
backgroundColor: '#ccc',
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
linkButton: {
|
||||||
|
marginTop: 10,
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
linkText: {
|
||||||
|
color: '#007AFF',
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
hint: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#666',
|
||||||
|
marginBottom: 10,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
processingText: {
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 16,
|
||||||
|
marginTop: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
101
components/WalletBindComponents.tsx
Normal file
101
components/WalletBindComponents.tsx
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { WalletType, FreechargePersonalBindResult, MobikwikPersonalBindResult, PaytmPersonalBindResult, PhonePePersonalBindResult } from 'rnwalletman';
|
||||||
|
import { OTPBindUI } from './OTPBindUI';
|
||||||
|
|
||||||
|
export class FreeChargeBind extends Component<{
|
||||||
|
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
|
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
|
onSuccess: (result: FreechargePersonalBindResult) => void;
|
||||||
|
onError: (error: string) => void;
|
||||||
|
isDebug: boolean;
|
||||||
|
}> {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<OTPBindUI
|
||||||
|
walletType={WalletType.FREECHARGE_PERSONAL}
|
||||||
|
title="Freecharge 绑定"
|
||||||
|
otpLength={4}
|
||||||
|
onRequestOTP={this.props.onRequestOTP}
|
||||||
|
onVerifyOTP={this.props.onVerifyOTP}
|
||||||
|
onSuccess={this.props.onSuccess}
|
||||||
|
onError={this.props.onError}
|
||||||
|
isDebug={this.props.isDebug}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MobikwikOTPBind extends Component<{
|
||||||
|
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
|
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
|
onSuccess: (result: MobikwikPersonalBindResult) => void;
|
||||||
|
onError: (error: string) => void;
|
||||||
|
isDebug: boolean;
|
||||||
|
deviceId: string;
|
||||||
|
tuneUserId: string;
|
||||||
|
}> {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<OTPBindUI
|
||||||
|
walletType={WalletType.MOBIKWIK_PERSONAL}
|
||||||
|
title="Mobikwik 绑定"
|
||||||
|
otpLength={6}
|
||||||
|
onRequestOTP={this.props.onRequestOTP}
|
||||||
|
onVerifyOTP={this.props.onVerifyOTP}
|
||||||
|
onSuccess={this.props.onSuccess}
|
||||||
|
onError={this.props.onError}
|
||||||
|
isDebug={this.props.isDebug}
|
||||||
|
additionalParams={{
|
||||||
|
deviceId: this.props.deviceId,
|
||||||
|
tuneUserId: this.props.tuneUserId,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PayTmPersonalOTPBind extends Component<{
|
||||||
|
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
|
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
|
onSuccess: (result: PaytmPersonalBindResult) => void;
|
||||||
|
onError: (error: string) => void;
|
||||||
|
isDebug: boolean;
|
||||||
|
}> {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<OTPBindUI
|
||||||
|
walletType={WalletType.PAYTM_PERSONAL}
|
||||||
|
title="Paytm 绑定"
|
||||||
|
otpLength={6}
|
||||||
|
onRequestOTP={this.props.onRequestOTP}
|
||||||
|
onVerifyOTP={this.props.onVerifyOTP}
|
||||||
|
onSuccess={this.props.onSuccess}
|
||||||
|
onError={this.props.onError}
|
||||||
|
isDebug={this.props.isDebug}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PhonePePersonalOTPBind extends Component<{
|
||||||
|
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
|
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
|
onSuccess: (result: PhonePePersonalBindResult) => void;
|
||||||
|
onError: (error: string) => void;
|
||||||
|
isDebug: boolean;
|
||||||
|
}> {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<OTPBindUI
|
||||||
|
walletType={WalletType.PHONEPE_PERSONAL}
|
||||||
|
title="PhonePe 绑定"
|
||||||
|
otpLength={5}
|
||||||
|
onRequestOTP={this.props.onRequestOTP}
|
||||||
|
onVerifyOTP={this.props.onVerifyOTP}
|
||||||
|
onSuccess={this.props.onSuccess}
|
||||||
|
onError={this.props.onError}
|
||||||
|
isDebug={this.props.isDebug}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
151
hooks/useOTPBind.ts
Normal file
151
hooks/useOTPBind.ts
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { WalletType } from 'rnwalletman';
|
||||||
|
|
||||||
|
export interface OTPBindCallbacks {
|
||||||
|
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
|
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||||
|
onSuccess: (result: any) => void;
|
||||||
|
onError: (error: string) => void;
|
||||||
|
isDebug?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OTPBindState {
|
||||||
|
mobile: string;
|
||||||
|
otp: string;
|
||||||
|
step: 'mobile' | 'otp' | 'processing';
|
||||||
|
loading: boolean;
|
||||||
|
otpData: any;
|
||||||
|
errorMessage: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OTPBindActions {
|
||||||
|
setMobile: (mobile: string) => void;
|
||||||
|
setOtp: (otp: string) => void;
|
||||||
|
requestOTP: () => Promise<void>;
|
||||||
|
verifyOTP: () => Promise<void>;
|
||||||
|
resetToMobile: () => void;
|
||||||
|
clearError: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useOTPBind(
|
||||||
|
walletType: WalletType,
|
||||||
|
callbacks: OTPBindCallbacks,
|
||||||
|
config?: {
|
||||||
|
otpLength?: number;
|
||||||
|
mobileLength?: number;
|
||||||
|
additionalParams?: any;
|
||||||
|
}
|
||||||
|
): [OTPBindState, OTPBindActions] {
|
||||||
|
const [mobile, setMobile] = useState('');
|
||||||
|
const [otp, setOtp] = useState('');
|
||||||
|
const [step, setStep] = useState<'mobile' | 'otp' | 'processing'>('mobile');
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [otpData, setOtpData] = useState<any>(null);
|
||||||
|
const [errorMessage, setErrorMessage] = useState('');
|
||||||
|
|
||||||
|
const { onRequestOTP, onVerifyOTP, onSuccess, onError, isDebug = false } = callbacks;
|
||||||
|
const { otpLength = 6, mobileLength = 10, additionalParams = {} } = config || {};
|
||||||
|
|
||||||
|
const clearError = () => setErrorMessage('');
|
||||||
|
|
||||||
|
const log = (...args: any[]) => {
|
||||||
|
if (isDebug) console.log(`[${walletType}]`, ...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
const error = (...args: any[]) => {
|
||||||
|
if (isDebug) console.error(`[${walletType}]`, ...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
const requestOTP = async () => {
|
||||||
|
if (!mobile || mobile.length !== mobileLength) {
|
||||||
|
const msg = 'Invalid mobile number';
|
||||||
|
setErrorMessage(msg);
|
||||||
|
onError(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
setErrorMessage('');
|
||||||
|
log('Requesting OTP for:', mobile);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await onRequestOTP(walletType, {
|
||||||
|
mobile,
|
||||||
|
...additionalParams,
|
||||||
|
});
|
||||||
|
log('OTP response:', response);
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setOtpData(response.data);
|
||||||
|
setStep('otp');
|
||||||
|
setErrorMessage('');
|
||||||
|
} else {
|
||||||
|
error('OTP request failed:', response.message);
|
||||||
|
const msg = response.message || 'Failed to request OTP';
|
||||||
|
setErrorMessage(msg);
|
||||||
|
onError(msg);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
error('Request OTP error:', e);
|
||||||
|
const msg = e instanceof Error ? e.message : 'Failed to request OTP';
|
||||||
|
setErrorMessage(msg);
|
||||||
|
onError(msg);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const verifyOTP = async () => {
|
||||||
|
if (!otp || otp.length !== otpLength) {
|
||||||
|
const msg = `请输入 ${otpLength} 位验证码`;
|
||||||
|
setErrorMessage(msg);
|
||||||
|
onError(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
setErrorMessage('');
|
||||||
|
setStep('processing');
|
||||||
|
log('Verifying OTP:', otp);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await onVerifyOTP(walletType, {
|
||||||
|
mobile,
|
||||||
|
otp,
|
||||||
|
...additionalParams,
|
||||||
|
...(otpData || {}),
|
||||||
|
});
|
||||||
|
log('Verify response:', response);
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setErrorMessage('');
|
||||||
|
onSuccess(response.data);
|
||||||
|
} else {
|
||||||
|
error('Verify failed:', response.message);
|
||||||
|
const msg = response.message || 'Failed to verify OTP';
|
||||||
|
setStep('otp');
|
||||||
|
setErrorMessage(msg);
|
||||||
|
onError(msg);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
error('Verify OTP error:', e);
|
||||||
|
const msg = e instanceof Error ? e.message : 'Failed to verify OTP';
|
||||||
|
setStep('otp');
|
||||||
|
setErrorMessage(msg);
|
||||||
|
onError(msg);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetToMobile = () => {
|
||||||
|
setStep('mobile');
|
||||||
|
setOtp('');
|
||||||
|
setErrorMessage('');
|
||||||
|
};
|
||||||
|
|
||||||
|
return [
|
||||||
|
{ mobile, otp, step, loading, otpData, errorMessage },
|
||||||
|
{ setMobile, setOtp, requestOTP, verifyOTP, resetToMobile, clearError },
|
||||||
|
];
|
||||||
|
}
|
||||||
Submodule libs/rnwalletman updated: ad66622161...9e984f0edd
@@ -7,7 +7,8 @@
|
|||||||
"ios": "react-native run-ios",
|
"ios": "react-native run-ios",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"start": "react-native start",
|
"start": "react-native start",
|
||||||
"test": "jest"
|
"test": "jest",
|
||||||
|
"postinstall": "patch-package"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-native-cookies/cookies": "^6.2.1",
|
"@react-native-cookies/cookies": "^6.2.1",
|
||||||
@@ -15,6 +16,7 @@
|
|||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-native": "0.72.10",
|
"react-native": "0.72.10",
|
||||||
|
"react-native-background-actions": "^4.0.1",
|
||||||
"react-native-device-info": "14.0.4",
|
"react-native-device-info": "14.0.4",
|
||||||
"react-native-fs": "^2.20.0",
|
"react-native-fs": "^2.20.0",
|
||||||
"react-native-tcp-socket": "^6.4.1",
|
"react-native-tcp-socket": "^6.4.1",
|
||||||
@@ -35,6 +37,7 @@
|
|||||||
"eslint": "^8.19.0",
|
"eslint": "^8.19.0",
|
||||||
"jest": "^29.2.1",
|
"jest": "^29.2.1",
|
||||||
"metro-react-native-babel-preset": "0.76.8",
|
"metro-react-native-babel-preset": "0.76.8",
|
||||||
|
"patch-package": "^8.0.1",
|
||||||
"prettier": "^2.4.1",
|
"prettier": "^2.4.1",
|
||||||
"react-test-renderer": "18.2.0",
|
"react-test-renderer": "18.2.0",
|
||||||
"typescript": "4.8.4"
|
"typescript": "4.8.4"
|
||||||
|
|||||||
15
patches/react-native-background-actions+4.0.1.patch
Normal file
15
patches/react-native-background-actions+4.0.1.patch
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
diff --git a/node_modules/react-native-background-actions/android/src/main/java/com/asterinet/react/bgactions/RNBackgroundActionsTask.java b/node_modules/react-native-background-actions/android/src/main/java/com/asterinet/react/bgactions/RNBackgroundActionsTask.java
|
||||||
|
index 9900fc0..d810b1c 100644
|
||||||
|
--- a/node_modules/react-native-background-actions/android/src/main/java/com/asterinet/react/bgactions/RNBackgroundActionsTask.java
|
||||||
|
+++ b/node_modules/react-native-background-actions/android/src/main/java/com/asterinet/react/bgactions/RNBackgroundActionsTask.java
|
||||||
|
@@ -41,8 +41,8 @@ final public class RNBackgroundActionsTask extends HeadlessJsTaskService {
|
||||||
|
notificationIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER);
|
||||||
|
}
|
||||||
|
final PendingIntent contentIntent;
|
||||||
|
- if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
|
- contentIntent = PendingIntent.getActivity(context,0, notificationIntent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT);
|
||||||
|
+ if (Build.VERSION.SDK_INT >= 34) { // Android 14 (UPSIDE_DOWN_CAKE)
|
||||||
|
+ contentIntent = PendingIntent.getActivity(context,0, notificationIntent, PendingIntent.FLAG_MUTABLE | 0x01000000); // FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_MUTABLE);
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
Submodule servers/walletman updated: aa50a6588d...d85e65d275
84
services/api.ts
Normal file
84
services/api.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { WalletType } from 'rnwalletman';
|
||||||
|
|
||||||
|
const DOMAIN = '192.168.0.101:16000';
|
||||||
|
const BASE_URL = `http://${DOMAIN}`;
|
||||||
|
const WS_URL = `ws://${DOMAIN}/ws`;
|
||||||
|
|
||||||
|
class Api {
|
||||||
|
public static readonly BASE_URL = BASE_URL;
|
||||||
|
public static readonly WS_URL = WS_URL;
|
||||||
|
private static _instance: Api | null = null;
|
||||||
|
private userId: number = 0;
|
||||||
|
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
public setUserId(userId: number) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getUserId(): number {
|
||||||
|
return this.userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static get instance() {
|
||||||
|
if (Api._instance === null) {
|
||||||
|
Api._instance = new Api();
|
||||||
|
}
|
||||||
|
return Api._instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private headers(): Record<string, string> {
|
||||||
|
const h: Record<string, string> = { 'Content-Type': 'application/json' };
|
||||||
|
if (this.userId > 0) {
|
||||||
|
h['X-User-ID'] = String(this.userId);
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async login(username: string, password: string): Promise<number> {
|
||||||
|
const res = await fetch(`${Api.BASE_URL}/login`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ username, password }),
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (!data.success) throw new Error(data.message);
|
||||||
|
this.userId = data.data.userId;
|
||||||
|
return this.userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async register(walletType: WalletType, params: any) {
|
||||||
|
const res = await fetch(`${Api.BASE_URL}/register`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: this.headers(),
|
||||||
|
body: JSON.stringify({ walletType, params }),
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (!data.success) throw new Error(data.message);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async requestOTP(walletType: WalletType, mobile: string, params: any = {}) {
|
||||||
|
const res = await fetch(`${Api.BASE_URL}/request-otp`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: this.headers(),
|
||||||
|
body: JSON.stringify({ walletType, mobile, ...params }),
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (!data.success) throw new Error(data.message);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async verifyOTP(walletType: WalletType, mobile: string, otp: string, params: any = {}) {
|
||||||
|
const res = await fetch(`${Api.BASE_URL}/verify-otp`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: this.headers(),
|
||||||
|
body: JSON.stringify({ walletType, mobile, otp, params }),
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (!data.success) throw new Error(data.message);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Api;
|
||||||
45
styles.ts
Normal file
45
styles.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
export const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
padding: 10,
|
||||||
|
backgroundColor: "lightblue",
|
||||||
|
borderRadius: 5,
|
||||||
|
width: 200,
|
||||||
|
height: 55,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
modal: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
modalContent: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
bindButton: {
|
||||||
|
marginTop: 10,
|
||||||
|
marginBottom: 10,
|
||||||
|
backgroundColor: "#007AFF",
|
||||||
|
borderRadius: 5,
|
||||||
|
width: "90%",
|
||||||
|
height: 45,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
},
|
||||||
|
bindButtonText: {
|
||||||
|
fontSize: 14,
|
||||||
|
// fontWeight: "bold",
|
||||||
|
color: "#fff",
|
||||||
|
},
|
||||||
|
});
|
||||||
25
types.ts
Normal file
25
types.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
export interface AppProps {}
|
||||||
|
|
||||||
|
export interface WalletmanAppState {
|
||||||
|
/* Paytm Personal */
|
||||||
|
showPaytmPersonalBind: boolean;
|
||||||
|
paytmPersonalBindType: 'otpMode' | 'tokenMode';
|
||||||
|
showPaytmBusinessBind: boolean;
|
||||||
|
|
||||||
|
/* PhonePe Personal */
|
||||||
|
showPhonePePersonalBind: boolean;
|
||||||
|
phonePePersonalBindType: 'otpMode' | 'tokenMode';
|
||||||
|
showPhonePeBusinessBind: boolean;
|
||||||
|
|
||||||
|
/* GooglePay Business */
|
||||||
|
showGooglePayBusinessBind: boolean;
|
||||||
|
|
||||||
|
/* BharatPe Business */
|
||||||
|
showBharatPeBusinessBind: boolean;
|
||||||
|
|
||||||
|
/* Mobikwik Personal */
|
||||||
|
showMobikwikPersonalBind: boolean;
|
||||||
|
|
||||||
|
/* Freecharge Personal */
|
||||||
|
showFreechargePersonalBind: boolean;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user