You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
3.5 KiB
147 lines
3.5 KiB
<script>
|
|
import { createEventDispatcher, onMount } from "svelte";
|
|
import api from "$/client/api.js";
|
|
import { stripe_public, base_host } from "$/client/config.js";
|
|
import Spinner from "$/client/components/Spinner.svelte";
|
|
import { fade } from "svelte/transition";
|
|
import { inject_remote } from "$/client/components/Source.svelte";
|
|
|
|
export let appearance = {
|
|
theme: 'flat',
|
|
variables: {
|
|
borderRadius: "5px", // hate this
|
|
}
|
|
};
|
|
|
|
const dispatch = createEventDispatcher();
|
|
let state = "loading"; // loading, ready,
|
|
let stripe;
|
|
let elements;
|
|
let error_message = "Please correct the form errors.";
|
|
|
|
const initialize_stripe = async (data) => {
|
|
await inject_remote(document, "https://js.stripe.com/v3/", () => {
|
|
stripe = Stripe(stripe_public.client_id);
|
|
});
|
|
|
|
// initializing stripe
|
|
const { clientSecret } = data;
|
|
|
|
elements = stripe.elements({ appearance, clientSecret });
|
|
|
|
const paymentElement = elements.create("payment");
|
|
paymentElement.mount("#payment-element");
|
|
}
|
|
|
|
const configure_payment = async () => {
|
|
// need to setup stripe first
|
|
const [status, data] = await api.post("/api/payments/stripe");
|
|
|
|
if(status === 200) {
|
|
await initialize_stripe(data);
|
|
state = "ready";
|
|
} else {
|
|
state = "error";
|
|
dispatch("error", data);
|
|
}
|
|
}
|
|
|
|
const handle_error = (error) => {
|
|
state = "error";
|
|
|
|
if (error.type === "card_error" || error.type === "validation_error") {
|
|
// can't dispatch error here because stripe handles these?
|
|
console.log("Stripe error", error);
|
|
error_message = error.message || "Please correct the form errors.";
|
|
} else {
|
|
dispatch("error", error);
|
|
}
|
|
}
|
|
|
|
|
|
const handle_submit = async () => {
|
|
state = "submit";
|
|
dispatch("click");
|
|
|
|
const { error } = await stripe.confirmPayment({
|
|
elements,
|
|
confirmParams: {
|
|
return_url: `${base_host}/api/payments/stripe`,
|
|
}
|
|
});
|
|
|
|
if(error) {
|
|
handle_error(error);
|
|
} else {
|
|
state = "submit_done";
|
|
}
|
|
}
|
|
|
|
onMount(configure_payment);
|
|
</script>
|
|
|
|
<style>
|
|
#load-cover {
|
|
min-height: 40ex;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
#load-cover > p {
|
|
color: var(--value4);
|
|
}
|
|
|
|
#payment-form {
|
|
background-color: var(--value9);
|
|
color: var(--value0);
|
|
padding: 0.5rem;
|
|
border-radius: var(--border-radius);
|
|
}
|
|
|
|
#payment-form button#submit {
|
|
color: var(--value9);
|
|
background-color: var(--value0);
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
#stripe-notice {
|
|
width: 100%;
|
|
text-align: center;
|
|
color: var(--value3);
|
|
font-size: 0.8em;
|
|
}
|
|
|
|
error {
|
|
max-width: 400px;
|
|
}
|
|
</style>
|
|
|
|
<div>
|
|
<form id="payment-form">
|
|
<div class="stacked">
|
|
<div id="load-cover" class="layer" class:top={ state === "loading"}>
|
|
{#if state === "loading"}
|
|
<p out:fade|local={{ delay: 1000}}>Preparing payment form...</p>
|
|
{/if}
|
|
</div>
|
|
<div id="payment-element" class="layer">
|
|
<!--Stripe.js injects the Payment Element-->
|
|
</div>
|
|
</div>
|
|
<br>
|
|
{#if state === "error"}
|
|
<error in:fade|local={{ delay: 1000}}>
|
|
{ error_message }
|
|
</error>
|
|
{/if}
|
|
<button disabled={ state === "submit" } id="submit" on:click|preventDefault={ handle_submit }>
|
|
{#if state === "submit"}
|
|
<Spinner size="24" color="#fff"/>
|
|
{:else}
|
|
Pay with Credit Card
|
|
{/if}
|
|
</button>
|
|
<div id="stripe-notice">Credit cards securely processed by <a href="https://stripe.com">Stripe</a>.</div>
|
|
</form>
|
|
</div>
|
|
|