mirror of
https://github.com/hubHarmony/servii-frontend.git
synced 2024-11-17 21:40:30 +00:00
+payement handling v0.5
This commit is contained in:
parent
0d6b741fd4
commit
da3a161f30
23
package-lock.json
generated
23
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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>
|
||||
|
49
src/pages/Payement/PaymentForm/PaymentForm.jsx
Normal file
49
src/pages/Payement/PaymentForm/PaymentForm.jsx
Normal 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;
|
19
src/pages/Payement/PaymentForm/PaymentForm.module.scss
Normal file
19
src/pages/Payement/PaymentForm/PaymentForm.module.scss
Normal 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;
|
||||
}
|
||||
|
68
src/pages/Payement/Pricing.jsx
Normal file
68
src/pages/Payement/Pricing.jsx
Normal 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;
|
60
src/pages/Payement/Pricing.module.scss
Normal file
60
src/pages/Payement/Pricing.module.scss
Normal 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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user