Uploaded by shubhamrajput1385

failed

advertisement
/* eslint-disable prettier/prettier */
import {
Anchor,
Breadcrumbs,
Button,
Group,
LoadingOverlay,
Modal,
Paper,
Text,
TextInput,
Tooltip,
Title,
ActionIcon,
Badge,
Grid,
InputWrapper,
Input,
Textarea,
Space,
Alert,
Center,
MultiSelect,
Checkbox,
CheckboxGroup,
NumberInput,
RadioGroup,
Radio,
} from "@mantine/core";
import { showNotification } from "@mantine/notifications";
import { CaretSortIcon, ResetIcon } from "@radix-ui/react-icons";
import Link from "next/link";
import React, { useEffect, useState, useRef } from "react";
import DataTable from "react-data-table-component";
import { useDispatch, useSelector } from "react-redux";
import { getConfig, isConfigLoaded } from "reducers/configReducer";
import { render } from "sass";
import ltcService from "services/ltcService";
import styled from "styled-components";
import { Row } from "styles/styledComponents";
import { Config } from "types/config";
import { LtcUser } from "types/user";
import translations from "../translations.json";
import {
Edit,
BrandProducthunt,
News,
VideoPlus,
Video,
Affiliate,
Replace,
CirclePlus,
Filter,
AlertCircle,
Calendar,
ListDetails,
} from "tabler-icons-react";
import { useRouter } from "next/router";
import {
faArrowRotateLeft,
faAward,
faCertificate,
faCoffee,
faEye,
faUserEdit,
faVideoCamera,
faCompress,
faPen,
faRss,
faUpload,
faTrash,
faEyeSlash,
faRetweet,
faTimes,
faDownload,
faCopy,
faList,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import moment from "moment";
import { DatePicker } from "@mantine/dates";
import { useDebouncedValue, useForm } from "@mantine/hooks";
import SubscriptionStatus from "../components/subscriptionStatus";
import { IconSearch } from "@tabler/icons";
const dataTableStyle = {
width: "98%",
headCells: {
style: {
fontSize: "16px",
fontWeight: "bold",
},
},
cells: {
style: {
fontSize: "14px",
},
},
};
const checkBoxStyle = {
padding: "0%",
textAlign: "left",
fontStyle: "normal",
wordWrap: "break-word",
// maxWidth:"20%",
};
const FailedPayments = () => {
const dispatch = useDispatch();
const configLoaded = useSelector(isConfigLoaded);
const config: Config = useSelector(getConfig);
const router = useRouter();
const {
id,
inputStatus,
inputFilter,
pagenumber,
pagesize,
inputSortField,
inputSortDirection,
} = router.query;
const [openResendDialog, setOpenResendDialog] = useState(false);
const [loader, setLoader] = useState(false);
const [packageLoading, setPackageLoading] = useState(false);
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [title, setTitle] = useState("");
const [paginationTotalRows, setPaginationTotalRows] = useState(0);
const [searchTerm, setSearchTerm] = useState("");
const [openPreviewDialog, setOpenPreviewDialog] = useState(false);
const [previewData, setPreviewData] = useState();
const [spinner, setSpinner] = React.useState(false);
const [pageNumber, setPageNumber] = useState(pagenumber ? pagenumber : 1);
const [pageSize, setPageSize] = useState(pagesize ? pagesize : 100);
const [sortField, setSortField] = useState(
inputSortField ? inputSortField : "startTime",
);
const [sortDirection, setSortDirection] = useState(
inputSortDirection ? inputSortDirection : "DESC",
);
const [toBeResendRecepId, setToBeResendRecepId] = useState();
const [templateIdentifiers, setTemplateIdentifiers] = useState([]);
const [selectedTempId, setSelectedTemp] = useState([]);
const [filter, setFilter] = useState(inputFilter ? inputFilter : "");
const [debouncedFilter] = useDebouncedValue(filter, 200);
//NEW FIELDS
const [selectedStatuses, setSelectedStatuses] = useState([]);
const [auto, setAuto] = useState([]);
const [todate, setToDate] = useState();
const [fromdate, setFromDate] = useState();
const [fromAmount, setFromAmount] = useState();
const [userId, setUserId] = useState();
const [email, setEmail] = useState();
const [toamount, setToAmount] = useState();
const [status, setStatus] = useState([]);
const [packages, setPackages] = useState([]);
const [products, setProducts] = useState([]);
const [selectedProducts, setSelectedProducts] = useState([]);
const [selectedPackages, setSelectedPackages] = useState([]);
const [copyLabel, setCopyLable] = useState("Copy");
const [emiData, setEmiData] = useState([]);
const [openEmiDialog, setOpenEmiDialog] = useState(false);
useEffect(() => {
if (inputStatus) {
if (typeof inputStatus == "object") {
setSelectedStatuses(inputStatus);
} else {
setSelectedStatuses([inputStatus]);
}
}
if (configLoaded && !inputStatus && !inputFilter) {
loadData();
}
}, [configLoaded]);
useEffect(() => {
if (configLoaded) {
filterData();
}
}, [
pageNumber,
pageSize,
sortField,
sortDirection,
debouncedFilter,
selectedStatuses,
]);
useEffect(() => {
if (products.length == 0) loadProducts();
}, []);
function loadEmiData(row) {
ltcService
.get(
`${config.paymentApiUrl}/payment-service/api/v1/subscription/${row.subscriptionId}/installment`,
)
.then((response) => {
setEmiData(response.data);
})
.catch((e) => {
setLoader(false);
});
}
async function loadProducts() {
const url = `${config.productApiUrl}/product-service/api/v1/product`;
const response = await ltcService
.get(url)
.then((response) => {
const data = [];
response.data.forEach((el) => {
data.push({
label: el["name"],
value: el["id"],
});
});
setProducts(data);
})
.catch((e) => {
setLoader(false);
});
}
async function loadData() {
setLoader(true);
let url = `${config.paymentApiUrl}/payment-service/admin/api/v1/installment/failedpayments`;
url =
url +
`?pageNumber=${pageNumber}&pageSize=${pageSize}&sortField=${sortField}&sortDirection=${sortDirection}`;
const response = await ltcService
.get(url)
.then((response) => {
handleDeepLink();
setData(response.data.data);
setTotalRows(response.data.pagination.total);
setPageNumber(response.data.pagination.pageNumber);
// setPaginationTotalRows(response.data.pagination.total);
setLoader(false);
})
.catch((e) => {
setLoader(false);
});
}
const handleSort = async (column, sortDirection) => {
/// reach out to some API and get new data using or sortField and sortDirection
// e.g. https://api.github.com/search/repositories?q=blog&sort=${column.sortField}&order=${sortDirection}
setSortField(column.sortField);
setSortDirection(sortDirection);
// alert(column);
// alert(column.sortField);
// alert(sortDirection);
};
const handlePageChange = async (page) => {
setPageNumber(page);
handleDeepLink();
};
const handlePerRowsChange = async (newPerPage, page) => {
setPageSize(newPerPage);
handleDeepLink();
};
async function filterData() {
setLoader(true);
if (configLoaded) {
setLoader(true);
if (
refFromAmount.current?.value &&
isNaN(parseInt(refFromAmount.current?.value))
) {
setLoader(false);
showNotification({
title: "Error",
color: "red",
message: "From Amount should be a number",
});
return;
} else if (
refToAmount.current?.value &&
isNaN(parseInt(refToAmount.current?.value))
) {
setLoader(false);
showNotification({
title: "Error",
color: "red",
message: "To Amount should be a number",
});
return;
}
let url = `${config.paymentApiUrl}/payment-service/admin/api/v1/installment/failedpayments?`;
if (fromdate) {
url = url + `fromDate=${moment(fromdate).format("YYYY-MM-DD")}`;
}
if (todate) {
url = url + `&toDate=${moment(todate).format("YYYY-MM-DD")}`;
}
if (refFromAmount.current?.value) {
url = url + `&fromAmount=${refFromAmount.current?.value}`;
}
if (refToAmount.current?.value) {
url = url + `&toAmount=${refToAmount.current?.value}`;
}
if (refUserId.current?.value) {
url = url + `&userId=${refUserId.current?.value}`;
}
if (refEmail.current?.value) {
url = url + `&email=${refEmail.current?.value}`;
url = url?.replaceAll("+", "%2B");
}
// if (status.length > 0) {
// status.forEach((el) => {
// url = url + `&statuses=${el}`;
// });
// }
if (selectedProducts.length > 0) {
selectedProducts.forEach((el) => {
url = url + `&products=${el}`;
});
}
url =
url +
`&pageNumber=${pageNumber}&pageSize=${pageSize}&sortField=${sortField}&sortDirection=${sortDirection}`;
await ltcService
.get(url)
.then((res) => {
setLoader(false);
handleDeepLink();
setData(res.data.data);
setTotalRows(res.data.pagination.total);
setPageNumber(res.data.pagination.pageNumber);
})
.catch((error) => {
setLoader(false);
});
// setData(response.data);
// setTotalRows(response.data.length);
}
}
const resetFilter = () => {
setFilter("");
};
async function resetData() {
setLoader(true);
let url = `${config.paymentApiUrl}/payment-service/admin/api/v1/installment/failedpayments`;
url =
url +
`?pageNumber=${1}&pageSize=${pageSize}&sortField=${"DESC"}&sortDirection=${"startTime"}`;
const response = await ltcService
.get(url)
.then((response) => {
setData(response.data.data);
setTotalRows(response.data.pagination.total);
setPageNumber(response.data.pagination.pageNumber);
// setPaginationTotalRows(response.data.pagination.total);
setLoader(false);
})
.catch((e) => {
setLoader(false);
});
}
const [resetPaginationToggle, setResetPaginationToggle] =
React.useState(false);
const caseInsensitiveSort = (rowA, rowB) => {
const a = rowA.errorCount;
const b = rowB.errorCount;
if (a > b) {
return 1;
}
if (b > a) {
return -1;
}
return 0;
};
const columns = [
{
name: (
<Tooltip label="User Id" gutter={10}>
User Id
</Tooltip>
),
selector: (row: any) => row["userId"],
cell: (row: any) => {
const data = row.userId?.split("-");
const filterData = data && data[0];
const copyId = (id: string) => {
navigator.clipboard
.writeText(id)
.then(() => setCopyLable("Copied!"));
setTimeout(() => setCopyLable("Copy"), 2000);
};
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<div style={{ minWidth: "65px" }}>
<Tooltip label={row.userId} gutter={10}>
<Anchor
href={`${config.userAppUrl}/users/${row.userId}`}
target="_self"
>
{filterData}
</Anchor>
</Tooltip>
</div>
<div>
<Tooltip label={copyLabel} gutter={10}>
<ActionIcon onClick={() => copyId(row.userId)}>
<FontAwesomeIcon
icon={faCopy}
size="1x"
type="button"
style={{
color: "228be6",
}}
/>
</ActionIcon>
</Tooltip>
</div>
</div>
);
},
sortable: "true",
headerStyle: { fontWeight: "bold" },
grow: 0.7,
sortField: "userId",
},
{
name: (
<Tooltip label="Email" gutter={10}>
Email
</Tooltip>
),
selector: (row: any) => row["email"],
cell: (row: any) => (
<div>
<Tooltip
label={row["email"] != null ? row["email"] : "-"}
gutter={10}
>
{row["email"] != null ? row["email"] : "-"}
</Tooltip>
</div>
),
sortable: "true",
headerStyle: { fontWeight: "bold" },
grow: 0.7,
sortField: "email",
},
{
name: (
<Tooltip label="Product" gutter={10}>
Product
</Tooltip>
),
selector: (row: any) => row["productName"],
cell: (row: any) => (
<div>
<Tooltip
label={
row["productName"] != null
? row["productName"]
: "-"
}
gutter={10}
>
<Anchor
href={`/product/${row.productId}`}
target="_self"
>
{row["productName"] != null
? row["productName"]
: "-"}
</Anchor>
</Tooltip>
</div>
),
sortable: "true",
headerStyle: { fontWeight: "bold" },
grow: 1,
sortField: "productName",
},
{
name: (
<Tooltip label="Status" gutter={10}>
Status
</Tooltip>
),
selector: (row: any) => row["status"],
cell: (row: any) => (
<div>
<Tooltip
label={row["status"] != null ? row["status"] : "-"}
gutter={10}
>
{row["status"] != null ? row["status"] : "-"}
</Tooltip>
</div>
),
sortable: "true",
headerStyle: { fontWeight: "bold" },
grow: 0.5,
sortField: "status",
},
{
name: (
<Tooltip label="Provider Status" gutter={10}>
Provider Status
</Tooltip>
),
selector: (row: any) => row["stripePaymentStatus"],
cell: (row: any) => (
<div>
<Tooltip
label={
row["stripePaymentStatus"] != null
? row["stripePaymentStatus"]
: "-"
}
gutter={10}
>
{row["stripePaymentStatus"] != null
? row["stripePaymentStatus"]
: "-"}
</Tooltip>
</div>
),
sortable: "true",
headerStyle: { fontWeight: "bold" },
grow: 0.5,
sortField: "stripePaymentStatus",
},
{
name: (
<Tooltip label="Provider Failure Reason" gutter={10}>
Provider Failure Reason
</Tooltip>
),
selector: (row: any) => row.failureReason,
cell: (row: any) => (
<div>
<Tooltip
label={
row["failureReason"] != null
? row["failureReason"]
: "-"
}
gutter={10}
>
{row["failureReason"] != null
? row["failureReason"]
: "-"}
</Tooltip>
</div>
),
sortable: "true",
headerStyle: { fontWeight: "bold" },
grow: 1.2,
wrap: true,
sortField: "reason",
},
{
name: (
<Tooltip label="Provider" gutter={10}>
Provider
</Tooltip>
),
selector: (row: any) => "stripe",
cell: (row: any) => (
<div>
<Tooltip label={"stripe"} gutter={10}>
stripe
</Tooltip>
</div>
),
sortable: "true",
headerStyle: { fontWeight: "bold" },
grow: 1.2,
wrap: true,
},
{
name: (
<Tooltip label="Price" gutter={10}>
Price
</Tooltip>
),
selector: (row: any) => row["formattedPrice"],
cell: (row: any) => (
<div>
<Tooltip
label={
row["formattedPrice"] != null
? row["formattedPrice"]
: "-"
}
gutter={10}
>
{row["formattedPrice"] != null
? row["formattedPrice"]
: "-"}
</Tooltip>
</div>
),
sortable: "true",
headerStyle: { fontWeight: "bold" },
grow: 0.3,
sortField: "price",
},
{
name: (
<Tooltip label="Payment Due Date" gutter={10}>
Payment Due Date
</Tooltip>
),
selector: (row: any) => row.paymentDueDate,
cell: (row: any) => (
<div>
<Tooltip
label={
row["paymentDueDate"] != null
? row["paymentDueDate"]
: "-"
}
gutter={10}
>
{row.paymentDueDate
? new Date(row.paymentDueDate)
.toLocaleDateString("de-CH")
.concat(" ")
.concat(
new Date(
row.paymentDueDate,
).toLocaleTimeString("de-CH"),
)
: "-"}
</Tooltip>
</div>
),
sortable: "true",
grow: 0.5,
sortField: "paymentDueDate",
},
];
const emiColumns = [
{
name: (
<Tooltip label="Installment Id" gutter={10}>
Installment Id
</Tooltip>
),
selector: (row: any) => row.paymentInstallmentId,
cell: (row: any) => (
<div>
<Tooltip label={row.paymentInstallmentId} gutter={10}>
{row.paymentInstallmentId}
</Tooltip>
</div>
),
sortable: "true",
headerStyle: { fontWeight: "bold" },
grow: 1.4,
wrap: true,
},
{
name: (
<Tooltip label="Subscription Id" gutter={10}>
Subscription Id
</Tooltip>
),
selector: (row: any) => row.subscriptionId,
cell: (row: any) => (
<div>
<Tooltip label={row.subscriptionId} gutter={10}>
{row.subscriptionId}
</Tooltip>
</div>
),
sortable: "true",
headerStyle: { fontWeight: "bold" },
grow: 1.4,
wrap: true,
},
{
name: (
<Tooltip label="Payment Due Date" gutter={10}>
Payment Due Date
</Tooltip>
),
selector: (row: any) => row.paymentDueDate,
cell: (row: any) => (
<Tooltip label={row.paymentDueDate} gutter={10}>
{row.paymentDueDate !== null
? new Date(row.paymentDueDate)
.toLocaleDateString("de-CH")
.concat(" ")
.concat(
new Date(
row.paymentDueDate,
).toLocaleTimeString("de-CH"),
)
: ""}
</Tooltip>
),
sortable: "true",
grow: 2,
},
{
name: (
<Tooltip label="Package Name" gutter={10}>
Package Name
</Tooltip>
),
selector: (row: any) => row.packageName,
cell: (row: any) => (
<div>
<Tooltip label={row.packageName} gutter={10}>
{row.packageName}
</Tooltip>
</div>
),
sortable: "true",
headerStyle: { fontWeight: "bold" },
grow: 1.2,
wrap: true,
},
{
name: (
<Tooltip label="Price" gutter={10}>
Price
</Tooltip>
),
selector: (row: any) => row.price,
cell: (row: any) => (
<div>
<Tooltip
label={row.price != null ? row.price + " €" : ""}
gutter={10}
>
{row.price != null ? row.price + " €" : ""}
</Tooltip>
</div>
),
sortable: "true",
headerStyle: { fontWeight: "bold" },
grow: 1,
wrap: true,
},
{
name: (
<Tooltip label="status" gutter={10}>
Status
</Tooltip>
),
selector: (row: any) => row.status,
cell: (row: any) => (
<div>
<Tooltip label={row.status} gutter={10}>
{row.status}
</Tooltip>
</div>
),
sortable: "true",
headerStyle: { fontWeight: "bold" },
grow: 1,
wrap: true,
},
];
const handleProductChange = (event: any) => {
if (event && event.target && event.target.value) {
setSelectedProducts(event.target.value);
} else {
setSelectedProducts(event);
}
};
const handlePackageChange = (event: any) => {
if (event && event.target && event.target.value) {
setSelectedPackages(event.target.value);
} else {
setSelectedPackages(event);
}
};
// BreadCrumb
const items = [
{ title: translations.breadCrumbs.adminHome, href: "/" },
{ title: "Payment Backoffice", href: "/" },
{ title: "Failed Payments", href: "/failedpayments" },
].map((item, index) => (
<Anchor href={item.href} key={index}>
{item.title}
</Anchor>
));
function handleDeepLink() {
let url = router.asPath.includes("#")
? router.asPath.substring(0, router.asPath.indexOf("#"))
: router.asPath;
url = router.asPath.includes("?")
? router.asPath.substring(0, router.asPath.indexOf("?"))
: router.asPath.substring(
0,
router.asPath.indexOf("/failedpayments"),
);
url = url + "?";
//sources
//from date
if (fromdate) {
if (!url.includes("=")) {
url = `${url}inputFromDate=${fromdate}`;
} else if (url.includes("=")) {
url = `${url}&inputFromDate=${fromdate}`;
}
}
if (todate) {
if (!url.includes("=")) {
url = `${url}inputToDate=${todate}`;
} else if (url.includes("=")) {
url = `${url}&inputToDate=${todate}`;
}
}
if (refFromAmount.current?.value) {
if (!url.includes("=")) {
url = `${url}inputFromAmount=${refFromAmount.current?.value}`;
} else if (url.includes("=")) {
url = `${url}&inputFromAmount=${refFromAmount.current?.value}`;
}
}
if (refToAmount.current?.value) {
if (!url.includes("=")) {
url = `${url}inputToAmount=${refToAmount.current?.value}`;
} else if (url.includes("=")) {
url = `${url}&inputToAmount=${refToAmount.current?.value}`;
}
}
if (refUserId.current?.value) {
if (!url.includes("=")) {
url = `${url}inputUserId=${refUserId.current?.value}`;
} else if (url.includes("=")) {
url = `${url}&inputUserId=${refUserId.current?.value}`;
}
}
if (refEmail.current?.value) {
if (!url.includes("=")) {
url = `${url}inputEmail=${refEmail.current?.value}`;
url = url?.replaceAll("+", "%2B");
} else if (url.includes("=")) {
url = `${url}&inputEmail=${refEmail.current?.value}`;
url = url?.replaceAll("+", "%2B");
}
}
selectedProducts?.forEach((element, index) => {
if (!url.includes("=")) {
if (index == 0) url = `${url}inputProducts=${element}`;
else {
url = `${url}&inputProducts=${element}`;
}
} else if (url.includes("=")) {
url = `${url}&inputProducts=${element}`;
}
});
if (!url.includes("=")) {
url = `${url}pagenumber=${pageNumber}&pagesize=${pageSize}&inputSortField=${sortField}&inputSortDirection=${sortDirection}`;
} else if (url.includes("=")) {
url = `${url}&pagenumber=${pageNumber}&pagesize=${pageSize}&inputSortField=${sortField}&inputSortDirection=${sortDirection}`;
}
router.replace("/failedpayments?", url);
console.log(router);
}
function resetLink() {
console.log(router);
setPageNumber(1);
setPageSize(100);
// loadData();
}
const onFilterChange = async (filter) => {
await setFilter(filter);
setPageNumber(1);
};
const form = useForm({
initialValues: {
source: "",
date: "",
amount: "",
paymentStatus: "",
invoiceNum: "",
email: "",
country: "",
},
});
return (
<>
<LoadingOverlay visible={loader}></LoadingOverlay>
<Breadcrumbs separator="→">{items}</Breadcrumbs>
<br />
<Title style={{ marginBottom: "25px", fontSize: "24px" }}>
Failed Payments
</Title>
<Paper withBorder shadow="sm" radius="md" p="md">
<Grid style={{ paddingTop: "10px" }}>
<Grid.Col span={5}>
<TextInput
icon={<IconSearch />}
styles={{
input: { borderRadius: "20px" },
}}
placeholder="Search"
value={filter}
onChange={(event) =>
onFilterChange(event.currentTarget.value)
}
rightSection={
filter && (
<IconCircleLetterX
onClick={() => {
resetFilter();
}}
size="2rem"
style={{
color: "#228be6",
display: "block",
cursor: "pointer",
}}
/>
)
}
/>
</Grid.Col>
<Grid.Col span={4}></Grid.Col>
<Grid.Col span={2}>
<MultiSelect
data={[
{ label: "Draft", value: "DRAFT" },
{ label: "Published", value: "PUBLISHED" },
{ label: "Deactivated", value: "DEACTIVATED" },
{ label: "Freebie", value: "FREEBIE" },
]}
value={selectedStatuses}
placeholder="Status"
searchable
clearable
onChange={handleStatusChange}
/>
</Grid.Col>
<Grid.Col span={1}>
<Group position="right">
<ActionIcon
onClick={() => {
setOpenAddDialog(true);
}}
variant="transparent"
>
<Tooltip label="Add New Product" gutter={10}>
{/* <Link href="/newMeditation" style={{ cursor: "pointer" }}> */}
<CirclePlus size={36} color={"#228be6"} />
{/* </Link> */}
</Tooltip>
</ActionIcon>
</Group>
</Grid.Col>
</Grid>
<br />
{data?.length > 0 ? (
<DataTable
highlightOnHover={true}
columns={columns}
data={data}
defaultSortFieldId="updatedOn"
defaultSortAsc={false}
defaultSortDirection="desc"
sortDirection="desc"
customStyles={dataTableStyle}
sortIcon={<CaretSortIcon />}
paginationPerPage={pageSize}
paginationTotalRows={totalRows}
paginationDefaultPage={pageNumber}
onChangePage={handlePageChange}
onChangeRowsPerPage={handlePerRowsChange}
pagination
paginationServer
sortServer
onSort={handleSort}
paginationResetDefaultPage={resetPaginationToggle} // optionally, a hook to reset pagination to page 1
selectableRowsNoSelectAll
noDataComponent
persistTableHead
paginationRowsPerPageOptions={[200, 250, 300]}
/>
) : (
<Alert
icon={<AlertCircle size={16} />}
title="No Payments Found"
></Alert>
)}
<Modal
transition="fade"
transitionDuration={600}
transitionTimingFunction="ease"
opened={openAddDialog}
withCloseButton
onClose={() => setOpenAddDialog(false)}
size="md"
title="Create new Product"
radius="md"
styles={{
header: { color: "#228be6" },
title: { color: "#228be6" },
}}
>
<TextInput
placeholder="Product Name"
label="Product Name"
defaultValue={name}
onChange={handleNameChange}
error={nameError}
required
/>
<br />
</Modal>
</Paper>
</>
);
};
FailedPayments.getInitialProps = async () => {
return {};
};
export default FailedPayments;
Download