Skip to content

Instantly share code, notes, and snippets.

@Tchangang
Created May 6, 2020 16:22
Show Gist options
  • Save Tchangang/a697e047e548dbb69d2834820c6cce62 to your computer and use it in GitHub Desktop.
Save Tchangang/a697e047e548dbb69d2834820c6cce62 to your computer and use it in GitHub Desktop.
Front to handle card info
var stripe = Stripe("xxxxxxxxxxxxxxxxxxx");
var elements = stripe.elements();
var style = {
base: {
color: "#32325d",
fontFamily: 'Arial, sans-serif',
fontSmoothing: "antialiased",
fontSize: "16px",
"::placeholder": {
color: "#32325d"
}
},
invalid: {
fontFamily: 'Arial, sans-serif',
color: "#fa755a",
iconColor: "#fa755a"
}
};
var card = elements.create("card", { style: style });
// Stripe injects an iframe into the DOM
card.mount("#card-element");
card.on("change", function (event) {
// Disable the Pay button if there are no card details in the Element
document.querySelector("button").disabled = event.empty;
document.querySelector("#card-errors").textContent = event.error ? event.error.message : "";
});
// Show a spinner on payment submission
function setLoading(isLoading) {
if (isLoading) {
// Disable the button and show a spinner
document.querySelector("button").disabled = true;
document.querySelector("#spinner").classList.remove("hidden");
document.querySelector("#button-text").classList.add("hidden");
} else {
document.querySelector("button").disabled = false;
document.querySelector("#spinner").classList.add("hidden");
document.querySelector("#button-text").classList.remove("hidden");
}
}
var form = document.getElementById("payment-form");
form.addEventListener("submit", async function(event) {
event.preventDefault();
setLoading(true);
// Complete payment when the submit button is clicked
const paymentMethod = await stripe.createPaymentMethod({ type: 'card', card }); // Create paymentMethod directly on stripe
console.log(paymentMethod);
try {
// Attach payment method to customer
const paymentAttached = await (await fetch("http://localhost:4242/attachpayment", { method: "POST", headers: {
"Content-Type": "application/json"
}, body: JSON.stringify({
id: paymentMethod.paymentMethod.id,
})
})).json();
console.log('paymentAttached', paymentAttached);
// Create subscription
const subscriptionCreated = await (await fetch("http://localhost:4242/subscription", { method: "POST", headers: {
"Content-Type": "application/json"
}, body: JSON.stringify({
id: paymentMethod.paymentMethod.id,
})
})).json();
console.log('subscriptionCreated', subscriptionCreated);
// If we use a card that need sca (4000002500003155), we need to confirm the payment
const { latest_invoice } = subscriptionCreated;
const { payment_intent } = latest_invoice;
if (payment_intent) {
const { client_secret, status } = payment_intent;
if (['requires_source_action', 'requires_payment_method', 'requires_confirmation'].includes(status)) {
const result = await stripe.confirmCardPayment(client_secret); // confirm payment there -> stripe will display a modal to continue
if (result.error) {
setLoading(false); // stop spinner there
alert(result.error);
return;
}
}
}
const subscriptionUpdated = await (await fetch("http://localhost:4242/subscription/".concat(subscriptionCreated.id), { method: "GET", headers: {
"Content-Type": "application/json"
}})).json();
console.log('subscriptionUpdated', subscriptionUpdated);
setLoading(false); // stop spinner there
alert('subscription ready');
} catch (e) {
setLoading(false); // stop spinner there
console.log('error', e);
alert(e.message);
}
});
/* Variables */
* {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 16px;
-webkit-font-smoothing: antialiased;
display: flex;
justify-content: center;
align-content: center;
height: 100vh;
width: 100vw;
}
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;
}
input {
border-radius: 6px;
margin-bottom: 6px;
padding: 12px;
border: 1px solid rgba(50, 50, 93, 0.1);
height: 44px;
font-size: 16px;
width: 100%;
background: white;
}
.result-message {
line-height: 22px;
font-size: 16px;
}
.result-message a {
color: rgb(89, 111, 214);
font-weight: 600;
text-decoration: none;
}
.hidden {
display: none;
}
.card-error {
color: rgb(105, 115, 134);
text-align: left;
font-size: 13px;
line-height: 17px;
margin-top: 12px;
}
#card-element {
border-radius: 4px 4px 0 0 ;
padding: 12px;
border: 1px solid rgba(50, 50, 93, 0.1);
height: 44px;
width: 100%;
background: white;
}
#payment-request-button {
margin-bottom: 32px;
}
/* Buttons and links */
button {
background: #5469d4;
color: #ffffff;
font-family: Arial, sans-serif;
border-radius: 0 0 4px 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%;
}
button:hover {
filter: contrast(115%);
}
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;
}
@-webkit-keyframes loading {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@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;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Accept a card payment</title>
<meta name="description" content="A demo of a card payment on Stripe" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="global.css" />
<script src="https://js.stripe.com/v3/"></script>
<script src="./client.js" defer></script>
</head>
<body>
<form id="payment-form">
<div>
<h1>LaGrowthMachine</h1>
</div>
<div id="card-element"><!--Stripe.js injects the Card Element--></div>
<button id="submit">
<div class="spinner hidden" id="spinner"></div>
<span id="button-text">Pay</span>
</button>
</form>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment