feat: implement order status tracking component and wallet management infrastructure
This commit is contained in:
parent
8c443d76eb
commit
eecbf9484c
@ -8,3 +8,4 @@ export * from './customerDetailsApi';
|
|||||||
export * from './orderApi';
|
export * from './orderApi';
|
||||||
export * from './offerApi';
|
export * from './offerApi';
|
||||||
export * from './reviewApi';
|
export * from './reviewApi';
|
||||||
|
export * from './walletApi';
|
||||||
15
app/api/walletApi.ts
Normal file
15
app/api/walletApi.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { WalletTopUpPaymentSessionResponse } from "@interfaces";
|
||||||
|
import { apiClient } from "@services";
|
||||||
|
|
||||||
|
export interface TopupPayload {
|
||||||
|
amount: number,
|
||||||
|
paymentMethod: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initializeWalletTopUpApi = async (
|
||||||
|
payload: TopupPayload): Promise<WalletTopUpPaymentSessionResponse> => {
|
||||||
|
return await apiClient.post<WalletTopUpPaymentSessionResponse>(
|
||||||
|
'/wallets/topup',
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -207,8 +207,7 @@ export const OrderStatusTimeline: React.FC<OrderStatusTimelineProps> = ({
|
|||||||
];
|
];
|
||||||
|
|
||||||
const showDriver = [
|
const showDriver = [
|
||||||
'OUT_FOR_DELIVERY',
|
'OUT_FOR_DELIVERY'
|
||||||
'DELIVERED',
|
|
||||||
].includes(currentStatus);
|
].includes(currentStatus);
|
||||||
const currentIndex = STATUS_FLOW.indexOf(currentStatus);
|
const currentIndex = STATUS_FLOW.indexOf(currentStatus);
|
||||||
|
|
||||||
|
|||||||
102
app/features/screens/walletScreen/hooks/useTopUp.ts
Normal file
102
app/features/screens/walletScreen/hooks/useTopUp.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { useState, useCallback } from 'react';
|
||||||
|
import { Alert } from 'react-native';
|
||||||
|
import { useStripe } from '@stripe/stripe-react-native';
|
||||||
|
import { PlaceOrderResponse, WalletTopUpPaymentSessionResponse } from '@interfaces';
|
||||||
|
import { verifyPaymentApi } from '@api';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom hook that encapsulates the entire Stripe card payment flow:
|
||||||
|
* 1. Extract session info from the place-order response
|
||||||
|
* 2. Initialize the Stripe Payment Sheet
|
||||||
|
* 3. Present the Payment Sheet to the user
|
||||||
|
* 4. Verify the payment on the backend via /payments/process
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* - processCardPayment: async function to run the full flow
|
||||||
|
* - isPaymentProcessing: loading flag for UI
|
||||||
|
*/
|
||||||
|
export const useTopUp = () => {
|
||||||
|
const { initPaymentSheet, presentPaymentSheet } = useStripe();
|
||||||
|
const [isPaymentProcessing, setIsPaymentProcessing] = useState(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the complete Stripe card payment flow.
|
||||||
|
* @param response – the PlaceOrderResponse from the backend (contains checkoutSessions)
|
||||||
|
* @returns `true` if payment was verified successfully, `false` otherwise
|
||||||
|
*/
|
||||||
|
const topUpCardPayment = useCallback(
|
||||||
|
async (response: WalletTopUpPaymentSessionResponse): Promise<boolean> => {
|
||||||
|
try {
|
||||||
|
setIsPaymentProcessing(true);
|
||||||
|
|
||||||
|
// ── 1. Extract session details ─────────────────────────────────────
|
||||||
|
// const session = response.checkoutSessions?.[0];
|
||||||
|
// console.log('session', session);
|
||||||
|
const clientSecret = response?.gatewayToken;
|
||||||
|
// console.log('clientSecret', clientSecret);
|
||||||
|
const gatewayOrderId = response?.gatewayOrderId;
|
||||||
|
// console.log('gatewayOrderId', gatewayOrderId);
|
||||||
|
|
||||||
|
if (!clientSecret) {
|
||||||
|
Alert.alert('Error', 'Payment session token not found.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 2. Initialize Payment Sheet ────────────────────────────────────
|
||||||
|
const { error: initError } = await initPaymentSheet({
|
||||||
|
paymentIntentClientSecret: clientSecret,
|
||||||
|
merchantDisplayName: 'SG Delivery',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (initError) {
|
||||||
|
Alert.alert('Error', `Stripe init failed: ${initError.message}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 3. Present Payment Sheet ───────────────────────────────────────
|
||||||
|
const { error: presentError } = await presentPaymentSheet();
|
||||||
|
|
||||||
|
if (presentError) {
|
||||||
|
if (presentError.code === 'Canceled') {
|
||||||
|
Alert.alert(
|
||||||
|
'Payment Cancelled',
|
||||||
|
'You cancelled the payment process.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(presentError.message);
|
||||||
|
Alert.alert('Payment Error', presentError.message);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 4. Verify payment on backend ───────────────────────────────────
|
||||||
|
const verifyRes = await verifyPaymentApi({
|
||||||
|
gatewayOrderId: gatewayOrderId || '',
|
||||||
|
gatewayPaymentId: gatewayOrderId || '',
|
||||||
|
gatewaySignature: 'stripe_signature_verified',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (verifyRes.success) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Alert.alert(
|
||||||
|
'Verification Failed',
|
||||||
|
verifyRes.message || 'Unable to confirm payment.',
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
Alert.alert(
|
||||||
|
'Payment Error',
|
||||||
|
error?.message || 'Something went wrong during payment.',
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
setIsPaymentProcessing(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[initPaymentSheet, presentPaymentSheet],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { topUpCardPayment, isPaymentProcessing };
|
||||||
|
};
|
||||||
@ -1,17 +1,19 @@
|
|||||||
import { WalletTransaction } from '@interfaces';
|
import { WalletTopUpPaymentSessionResponse, WalletTransaction } from '@interfaces';
|
||||||
import { createReducer } from '@reduxjs/toolkit';
|
import { createReducer } from '@reduxjs/toolkit';
|
||||||
import { getAllWalletTransactions } from './thunk';
|
import { getAllWalletTransactions, initializeWalletTopUpThunk } from './thunk';
|
||||||
|
|
||||||
export interface walletState {
|
export interface walletState {
|
||||||
walletTransactions: WalletTransaction[];
|
walletTransactions: WalletTransaction[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
|
walletTopUpPaymentSessionResponse: WalletTopUpPaymentSessionResponse | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: walletState = {
|
const initialState: walletState = {
|
||||||
walletTransactions: [],
|
walletTransactions: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
error: null,
|
error: null,
|
||||||
|
walletTopUpPaymentSessionResponse: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const walletReducer = createReducer(initialState, builder => {
|
export const walletReducer = createReducer(initialState, builder => {
|
||||||
@ -27,4 +29,16 @@ export const walletReducer = createReducer(initialState, builder => {
|
|||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.error = action.payload as string | null;
|
state.error = action.payload as string | null;
|
||||||
});
|
});
|
||||||
|
builder.addCase(initializeWalletTopUpThunk.pending, state => {
|
||||||
|
state.loading = true;
|
||||||
|
state.error = null;
|
||||||
|
});
|
||||||
|
builder.addCase(initializeWalletTopUpThunk.fulfilled, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.walletTopUpPaymentSessionResponse = action.payload;
|
||||||
|
});
|
||||||
|
builder.addCase(initializeWalletTopUpThunk.rejected, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error = action.payload as string | null;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { getWalletTransactions } from '@api';
|
import { getWalletTransactions, initializeWalletTopUpApi, TopupPayload } from '@api';
|
||||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
export const getAllWalletTransactions = createAsyncThunk(
|
export const getAllWalletTransactions = createAsyncThunk(
|
||||||
@ -12,3 +12,15 @@ export const getAllWalletTransactions = createAsyncThunk(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const initializeWalletTopUpThunk = createAsyncThunk(
|
||||||
|
'wallet/initializeWalletTopUp',
|
||||||
|
async (payload: TopupPayload, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const response = await initializeWalletTopUpApi(payload);
|
||||||
|
return response;
|
||||||
|
} catch (error: any) {
|
||||||
|
return rejectWithValue(error.response.data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|||||||
@ -125,4 +125,97 @@ export const getStyles = (theme: any) =>
|
|||||||
color: '#888',
|
color: '#888',
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
},
|
},
|
||||||
|
modalOverlay: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
},
|
||||||
|
|
||||||
|
modalBackdrop: {
|
||||||
|
...StyleSheet.absoluteFill,
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.4)',
|
||||||
|
},
|
||||||
|
|
||||||
|
modalCard: {
|
||||||
|
backgroundColor: '#FFF',
|
||||||
|
borderTopLeftRadius: 24,
|
||||||
|
borderTopRightRadius: 24,
|
||||||
|
padding: 24,
|
||||||
|
paddingBottom: 32,
|
||||||
|
},
|
||||||
|
|
||||||
|
modalTitle: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: '700',
|
||||||
|
color: '#222',
|
||||||
|
},
|
||||||
|
|
||||||
|
modalSubtitle: {
|
||||||
|
fontSize: 13,
|
||||||
|
color: '#888',
|
||||||
|
marginTop: 4,
|
||||||
|
marginBottom: 20,
|
||||||
|
},
|
||||||
|
|
||||||
|
amountInputWrapper: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#E0E0E0',
|
||||||
|
borderRadius: 12,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
height: 56,
|
||||||
|
},
|
||||||
|
|
||||||
|
rupeeSymbol: {
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: '700',
|
||||||
|
color: '#222',
|
||||||
|
marginRight: 8,
|
||||||
|
},
|
||||||
|
|
||||||
|
amountInput: {
|
||||||
|
flex: 1,
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: '700',
|
||||||
|
color: '#222',
|
||||||
|
padding: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
modalError: {
|
||||||
|
color: '#F04438',
|
||||||
|
fontSize: 13,
|
||||||
|
marginTop: 8,
|
||||||
|
},
|
||||||
|
|
||||||
|
topUpButton: {
|
||||||
|
marginTop: 24,
|
||||||
|
backgroundColor: '#2F80ED',
|
||||||
|
height: 52,
|
||||||
|
borderRadius: 12,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
|
||||||
|
topUpButtonDisabled: {
|
||||||
|
opacity: 0.6,
|
||||||
|
},
|
||||||
|
|
||||||
|
topUpButtonText: {
|
||||||
|
color: '#FFF',
|
||||||
|
fontWeight: '700',
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
|
||||||
|
modalCancelButton: {
|
||||||
|
marginTop: 12,
|
||||||
|
height: 48,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
|
||||||
|
modalCancelText: {
|
||||||
|
color: '#888',
|
||||||
|
fontWeight: '600',
|
||||||
|
fontSize: 15,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,10 +1,15 @@
|
|||||||
import React, { useCallback, useEffect } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
Text,
|
Text,
|
||||||
SafeAreaView,
|
SafeAreaView,
|
||||||
FlatList,
|
FlatList,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
|
Modal,
|
||||||
|
TextInput,
|
||||||
|
KeyboardAvoidingView,
|
||||||
|
Platform,
|
||||||
|
Pressable,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
// import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
// import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import { Header } from '@components';
|
import { Header } from '@components';
|
||||||
@ -12,42 +17,12 @@ import { useAppTheme } from '@theme';
|
|||||||
import { getStyles } from './walletScreen.styles';
|
import { getStyles } from './walletScreen.styles';
|
||||||
import { WalletTransaction } from '@interfaces';
|
import { WalletTransaction } from '@interfaces';
|
||||||
import { useAppDispatch, useAppSelector } from '@store';
|
import { useAppDispatch, useAppSelector } from '@store';
|
||||||
import { getAllWalletTransactions } from './thunk';
|
import { getAllWalletTransactions, initializeWalletTopUpThunk } from './thunk';
|
||||||
import { useFocusEffect } from '@react-navigation/native';
|
import { useFocusEffect } from '@react-navigation/native';
|
||||||
import { getWalletBalanceThunk } from '../accountScreen';
|
import { getWalletBalanceThunk } from '../accountScreen';
|
||||||
|
import { useStripePayment } from '../checkoutPaymentScreen/hooks/useStripePayment';
|
||||||
// const balance = 17496.05;
|
import { useTopUp } from './hooks/useTopUp';
|
||||||
|
// import { addMoneyThunk } from './thunk'; // wire this up to your actual top-up API
|
||||||
// const transactions: WalletTransaction[] = [
|
|
||||||
// {
|
|
||||||
// id: '1',
|
|
||||||
// walletId: '',
|
|
||||||
// transactionReference: 'TXN-1784543790637-2387',
|
|
||||||
// direction: 'OUT',
|
|
||||||
// amount: '1408.95',
|
|
||||||
// balanceBefore: '18905',
|
|
||||||
// balanceAfter: '17496.05',
|
|
||||||
// type: 'PAYMENT',
|
|
||||||
// referenceId: null,
|
|
||||||
// description: null,
|
|
||||||
// idempotencyKey: null,
|
|
||||||
// createdAt: '2026-07-20T10:36:30.639Z',
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '2',
|
|
||||||
// walletId: '',
|
|
||||||
// transactionReference: 'TXN-1784199810512-8502',
|
|
||||||
// direction: 'OUT',
|
|
||||||
// amount: '1095',
|
|
||||||
// balanceBefore: '20000',
|
|
||||||
// balanceAfter: '18905',
|
|
||||||
// type: 'PAYMENT',
|
|
||||||
// referenceId: null,
|
|
||||||
// description: null,
|
|
||||||
// idempotencyKey: null,
|
|
||||||
// createdAt: '2026-07-16T11:03:30.515Z',
|
|
||||||
// },
|
|
||||||
// ];
|
|
||||||
|
|
||||||
const WalletScreen = () => {
|
const WalletScreen = () => {
|
||||||
const { colors } = useAppTheme();
|
const { colors } = useAppTheme();
|
||||||
@ -57,6 +32,12 @@ const WalletScreen = () => {
|
|||||||
const { walletTransactions } = useAppSelector(state => state.wallet);
|
const { walletTransactions } = useAppSelector(state => state.wallet);
|
||||||
const { wallet } = useAppSelector(state => state.account);
|
const { wallet } = useAppSelector(state => state.account);
|
||||||
|
|
||||||
|
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||||
|
const [amount, setAmount] = useState('');
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
const [error, setError] = useState('');
|
||||||
|
const { topUpCardPayment, isPaymentProcessing } = useTopUp();
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
dispatch(getAllWalletTransactions());
|
dispatch(getAllWalletTransactions());
|
||||||
@ -64,6 +45,50 @@ const WalletScreen = () => {
|
|||||||
}, []),
|
}, []),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const openModal = () => {
|
||||||
|
setAmount('');
|
||||||
|
setError('');
|
||||||
|
setIsModalVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
if (isSubmitting) return;
|
||||||
|
setIsModalVisible(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAmountChange = (text: string) => {
|
||||||
|
// allow only digits and a single decimal point
|
||||||
|
const sanitized = text.replace(/[^0-9.]/g, '');
|
||||||
|
setAmount(sanitized);
|
||||||
|
if (error) setError('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTopUp = async () => {
|
||||||
|
const numericAmount = parseFloat(amount);
|
||||||
|
|
||||||
|
if (!amount || isNaN(numericAmount) || numericAmount <= 0) {
|
||||||
|
setError('Please enter a valid amount');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setIsSubmitting(true);
|
||||||
|
const payload = {
|
||||||
|
amount: numericAmount,
|
||||||
|
paymentMethod: 'CARD'
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await dispatch(initializeWalletTopUpThunk(payload)).unwrap();
|
||||||
|
const success = await topUpCardPayment(response);
|
||||||
|
setIsSubmitting(false);
|
||||||
|
setIsModalVisible(false);
|
||||||
|
setAmount('');
|
||||||
|
} catch (e) {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
setError('Something went wrong. Please try again.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const renderItem = ({ item }: { item: WalletTransaction }) => {
|
const renderItem = ({ item }: { item: WalletTransaction }) => {
|
||||||
const isCredit = item.direction === 'IN';
|
const isCredit = item.direction === 'IN';
|
||||||
|
|
||||||
@ -113,7 +138,7 @@ const WalletScreen = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container}>
|
<KeyboardAvoidingView style={styles.container}>
|
||||||
<Header title="Wallet" />
|
<Header title="Wallet" />
|
||||||
|
|
||||||
<View style={styles.balanceCard}>
|
<View style={styles.balanceCard}>
|
||||||
@ -121,7 +146,7 @@ const WalletScreen = () => {
|
|||||||
|
|
||||||
<Text style={styles.balance}>₹{wallet?.balance.toFixed(2)}</Text>
|
<Text style={styles.balance}>₹{wallet?.balance.toFixed(2)}</Text>
|
||||||
|
|
||||||
<TouchableOpacity style={styles.addMoneyButton}>
|
<TouchableOpacity style={styles.addMoneyButton} onPress={openModal}>
|
||||||
{/* <Icon name="plus" color="#FFF" size={20} /> */}
|
{/* <Icon name="plus" color="#FFF" size={20} /> */}
|
||||||
<Text style={styles.addMoneyText}>Add Money</Text>
|
<Text style={styles.addMoneyText}>Add Money</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
@ -142,8 +167,65 @@ const WalletScreen = () => {
|
|||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</SafeAreaView>
|
|
||||||
|
<Modal
|
||||||
|
visible={isModalVisible}
|
||||||
|
transparent
|
||||||
|
animationType="fade"
|
||||||
|
onRequestClose={closeModal}
|
||||||
|
>
|
||||||
|
<KeyboardAvoidingView
|
||||||
|
style={styles.modalOverlay}
|
||||||
|
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||||
|
>
|
||||||
|
<Pressable style={styles.modalBackdrop} onPress={closeModal} />
|
||||||
|
|
||||||
|
<View style={styles.modalCard}>
|
||||||
|
<Text style={styles.modalTitle}>Add Money</Text>
|
||||||
|
<Text style={styles.modalSubtitle}>
|
||||||
|
Enter an amount to top up your wallet
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<View style={styles.amountInputWrapper}>
|
||||||
|
<Text style={styles.rupeeSymbol}>₹</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.amountInput}
|
||||||
|
keyboardType="decimal-pad"
|
||||||
|
placeholder="0.00"
|
||||||
|
placeholderTextColor="#B0B0B0"
|
||||||
|
value={amount}
|
||||||
|
onChangeText={handleAmountChange}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{error ? <Text style={styles.modalError}>{error}</Text> : null}
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[
|
||||||
|
styles.topUpButton,
|
||||||
|
isSubmitting && styles.topUpButtonDisabled,
|
||||||
|
]}
|
||||||
|
onPress={handleTopUp}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
>
|
||||||
|
<Text style={styles.topUpButtonText}>
|
||||||
|
{isSubmitting ? 'Processing...' : 'Add Top Up'}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.modalCancelButton}
|
||||||
|
onPress={closeModal}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
>
|
||||||
|
<Text style={styles.modalCancelText}>Cancel</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</KeyboardAvoidingView>
|
||||||
|
</Modal>
|
||||||
|
</KeyboardAvoidingView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default WalletScreen;
|
export default WalletScreen;
|
||||||
@ -22,3 +22,12 @@ export type TransactionType =
|
|||||||
| 'WITHDRAWAL'
|
| 'WITHDRAWAL'
|
||||||
| 'TRANSFER'
|
| 'TRANSFER'
|
||||||
| 'ADJUSTMENT';
|
| 'ADJUSTMENT';
|
||||||
|
|
||||||
|
export interface WalletTopUpPaymentSessionResponse {
|
||||||
|
message: string;
|
||||||
|
paymentId: string;
|
||||||
|
paymentReference: string;
|
||||||
|
gatewayOrderId: string;
|
||||||
|
gatewayToken: string;
|
||||||
|
amount: number;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user