+payement handling v0.5

This commit is contained in:
AntoninoP 2024-09-23 03:02:29 +02:00
parent 0d6b741fd4
commit da3a161f30
7 changed files with 224 additions and 0 deletions

23
package-lock.json generated
View File

@ -10,6 +10,8 @@
"dependencies": {
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/react-fontawesome": "^0.2.2",
"@stripe/react-stripe-js": "^2.8.0",
"@stripe/stripe-js": "^4.5.0",
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.0",
"@types/jest": "^29.5.12",
@ -3184,6 +3186,27 @@
"@sinonjs/commons": "^3.0.0"
}
},
"node_modules/@stripe/react-stripe-js": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-2.8.0.tgz",
"integrity": "sha512-Vf1gNEuBxA9EtxiLghm2ZWmgbADNMJw4HW6eolUu0DON/6mZvWZgk0KHolN0sozNJwYp0i/8hBsDBcBUWcvnbw==",
"dependencies": {
"prop-types": "^15.7.2"
},
"peerDependencies": {
"@stripe/stripe-js": "^1.44.1 || ^2.0.0 || ^3.0.0 || ^4.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/@stripe/stripe-js": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-4.5.0.tgz",
"integrity": "sha512-dMOzc58AOlsF20nYM/avzV8RFhO/vgYTY7ajLMH6mjlnZysnOHZxsECQvjEmL8Q/ukPwHkOnxSPW/QGCCnp7XA==",
"engines": {
"node": ">=12.16"
}
},
"node_modules/@testing-library/dom": {
"version": "10.3.0",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.3.0.tgz",

View File

@ -13,6 +13,8 @@
"dependencies": {
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/react-fontawesome": "^0.2.2",
"@stripe/react-stripe-js": "^2.8.0",
"@stripe/stripe-js": "^4.5.0",
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.0",
"@types/jest": "^29.5.12",

View File

@ -5,6 +5,7 @@ import 'react-toastify/dist/ReactToastify.css';
import { auth } from './service/firebase';
import styles from './App.module.scss';
import Loading from './pages/Loading/loading';
import Pricing from './pages/Payement/Pricing';
const LoginPage = lazy(() => import('./pages/LoginPage/LoginPage'));
const ServerDetails = lazy(() => import('./pages/ServerDetails/ServerDetails'));
@ -55,6 +56,8 @@ const App = () => {
<Route path="/createServer/bedrock" element={user ? <Bedrock user={user} /> : <Navigate to="/login" />} />
<Route path="/createServer/modpack" element={user ? <Modpack user={user} /> : <Navigate to="/login" />} />
<Route path="/server/:serverName/*" element={user ? <ServerDetails user={user} /> : <Navigate to="/login" />} />
<Route path="/pricing" element={user ? <Pricing user={user} /> : <Navigate to="/login" />} />
<Route path="/" element={<Navigate to={user ? "/dashboard" : "/login"} />} />
<Route path="*" element={user ? <DashboardPage user={user} /> : <Navigate to="/login" />} />
</Routes>

View File

@ -0,0 +1,49 @@
import React, { useState } from 'react';
import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js';
import styles from './PaymentForm.module.scss';
const PaymentForm = ({ groups, closeModal }) => {
const stripe = useStripe();
const elements = useElements();
const [errorMessage, setErrorMessage] = useState(null);
const [successMessage, setSuccessMessage] = useState(null);
const handleSubmit = async (event) => {
event.preventDefault();
if (!stripe || !elements) return;
const cardElement = elements.getElement(CardElement);
const { error, paymentMethod } = await stripe.createPaymentMethod({
type: 'card',
card: cardElement,
});
if (error) {
setErrorMessage(error.message);
setSuccessMessage(null);
} else {
setSuccessMessage(`Paiement réussi! Méthode de paiement ID: ${paymentMethod.id}`);
setErrorMessage(null);
// Ici, vous devriez également envoyer paymentMethod.id à votre serveur
// Fermer la modal après un court délai ou après l'envoi à votre serveur
setTimeout(() => closeModal(), 2000);
}
};
return (
<form onSubmit={handleSubmit} className={styles.paymentForm}>
<CardElement />
{errorMessage && <p className={styles.error}>{errorMessage}</p>}
{successMessage && <p className={styles.success}>{successMessage}</p>}
<button type="submit" disabled={!stripe} className={styles.submitButton}>
Payer {groups.price}
</button>
<button type="button" onClick={closeModal} className={styles.closeButton}>
Fermer
</button>
</form>
);
};
export default PaymentForm;

View File

@ -0,0 +1,19 @@
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modalContent {
background: #fff;
padding: 20px;
border-radius: 8px;
width: 300px;
}

View File

@ -0,0 +1,68 @@
import React, { useState } from 'react';
import styles from './Pricing.module.scss';
import Navbar from '../../components/navbar/Navbar';
import { useNavigate } from 'react-router-dom';
import PropTypes from 'prop-types';
import { loadStripe } from '@stripe/stripe-js';
import { Elements } from '@stripe/react-stripe-js';
import PaymentForm from './PaymentForm/PaymentForm';
const stripePromise = loadStripe('pk_test_51PyIYTP3VLLeb9GlXCKgD4ylbemZPx72I3HkEAu0bRtcsfK31nqb3WtUbXKXUcKmyfrxKLfuJzZCPyp7Ymtlq9zy00c7VmkL6G');
const Pricing = ({ user }) => {
const navigate = useNavigate();
const [selectedPackage, setSelectedPackage] = useState(null);
const [isModalOpen, setIsModalOpen] = useState(false);
const groups = [
{ title: 'Gratuit', price: '0 €', features: ['Accès limité', 'Support par e-mail'] },
{ title: 'Standard', price: '2 €', features: ['Accès complet', 'Support prioritaire'] },
{ title: 'Premium', price: '9 €', features: ['Accès illimité', 'Support 24/7'] },
];
const handleSubscribe = (pkg) => {
setSelectedPackage(pkg);
setIsModalOpen(true);
};
return (
<div className={styles.pricingContainer}>
<Navbar
user={user}
hasShadow={true}
showBackButton={true}
onBackClick={() => navigate('/Dashboard')}
/>
{groups.map((pkg, index) => (
<div key={index} className={styles.packageCard}>
<h2 className={styles.title}>{pkg.title}</h2>
<p className={styles.price}>{pkg.price}</p>
<ul className={styles.features}>
{pkg.features.map((feature, idx) => (
<li key={idx}>{feature}</li>
))}
</ul>
<button className={styles.button} onClick={() => handleSubscribe(pkg)}>Sinscrire</button>
</div>
))}
{isModalOpen && (
<div className={styles.modal}>
<div className={styles.modalContent}>
<h2>Paiement pour {selectedPackage.title}</h2>
<Elements stripe={stripePromise}>
<PaymentForm groups={selectedPackage} closeModal={() => setIsModalOpen(false)} />
</Elements>
</div>
</div>
)}
</div>
);
};
Pricing.propTypes = {
user: PropTypes.object.isRequired,
};
export default Pricing;

View File

@ -0,0 +1,60 @@
$primary-color: #000;
$secondary-color: #fff;
.pricingContainer {
display: flex;
justify-content: center;
gap: 20px;
padding: 40px;
background-color: $secondary-color;
}
.packageCard {
background-color: $primary-color;
color: $secondary-color;
border-radius: 8px;
padding: 20px;
text-align: center;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
transition: transform 0.3s;
&:hover {
transform: scale(1.05);
}
}
.title {
font-size: 24px;
margin-bottom: 10px;
}
.price {
font-size: 32px;
margin: 10px 0;
}
.features {
list-style-type: none;
padding: 0;
margin: 20px 0;
li {
margin: 5px 0;
}
}
.button {
background-color: $secondary-color;
color: $primary-color;
border: none;
border-radius: 4px;
padding: 10px 20px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
&:hover {
background-color: $primary-color;
color: $secondary-color;
}
}