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.
183 lines
5.3 KiB
183 lines
5.3 KiB
2 years ago
|
<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}
|