This is the template project that's checked out and configured when you run the bando-up command from ljsthw-bandolier. This is where the code really lives.
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.
 
 
 
 
bandolier-template/client/components/Paypal.svelte

182 lines
5.3 KiB

<svelte:head>
<meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Ensures optimal rendering on mobile devices. -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <!-- Optimal Internet Explorer compatibility -->
</svelte:head>
<script>
import { onMount, createEventDispatcher } from 'svelte';
import { inject_remote } from './Source.svelte';
import Spinner from './Spinner.svelte';
import { fake_payments, paypal_public } from '$/client/config.js';
import api from "$/client/api.js";
import { log } from "$/client/logging.js";
import { fade } from "svelte/transition";
export let product;
export let credit_card = false;
export let disabled = false;
let paypal_ready = false;
let paypal_sending = false;
let paypal_loaded = false;
let show_warning = false;
const dispatch = createEventDispatcher();
const dispatch_finished = async (paypal) => {
paypal_sending = false;
if(paypal.status === "COMPLETED") {
let payment = {
system: 'paypal',
sys_primary_id: paypal.id,
sys_secondary_id: paypal.purchase_units[0].payments.captures[0].id,
sys_created_on: paypal.create_time,
status: 'complete',
};
const [status, data] = await api.post('/api/payments/paypal', payment);
if(status == 200) {
log.debug("Paypal request returned.");
// TODO: confirm no errors on reply and the returned data matches
payment.internal_id = data.internal_id;
dispatch('finished', payment);
} else {
log.debug("ERROR RESPONSE", status, data);
dispatch("error", {message: "Validation error.", payment});
}
} else {
dispatch("error", {message: `dispatch_finished called but status is ${paypal.status}`});
}
}
const dispatch_canceled = (what) => dispatch('canceled', {paypal: what});
const dispatch_error = (what) => dispatch('error', {paypal: what});
const dispatch_loading = (what) => dispatch('loading', {paypal: what});
const load_paypal = () => {
paypal_loaded = true;
dispatch_loading();
paypal.Buttons(
{
style: {
layout: 'vertical',
color: 'black',
label: 'pay',
tagline: false
},
onInit: () => {
dispatch("init");
paypal_ready = true;
},
onClick: () => {
dispatch("click");
show_warning = true;
},
createOrder: (data, actions) => {
return actions.order.create({
purchase_units: [{
amount: { value: `${ product.price }` },
description: `${product.title}: ${product.description}`,
}],
application_context: {
brand_name: product.title,
landing_page: 'BILLING',
shipping_preference: 'NO_SHIPPING',
user_action: 'PAY_NOW',
},
});
},
onApprove: (data, actions) => {
paypal_sending = true;
// do a warning here about the paypal BS?
return actions.order.capture().then((details) => {
if(details.error === 'INSTRUMENT_DECLINED') {
dispatch("error", {
message: "Instrument declined.",
instrument_declined: true
});
return false;
} else {
dispatch_finished(details);
return true;
}
});
},
onCancel: (data) => dispatch_canceled(data),
onError: (err) => dispatch_error(err),
}).render('#paypal-button-container');
}
const install_paypal = async () => {
if(disabled) return;
if(fake_payments) {
log.debug("fake_payments set to true, sending error");
dispatch("error", { system: "paypal", sys_primary_id: 11333, status: "complete", invoice_id: "FAKE" });
} else if(!window.paypal) {
let disable_funding = ['credit', 'bancontact',
'blik', 'eps', 'giropay', 'ideal', 'mybank',
'p24', 'sepa', 'sofort', 'venmo'];
if(!credit_card) disable_funding.push('card');
let script_url = `https://www.paypal.com/sdk/js?client-id=${paypal_public.client_id}&disable-funding=${disable_funding.join(',')}&debug=false&intent=capture&commit=true`;
await inject_remote(document, script_url, load_paypal);
} else {
load_paypal();
}
}
onMount(async () => {
await install_paypal();
});
</script>
<style>
button.paypal-activate {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
cursor: pointer;
width: 100%;
margin-bottom: 0.5rem;
background-color: var(--color-bg-secondary);
color: var(--color);
}
.paypal-logo {
max-height: 1.3rem;
margin: 0.5rem;
vertical-align: bottom;
width: unset;
}
</style>
{#if paypal_sending}
<p>Please wait while we confirm your payment...</p>
<Spinner />
{/if}
{#if !paypal_loaded}
<Spinner />
{/if}
<div id="paypal-button-container">
</div>
{#if show_warning}
<callout in:fade|local="{{ delay: 5000, duration: 500}}" class="info" style="max-height: min-content; z-index: -1000;"><span>Check your <b>Street Address</b> if your submit "does nothing". Paypal will add a <b>Street Address</b> for validation, but not tell you.</span></callout>
{/if}