Commit e66a4d54 by G

feat: Payment processing using Wave as the payment gateway with navigation on…

feat: Payment processing using Wave as the payment gateway with navigation on QrCode screen upon valid initialization. User is given the possibility to verify the state manually with a button.
parent 0ddf355b
......@@ -5,6 +5,8 @@ import type {
OmInitializationPayload as OmTransactionInitializationPayload,
OmInitializationResponse as OmTransactionInitializationResponse,
PaymentType,
WaveInitializationPayload,
WaveTransactionInitilizationResponse,
} from "./types";
export const getPaymentTypes = () => {
......@@ -44,3 +46,11 @@ export const omVerifyTransactionStateWithTimeout = async (
await sleep(retryAfter);
}
};
export const waveInitializeTransaction = (payload: WaveInitializationPayload) => {
return axiosInstance.post<WaveTransactionInitilizationResponse>("/transactions/", payload);
};
export const waveGetTransactionStatus = (id: string) => {
return axiosInstance.get<WaveTransactionInitilizationResponse>(`/wave-session/${id}/`);
};
......@@ -65,3 +65,27 @@ export interface OmTransaction {
txnid?: string;
};
}
// WAVE
export type WaveInitializationPayload = {
type_paiement: number;
marchand: string;
service: string;
montant: number;
};
export interface WaveTransactionInitilizationResponse {
id: string;
amount: string;
checkout_status: string;
client_reference: unknown;
currenfy: string;
error_url: string;
last_payment_errror: unknown;
business_name: string;
payment_status: string;
succes_url: string;
wave_launch_url: string;
when_completed: unknown;
when_created: string;
when_expires: string;
}
import type { NativeStackScreenProps } from "@react-navigation/native-stack";
import type { PaymentType } from "@/features/pay/types";
import type { IwaveStarterRespone } from "@/utils/requests/wavePayment";
import type { PaymentType, WaveTransactionInitilizationResponse } from "@/features/pay/types";
export type IpaymentStackNavigator = {
homePageWithPaymentOptions: undefined;
......@@ -26,7 +25,7 @@ export type ImainStackNavigator = {
appBottomTabsNavigator: undefined;
paymentResultScreen: undefined;
waveQrCodePaymentScreen: {
data: IwaveStarterRespone;
data: WaveTransactionInitilizationResponse;
};
};
......
......@@ -10,7 +10,11 @@ import type { AxiosError } from "axios";
import * as WebBrowser from "expo-web-browser";
import { useEffect, useRef, useState } from "react";
import { AppState, Text, View } from "react-native";
import { omInitializeTransaction, omVerifyTransactionStateWithTimeout } from "@/features/pay/api";
import {
omInitializeTransaction,
omVerifyTransactionStateWithTimeout,
waveInitializeTransaction,
} from "@/features/pay/api";
import PaymentType from "@/features/pay/components/PaymentType";
import type { PaymentStackScreenComponentProps } from "@/navigations/types";
......@@ -39,6 +43,16 @@ const PaymentAmountInputScreen: PaymentStackScreenComponentProps<"paymentAmountI
numero: "0707070707",
});
WebBrowser.openBrowserAsync(res.data.payment_url);
} else if (code === "WAVE") {
const res = await waveInitializeTransaction({
type_paiement: 2,
marchand: "1",
service: "1",
montant: amount,
});
navigation?.getParent()?.navigate("waveQrCodePaymentScreen", {
data: res,
});
} else {
navigation.navigate("numberAndOtpForPaymentScreen", {
paymentType: route.params.paymentType,
......
import Button from "@components/Button";
import BackgroundWithBeasyIconAndWhiteContentArea from "@components/backgrounds/BackgroundWithBeasyIconAndWhiteContentArea";
import Box from "@components/bases/Box";
import Text from "@components/bases/Text";
import useWave from "@hooks/useWave";
import { asp as g } from "@asp/asp";
import { BarnoinPayBackground } from "@components/BarnoinPayBackground";
import BeasyLogoIcon from "@components/BeasyLogoIcon";
import * as Button from "@components/ButtonNew";
import * as Modal from "@components/Modal";
import AntDesign from "@expo/vector-icons/AntDesign";
import { LOG } from "@logger";
import { Dimensions } from "react-native";
// biome-ignore lint/style/useNamingConvention: <explanation>
import QRCode from "react-native-qrcode-svg";
import { useMutation } from "@tanstack/react-query";
import type { AxiosError } from "axios";
import { useState } from "react";
import { Dimensions, ScrollView, Text, View } from "react-native";
import QrCode from "react-native-qrcode-svg";
import { waveGetTransactionStatus } from "@/features/pay/api";
import type { MainStackScreenComponentProps } from "@/navigations/types";
const log = LOG.extend("WaveQrCodePaymentScreen");
......@@ -16,62 +20,108 @@ const WaveQrCodePaymentScreen: MainStackScreenComponentProps<"waveQrCodePaymentS
navigation,
}) => {
log.verbose("WaveQrCodePaymentScreen");
const [error, setError] = useState("");
const data = route.params.data;
const windowWidth = Dimensions.get("window").width;
const { handlePaymentVerification } = useWave();
const qrSize = windowWidth * 0.7;
const [isSuccess, setIsSuccess] = useState(false);
const query = useMutation({
mutationKey: ["waveTransactionStatus", data.id],
mutationFn: () => waveGetTransactionStatus(data.id),
onError: (err: AxiosError) => {
setError(JSON.stringify(err.response?.data) || err.message);
},
onSuccess: (_data) => {
setIsSuccess(true);
},
});
return (
<BackgroundWithBeasyIconAndWhiteContentArea goBack={true}>
<Box
style={{
height: "100%",
width: "100%",
}}
p={"m"}
alignItems={"center"}
>
<Text
variant={"black"}
mb={"l"}
fontSize={20}
textAlign={"center"}
fontWeight={"bold"}
<BarnoinPayBackground style={[g.justify_between]}>
<View style={[g.px_lg, g.flex_row, g.align_center, g.justify_between]}>
<BeasyLogoIcon />
<AntDesign
name="arrowleft"
size={24}
color="black"
onPress={() => navigation.goBack()}
/>
</View>
<View
style={[
g.w_full,
g.p_5xl,
g.gap_xl,
{
backgroundColor: "white",
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
height: "80%",
},
]}
>
Votre QR Code
</Text>
<Text style={[g.text_3xl, g.font_bold]}>Votre QR Code</Text>
<Box
style={{ height: windowWidth - 80, aspectRatio: 1 }}
backgroundColor={"primary"}
alignItems={"center"}
justifyContent={"center"}
borderRadius={20}
borderWidth={2}
borderColor={"secondary"}
shadowColor={"black"}
shadowOffset={{ width: 0, height: 10 }}
shadowOpacity={0.5}
shadowRadius={13.16}
elevation={20}
mb={"l"}
<View
style={[
g.w_full,
g.align_center,
g.justify_center,
g.p_md,
g.rounded_lg,
g.shadow_elevated,
{ aspectRatio: 1, backgroundColor: "white" },
]}
>
<QRCode
value={data.wave_launch_url}
size={windowWidth - 120}
// backgroundColor={theme.colors.secondary}
/>
</Box>
<QrCode value={data.wave_launch_url} size={qrSize} />
</View>
<Text>Veuillez scanner le QR Code pour terminer le paiement</Text>
<Box width={"100%"} mt={"x100"}>
<Button
variant={"full"}
textVariants={"white"}
label="Verification"
onPress={() => handlePaymentVerification(data.id)}
/>
</Box>
</Box>
</BackgroundWithBeasyIconAndWhiteContentArea>
<Button.Container onPress={() => query.mutate()} isLoading={query.isPending}>
<Button.Label>Verification</Button.Label>
</Button.Container>
</View>
<Modal.RnModal visible={!!error} style={{ backgroundColor: "rgba(0, 0, 0, 0.25)" }}>
<Modal.OuterView
style={[
g.p_md,
g.shadow_elevated,
{ backgroundColor: "white", width: "80%", height: "40%" },
]}
>
<View style={[g.flex_1, g.gap_md]}>
<ScrollView style={[g.flex_1]}>
<Text>{error}</Text>
</ScrollView>
<Button.Container onPress={() => setError("")}>
<Button.Label>OK</Button.Label>
</Button.Container>
</View>
</Modal.OuterView>
</Modal.RnModal>
<Modal.RnModal visible={isSuccess} style={{ backgroundColor: "rgba(0, 0, 0, 0.25)" }}>
<Modal.OuterView
style={[
g.p_md,
g.shadow_elevated,
{ backgroundColor: "white", width: "80%", height: "40%" },
]}
>
<View style={[g.flex_1, g.gap_md]}>
<Text style={[g.text_center]}>Transaction réussie</Text>
<Button.Container
onPress={() => navigation.getParent()?.navigate("paymentResultScreen")}
>
<Button.Label>OK</Button.Label>
</Button.Container>
</View>
</Modal.OuterView>
</Modal.RnModal>
</BarnoinPayBackground>
);
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment