forked from coderdost/MERN-ecommerce-Frontend
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Part 25 : Stripe Integration (Backend Part 8)
- Loading branch information
1 parent
11fd5ea
commit e52c0a1
Showing
7 changed files
with
324 additions
and
11 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
|
||
|
||
|
||
.Stripe form { | ||
width: 30vw; | ||
min-width: 500px; | ||
align-self: center; | ||
box-shadow: 0px 0px 0px 0.5px rgba(50, 50, 93, 0.1), | ||
0px 2px 5px 0px rgba(50, 50, 93, 0.1), 0px 1px 1.5px 0px rgba(0, 0, 0, 0.07); | ||
border-radius: 7px; | ||
padding: 40px; | ||
} | ||
|
||
#payment-message { | ||
color: rgb(105, 115, 134); | ||
font-size: 16px; | ||
line-height: 20px; | ||
padding-top: 12px; | ||
text-align: center; | ||
} | ||
|
||
#payment-element { | ||
margin-bottom: 24px; | ||
} | ||
|
||
/* Buttons and links */ | ||
.Stripe button { | ||
background: #5469d4; | ||
font-family: Arial, sans-serif; | ||
color: #ffffff; | ||
border-radius: 4px; | ||
border: 0; | ||
padding: 12px 16px; | ||
font-size: 16px; | ||
font-weight: 600; | ||
cursor: pointer; | ||
display: block; | ||
transition: all 0.2s ease; | ||
box-shadow: 0px 4px 5.5px 0px rgba(0, 0, 0, 0.07); | ||
width: 100%; | ||
} | ||
|
||
.Stripe button:hover { | ||
filter: contrast(115%); | ||
} | ||
|
||
.Stripe button:disabled { | ||
opacity: 0.5; | ||
cursor: default; | ||
} | ||
|
||
/* spinner/processing state, errors */ | ||
.spinner, | ||
.spinner:before, | ||
.spinner:after { | ||
border-radius: 50%; | ||
} | ||
|
||
.spinner { | ||
color: #ffffff; | ||
font-size: 22px; | ||
text-indent: -99999px; | ||
margin: 0px auto; | ||
position: relative; | ||
width: 20px; | ||
height: 20px; | ||
box-shadow: inset 0 0 0 2px; | ||
-webkit-transform: translateZ(0); | ||
-ms-transform: translateZ(0); | ||
transform: translateZ(0); | ||
} | ||
|
||
.spinner:before, | ||
.spinner:after { | ||
position: absolute; | ||
content: ''; | ||
} | ||
|
||
.spinner:before { | ||
width: 10.4px; | ||
height: 20.4px; | ||
background: #5469d4; | ||
border-radius: 20.4px 0 0 20.4px; | ||
top: -0.2px; | ||
left: -0.2px; | ||
-webkit-transform-origin: 10.4px 10.2px; | ||
transform-origin: 10.4px 10.2px; | ||
-webkit-animation: loading 2s infinite ease 1.5s; | ||
animation: loading 2s infinite ease 1.5s; | ||
} | ||
|
||
.spinner:after { | ||
width: 10.4px; | ||
height: 10.2px; | ||
background: #5469d4; | ||
border-radius: 0 10.2px 10.2px 0; | ||
top: -0.1px; | ||
left: 10.2px; | ||
-webkit-transform-origin: 0px 10.2px; | ||
transform-origin: 0px 10.2px; | ||
-webkit-animation: loading 2s infinite ease; | ||
animation: loading 2s infinite ease; | ||
} | ||
|
||
@keyframes loading { | ||
0% { | ||
-webkit-transform: rotate(0deg); | ||
transform: rotate(0deg); | ||
} | ||
100% { | ||
-webkit-transform: rotate(360deg); | ||
transform: rotate(360deg); | ||
} | ||
} | ||
|
||
@media only screen and (max-width: 600px) { | ||
form { | ||
width: 80vw; | ||
min-width: initial; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import React, { useEffect, useState } from "react"; | ||
import { | ||
PaymentElement, | ||
useStripe, | ||
useElements | ||
} from "@stripe/react-stripe-js"; | ||
import { useSelector } from 'react-redux'; | ||
import { selectCurrentOrder } from "../features/order/orderSlice"; | ||
|
||
export default function CheckoutForm() { | ||
const stripe = useStripe(); | ||
const elements = useElements(); | ||
const currentOrder = useSelector(selectCurrentOrder); | ||
|
||
const [message, setMessage] = useState(null); | ||
const [isLoading, setIsLoading] = useState(false); | ||
|
||
useEffect(() => { | ||
if (!stripe) { | ||
return; | ||
} | ||
|
||
const clientSecret = new URLSearchParams(window.location.search).get( | ||
"payment_intent_client_secret" | ||
); | ||
|
||
if (!clientSecret) { | ||
return; | ||
} | ||
|
||
stripe.retrievePaymentIntent(clientSecret).then(({ paymentIntent }) => { | ||
switch (paymentIntent.status) { | ||
case "succeeded": | ||
setMessage("Payment succeeded!"); | ||
break; | ||
case "processing": | ||
setMessage("Your payment is processing."); | ||
break; | ||
case "requires_payment_method": | ||
setMessage("Your payment was not successful, please try again."); | ||
break; | ||
default: | ||
setMessage("Something went wrong."); | ||
break; | ||
} | ||
}); | ||
}, [stripe]); | ||
|
||
const handleSubmit = async (e) => { | ||
e.preventDefault(); | ||
|
||
if (!stripe || !elements) { | ||
// Stripe.js hasn't yet loaded. | ||
// Make sure to disable form submission until Stripe.js has loaded. | ||
return; | ||
} | ||
|
||
setIsLoading(true); | ||
|
||
const { error } = await stripe.confirmPayment({ | ||
elements, | ||
confirmParams: { | ||
// Make sure to change this to your payment completion page | ||
return_url: `http://localhost:3000/order-success/${currentOrder.id}`, | ||
}, | ||
}); | ||
|
||
// This point will only be reached if there is an immediate error when | ||
// confirming the payment. Otherwise, your customer will be redirected to | ||
// your `return_url`. For some payment methods like iDEAL, your customer will | ||
// be redirected to an intermediate site first to authorize the payment, then | ||
// redirected to the `return_url`. | ||
if (error.type === "card_error" || error.type === "validation_error") { | ||
setMessage(error.message); | ||
} else { | ||
setMessage("An unexpected error occurred."); | ||
} | ||
|
||
setIsLoading(false); | ||
}; | ||
|
||
const paymentElementOptions = { | ||
layout: "tabs" | ||
} | ||
|
||
return ( | ||
<form id="payment-form" onSubmit={handleSubmit}> | ||
<PaymentElement id="payment-element" options={paymentElementOptions} /> | ||
<button disabled={isLoading || !stripe || !elements} id="submit"> | ||
<span id="button-text"> | ||
{isLoading ? <div className="spinner" id="spinner"></div> : "Pay now"} | ||
</span> | ||
</button> | ||
{/* Show any error or success messages */} | ||
{message && <div id="payment-message">{message}</div>} | ||
</form> | ||
); | ||
} |
Oops, something went wrong.