Add paypal checkout
This commit is contained in:
7
res/static/img/payment_providers/bitcoin.svg
Normal file
7
res/static/img/payment_providers/bitcoin.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="64" width="64" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<g transform="translate(0.00630876,-0.00301984)">
|
||||||
|
<path fill="#f7931a" d="m63.033,39.744c-4.274,17.143-21.637,27.576-38.782,23.301-17.138-4.274-27.571-21.638-23.295-38.78,4.272-17.145,21.635-27.579,38.775-23.305,17.144,4.274,27.576,21.64,23.302,38.784z"/>
|
||||||
|
<path fill="#FFF" d="m46.103,27.444c0.637-4.258-2.605-6.547-7.038-8.074l1.438-5.768-3.511-0.875-1.4,5.616c-0.923-0.23-1.871-0.447-2.813-0.662l1.41-5.653-3.509-0.875-1.439,5.766c-0.764-0.174-1.514-0.346-2.242-0.527l0.004-0.018-4.842-1.209-0.934,3.75s2.605,0.597,2.55,0.634c1.422,0.355,1.679,1.296,1.636,2.042l-1.638,6.571c0.098,0.025,0.225,0.061,0.365,0.117-0.117-0.029-0.242-0.061-0.371-0.092l-2.296,9.205c-0.174,0.432-0.615,1.08-1.609,0.834,0.035,0.051-2.552-0.637-2.552-0.637l-1.743,4.019,4.569,1.139c0.85,0.213,1.683,0.436,2.503,0.646l-1.453,5.834,3.507,0.875,1.439-5.772c0.958,0.26,1.888,0.5,2.798,0.726l-1.434,5.745,3.511,0.875,1.453-5.823c5.987,1.133,10.489,0.676,12.384-4.739,1.527-4.36-0.076-6.875-3.226-8.515,2.294-0.529,4.022-2.038,4.483-5.155zm-8.022,11.249c-1.085,4.36-8.426,2.003-10.806,1.412l1.928-7.729c2.38,0.594,10.012,1.77,8.878,6.317zm1.086-11.312c-0.99,3.966-7.1,1.951-9.082,1.457l1.748-7.01c1.982,0.494,8.365,1.416,7.334,5.553z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
1
res/static/img/payment_providers/dogecoin.svg
Normal file
1
res/static/img/payment_providers/dogecoin.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 56 KiB |
18
res/static/img/payment_providers/mollie.svg
Normal file
18
res/static/img/payment_providers/mollie.svg
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 26.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 320 94" style="enable-background:new 0 0 320 94;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<path class="st0" d="M289.3,44.3c6.9,0,13.2,4.5,15.4,11h-30.7C276.1,48.9,282.3,44.3,289.3,44.3z M320,60.9c0-8-3.1-15.6-8.8-21.4
|
||||||
|
c-5.7-5.8-13.3-9-21.3-9h-0.4c-8.3,0.1-16.2,3.4-22.1,9.3c-5.9,5.9-9.2,13.7-9.3,22c-0.1,8.5,3.2,16.5,9.2,22.6
|
||||||
|
c6.1,6.1,14.1,9.5,22.6,9.5h0c11.2,0,21.7-6,27.4-15.6l0.7-1.2l-12.6-6.2l-0.6,1c-3.1,5.2-8.6,8.2-14.7,8.2
|
||||||
|
c-7.7,0-14.4-5.1-16.5-12.5H320V60.9z M241.2,19.8c-5.5,0-9.9-4.4-9.9-9.9c0-5.5,4.4-9.9,9.9-9.9s9.9,4.4,9.9,9.9
|
||||||
|
C251.2,15.3,246.7,19.8,241.2,19.8z M233.6,92.7h15.2V31.8h-15.2V92.7z M204.5,1.3h15.2v91.5h-15.2V1.3z M175.4,92.7h15.2V1.3h-15.2
|
||||||
|
V92.7z M135.3,79c-9.2,0-16.8-7.5-16.8-16.7c0-9.2,7.5-16.7,16.8-16.7s16.8,7.5,16.8,16.7C152.1,71.5,144.6,79,135.3,79z
|
||||||
|
M135.3,30.5c-17.6,0-31.8,14.2-31.8,31.7S117.8,94,135.3,94c17.5,0,31.8-14.2,31.8-31.7S152.9,30.5,135.3,30.5z M70.4,30.6
|
||||||
|
c-0.8-0.1-1.6-0.1-2.4-0.1c-7.7,0-15,3.1-20.2,8.7c-5.2-5.5-12.5-8.7-20.1-8.7C12.4,30.5,0,42.9,0,58v34.7h14.9V58.5
|
||||||
|
c0-6.3,5.2-12.1,11.3-12.7c0.4,0,0.9-0.1,1.3-0.1c6.9,0,12.5,5.6,12.5,12.5v34.6h15.2V58.4c0-6.3,5.2-12.1,11.3-12.7
|
||||||
|
c0.4,0,0.9-0.1,1.3-0.1c6.9,0,12.5,5.6,12.6,12.4v34.7h15.2V58.5c0-7-2.6-13.6-7.2-18.8C83.7,34.4,77.3,31.2,70.4,30.6z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
1
res/static/img/payment_providers/monero.svg
Normal file
1
res/static/img/payment_providers/monero.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3756.09 3756.49"><title>monero</title><path d="M4128,2249.81C4128,3287,3287.26,4127.86,2250,4127.86S372,3287,372,2249.81,1212.76,371.75,2250,371.75,4128,1212.54,4128,2249.81Z" transform="translate(-371.96 -371.75)" style="fill:#fff"/><path id="_149931032" data-name=" 149931032" d="M2250,371.75c-1036.89,0-1879.12,842.06-1877.8,1878,0.26,207.26,33.31,406.63,95.34,593.12h561.88V1263L2250,2483.57,3470.52,1263v1579.9h562c62.12-186.48,95-385.85,95.37-593.12C4129.66,1212.76,3287,372,2250,372Z" transform="translate(-371.96 -371.75)" style="fill:#f26822"/><path id="_149931160" data-name=" 149931160" d="M1969.3,2764.17l-532.67-532.7v994.14H1029.38l-384.29.07c329.63,540.8,925.35,902.56,1604.91,902.56S3525.31,3766.4,3855,3225.6H3063.25V2231.47l-532.7,532.7-280.61,280.61-280.62-280.61h0Z" transform="translate(-371.96 -371.75)" style="fill:#4d4d4d"/></svg>
|
After Width: | Height: | Size: 940 B |
1
res/static/img/payment_providers/paypal_full.svg
Normal file
1
res/static/img/payment_providers/paypal_full.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="772.875" height="720" viewBox="0 0 204.49 190.5" xmlns:v="https://vecta.io/nano"><g transform="matrix(.852554 0 0 .852554 680.18141 153.25756)"><path clip-path="none" d="M-788.334-20.913a1.42 1.42 0 0 0-1.399 1.197l-8.067 51.163a1.31 1.31 0 0 0 1.292 1.514h9.569a1.42 1.42 0 0 0 1.399-1.195l2.368-15.025a1.42 1.42 0 0 1 1.399-1.195h8.697c10.165 0 18.792-7.415 20.368-17.465 1.59-10.133-6.325-18.973-17.547-18.994zm9.301 11.422h6.96c5.73 0 7.594 3.38 7.004 7.119-.59 3.747-3.485 6.507-9.029 6.507h-7.084zm45.787 3.48c-2.416.009-5.197.503-8.318 1.803-7.158 2.983-10.597 9.151-12.056 13.647 0 0-4.647 13.717 5.852 21.254 0 0 9.736 7.253 20.698-.449l-.188 1.203a1.31 1.31 0 0 0 1.292 1.514h9.083c.696 0 1.288-.508 1.397-1.197l5.526-35.037a1.31 1.31 0 0 0-1.292-1.514h-9.083c-.698 0-1.29.508-1.399 1.195l-.296 1.885h-.002s-3.966-4.331-11.215-4.304zm.297 11.067c1.043 0 1.997.142 2.852.418 3.918 1.259 6.142 5.021 5.498 9.103-.793 5.026-4.914 8.728-10.198 8.728-1.043 0-1.999-.143-2.854-.418-3.918-1.259-6.155-5.023-5.51-9.105.793-5.024 4.928-8.726 10.212-8.726z" fill="#003087"/><path clip-path="none" d="M-657.536-20.913c-.696 0-1.288.508-1.397 1.197l-8.069 51.163a1.31 1.31 0 0 0 1.294 1.514h9.569c.696 0 1.288-.507 1.397-1.195l2.37-15.025a1.42 1.42 0 0 1 1.399-1.195h8.697c10.165 0 18.792-7.415 20.368-17.465 1.59-10.133-6.327-18.973-17.549-18.994zm9.301 11.422h6.96c5.73 0 7.596 3.38 7.006 7.119-.59 3.747-3.486 6.507-9.031 6.507h-7.084zm45.789 3.48c-2.416.009-5.197.503-8.318 1.803-7.158 2.983-10.599 9.151-12.058 13.647 0 0-4.646 13.717 5.854 21.254 0 0 9.736 7.253 20.698-.449l-.19 1.203a1.31 1.31 0 0 0 1.294 1.514h9.081a1.42 1.42 0 0 0 1.399-1.197l5.526-35.037a1.31 1.31 0 0 0-1.294-1.514h-9.081c-.698 0-1.29.508-1.399 1.195l-.297 1.885s-3.966-4.331-11.215-4.304zm.295 11.067c1.043 0 1.999.142 2.854.418 3.918 1.259 6.14 5.021 5.497 9.103-.793 5.026-4.914 8.728-10.198 8.728-1.043 0-1.997-.143-2.852-.418-3.918-1.259-6.155-5.023-5.51-9.105.792-5.024 4.926-8.726 10.21-8.726z" fill="#0070e0"/><path clip-path="none" d="M-705.651-4.787a1.06 1.06 0 0 0-1.01 1.387l9.956 30.9-9.004 14.564c-.436.707.071 1.618.903 1.618h10.639a1.77 1.77 0 0 0 1.515-.853l27.807-46.007c.428-.707-.081-1.611-.907-1.611h-10.639a1.77 1.77 0 0 0-1.523.867l-10.946 18.483-5.557-18.345c-.182-.597-.731-1.004-1.356-1.004z" fill="#003087"/><path clip-path="none" d="M-568.839-20.913c-.696 0-1.288.508-1.397 1.197l-8.069 51.163a1.31 1.31 0 0 0 1.294 1.514h9.567c.696 0 1.288-.507 1.397-1.195l8.071-51.165a1.31 1.31 0 0 0-1.296-1.514z" fill="#0070e0"/><path clip-path="none" d="M-687.026-152.855c-1.907 0-3.532 1.387-3.829 3.272l-6.365 40.358-5.848 37.085-.004.031.007-.031 5.848-37.085c.297-1.885 1.92-3.272 3.828-3.272h18.646c18.765 0 34.693-13.691 37.601-32.243a30.77 30.77 0 0 0 .367-4.183v-.002h-.002c-4.769-2.502-10.369-3.931-16.505-3.931z" fill="#001c64"/><path clip-path="none" d="M-636.776-148.925c-.026 1.378-.146 2.775-.367 4.185-2.909 18.552-18.836 32.243-37.601 32.243h-18.646c-1.907 0-3.53 1.387-3.828 3.272l-5.848 37.085-3.67 23.267a3.15 3.15 0 0 0 3.111 3.639h20.239c1.909 0 3.532-1.387 3.83-3.272l5.331-33.813a3.88 3.88 0 0 1 3.829-3.274h11.918c18.765 0 34.691-13.69 37.599-32.241 2.065-13.17-4.562-25.15-15.897-31.091z" fill="#0070e0"/><path clip-path="none" d="M-710.02-179.763a3.88 3.88 0 0 0-3.829 3.274l-15.882 100.708a3.15 3.15 0 0 0 3.111 3.641h23.552l5.848-37.085 6.365-40.358c.297-1.885 1.922-3.272 3.829-3.272h33.743c6.137 0 11.737 1.43 16.507 3.929.325-16.89-13.612-30.836-32.775-30.836z" fill="#003087"/></g></svg>
|
After Width: | Height: | Size: 3.5 KiB |
166
svelte/src/admin_panel/InvoiceVAT.svelte
Normal file
166
svelte/src/admin_panel/InvoiceVAT.svelte
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import { formatDate } from "util/Formatting";
|
||||||
|
import LoadingIndicator from "util/LoadingIndicator.svelte";
|
||||||
|
import Euro from "util/Euro.svelte";
|
||||||
|
import { get_endpoint } from "lib/PixeldrainAPI";
|
||||||
|
|
||||||
|
type Invoice = {
|
||||||
|
id: string
|
||||||
|
time: string
|
||||||
|
amount: number
|
||||||
|
vat: number
|
||||||
|
country: string
|
||||||
|
payment_method: string
|
||||||
|
status: string
|
||||||
|
}
|
||||||
|
|
||||||
|
let loading = true
|
||||||
|
let invoices: Invoice[] = []
|
||||||
|
|
||||||
|
let year = 0
|
||||||
|
let month = 0
|
||||||
|
let month_str = ""
|
||||||
|
|
||||||
|
type Total = {
|
||||||
|
count: number
|
||||||
|
amount: number
|
||||||
|
vat: number
|
||||||
|
}
|
||||||
|
let totals: { [id: string]: Total } = {}
|
||||||
|
const add_total = (i: Invoice) => {
|
||||||
|
if (totals[i.payment_method] === undefined) {
|
||||||
|
totals[i.payment_method] = {count: 0, amount: 0, vat: 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
totals[i.payment_method].count++
|
||||||
|
totals[i.payment_method].amount += i.amount
|
||||||
|
totals[i.payment_method].vat += i.vat
|
||||||
|
}
|
||||||
|
|
||||||
|
const get_invoices = async () => {
|
||||||
|
loading = true;
|
||||||
|
month_str = year + "-" + ("00"+(month)).slice(-2)
|
||||||
|
try {
|
||||||
|
const resp = await fetch(get_endpoint()+"/admin/invoices/"+month_str);
|
||||||
|
if(resp.status >= 400) {
|
||||||
|
throw new Error(await resp.text());
|
||||||
|
}
|
||||||
|
|
||||||
|
let resp_json = await resp.json() as Invoice[];
|
||||||
|
|
||||||
|
resp_json.sort((a, b) => {
|
||||||
|
if (a.status !== b.status) {
|
||||||
|
return a.status.localeCompare(b.status)
|
||||||
|
}
|
||||||
|
|
||||||
|
const date_a = new Date(a.time)
|
||||||
|
const date_b = new Date(b.time)
|
||||||
|
return date_a.getTime() - date_b.getTime()
|
||||||
|
})
|
||||||
|
|
||||||
|
totals = {}
|
||||||
|
resp_json.forEach(row => {
|
||||||
|
if (row.status === "paid") {
|
||||||
|
add_total(row)
|
||||||
|
}
|
||||||
|
if (row.status === "chargeback") {
|
||||||
|
alert(row.vat)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
invoices = resp_json
|
||||||
|
} catch (err) {
|
||||||
|
alert(err);
|
||||||
|
} finally {
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const last_month = () => {
|
||||||
|
month--
|
||||||
|
if (month === 0) {
|
||||||
|
month = 12
|
||||||
|
year--
|
||||||
|
}
|
||||||
|
|
||||||
|
get_invoices()
|
||||||
|
}
|
||||||
|
const next_month = () => {
|
||||||
|
month++
|
||||||
|
if (month === 13) {
|
||||||
|
month = 1
|
||||||
|
year++
|
||||||
|
}
|
||||||
|
|
||||||
|
get_invoices()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
let now = new Date()
|
||||||
|
year = now.getFullYear()
|
||||||
|
month = now.getMonth()+1
|
||||||
|
get_invoices()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<LoadingIndicator loading={loading}/>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>{month_str}</h3>
|
||||||
|
<div class="toolbar">
|
||||||
|
<button on:click={last_month}>
|
||||||
|
<i class="icon">chevron_left</i>
|
||||||
|
Previous month
|
||||||
|
</button>
|
||||||
|
<div class="toolbar_spacer"></div>
|
||||||
|
<button on:click={next_month}>
|
||||||
|
Next month
|
||||||
|
<i class="icon">chevron_right</i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#each Object.entries(totals) as [key, tot]}
|
||||||
|
{key} ({tot.count})<br/>
|
||||||
|
Amount:<Euro amount={tot.amount}/><br/>
|
||||||
|
VAT: <Euro amount={tot.vat}/><br/>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
<div class="table_scroll">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>Time</td>
|
||||||
|
<td>Amount</td>
|
||||||
|
<td>VAT</td>
|
||||||
|
<td>Country</td>
|
||||||
|
<td>Method</td>
|
||||||
|
<td>Status</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each invoices as row (row.id)}
|
||||||
|
<tr>
|
||||||
|
<td>{formatDate(row.time)}</td>
|
||||||
|
<td><Euro amount={row.amount}/></td>
|
||||||
|
<td><Euro amount={row.vat}/></td>
|
||||||
|
<td>{row.country}</td>
|
||||||
|
<td>{row.payment_method}</td>
|
||||||
|
<td>{row.status}</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.toolbar > * { flex: 0 0 auto; }
|
||||||
|
.toolbar_spacer { flex: 1 1 auto; }
|
||||||
|
</style>
|
@@ -9,6 +9,7 @@ import EmailReporters from "./EmailReporters.svelte";
|
|||||||
import MollieSettlements from "./MollieSettlements.svelte";
|
import MollieSettlements from "./MollieSettlements.svelte";
|
||||||
import PayPalTaxes from "./PayPalTaxes.svelte";
|
import PayPalTaxes from "./PayPalTaxes.svelte";
|
||||||
import UserBans from "./user_bans/UserBans.svelte";
|
import UserBans from "./user_bans/UserBans.svelte";
|
||||||
|
import InvoiceVat from "./InvoiceVAT.svelte";
|
||||||
|
|
||||||
let pages = [
|
let pages = [
|
||||||
{
|
{
|
||||||
@@ -62,6 +63,11 @@ let pages = [
|
|||||||
title: "Paypal Taxes",
|
title: "Paypal Taxes",
|
||||||
icon: "paypal",
|
icon: "paypal",
|
||||||
component: PayPalTaxes,
|
component: PayPalTaxes,
|
||||||
|
}, {
|
||||||
|
path: "/admin/invoices",
|
||||||
|
title: "Invoices",
|
||||||
|
icon: "receipt",
|
||||||
|
component: InvoiceVat,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import CreditDeposit from "layout/CreditDeposit.svelte";
|
||||||
import LoginRegister from "login/LoginRegister.svelte";
|
import LoginRegister from "login/LoginRegister.svelte";
|
||||||
import MollieDeposit from "user_home/MollieDeposit.svelte";
|
|
||||||
import Euro from "util/Euro.svelte";
|
import Euro from "util/Euro.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -19,13 +19,13 @@ import Euro from "util/Euro.svelte";
|
|||||||
balance is <Euro amount={window.user.balance_micro_eur}/>. Use
|
balance is <Euro amount={window.user.balance_micro_eur}/>. Use
|
||||||
the form below to top up your balance.
|
the form below to top up your balance.
|
||||||
</p>
|
</p>
|
||||||
<MollieDeposit/>
|
<CreditDeposit/>
|
||||||
{:else}
|
{:else}
|
||||||
<p>
|
<p>
|
||||||
You are currently logged in as '{window.user.username}'. Use the
|
You are currently logged in as '{window.user.username}'. Use the
|
||||||
form below to activate prepaid on this account.
|
form below to activate prepaid on this account.
|
||||||
</p>
|
</p>
|
||||||
<MollieDeposit/>
|
<CreditDeposit/>
|
||||||
{/if}
|
{/if}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -4,11 +4,13 @@ import Euro from "util/Euro.svelte";
|
|||||||
import LoadingIndicator from "util/LoadingIndicator.svelte";
|
import LoadingIndicator from "util/LoadingIndicator.svelte";
|
||||||
import { countries } from "country-data-list";
|
import { countries } from "country-data-list";
|
||||||
import { get_endpoint, get_misc_vat_rate } from "lib/PixeldrainAPI";
|
import { get_endpoint, get_misc_vat_rate } from "lib/PixeldrainAPI";
|
||||||
|
import CreditDepositNav from "./CreditDepositNav.svelte";
|
||||||
|
|
||||||
let loading = false
|
let loading = false
|
||||||
let amount = 20
|
let amount = 20
|
||||||
let country: typeof countries.all[0] = null
|
let country: typeof countries.all[0] = null
|
||||||
let country_input = ""
|
let country_input = ""
|
||||||
|
let provider: PaymentProvider = null
|
||||||
let vat = 0
|
let vat = 0
|
||||||
|
|
||||||
const amounts = [10, 20, 50, 100, 200, 500, 1000, 2000, 5000]
|
const amounts = [10, 20, 50, 100, 200, 500, 1000, 2000, 5000]
|
||||||
@@ -19,8 +21,30 @@ onMount(() => {
|
|||||||
country_input = checkout_country
|
country_input = checkout_country
|
||||||
set_country()
|
set_country()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const checkout_provider = window.localStorage.getItem("checkout_provider")
|
||||||
|
for (const p of providers) {
|
||||||
|
if (p.name === checkout_provider) {
|
||||||
|
set_provider(p)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
type PaymentProvider = {
|
||||||
|
icon: string,
|
||||||
|
name: string,
|
||||||
|
label: string,
|
||||||
|
crypto?: boolean,
|
||||||
|
};
|
||||||
|
const providers: PaymentProvider[] = [
|
||||||
|
{icon: "paypal_full", name: "paypal", label: "PayPal"},
|
||||||
|
{icon: "mollie", name: "mollie", label: "Mollie"},
|
||||||
|
{icon: "bitcoin", name: "btc", label: "Bitcoin", crypto: true},
|
||||||
|
{icon: "dogecoin", name: "doge", label: "Dogecoin", crypto: true},
|
||||||
|
{icon: "monero", name: "xmr", label: "Monero", crypto: true},
|
||||||
|
]
|
||||||
|
|
||||||
const payment_providers = [
|
const payment_providers = [
|
||||||
{icon: "paypal", name: "PayPal"},
|
{icon: "paypal", name: "PayPal"},
|
||||||
{icon: "creditcard", name: "Credit/debit"},
|
{icon: "creditcard", name: "Credit/debit"},
|
||||||
@@ -69,6 +93,13 @@ const set_country = async (e?: Event) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const set_provider = (p: PaymentProvider) => {
|
||||||
|
provider = p
|
||||||
|
|
||||||
|
// Cache the value for next checkout
|
||||||
|
window.localStorage.setItem("checkout_provider", p.name)
|
||||||
|
}
|
||||||
|
|
||||||
const checkout = async () => {
|
const checkout = async () => {
|
||||||
loading = true
|
loading = true
|
||||||
|
|
||||||
@@ -79,7 +110,7 @@ const checkout = async () => {
|
|||||||
|
|
||||||
const form = new FormData()
|
const form = new FormData()
|
||||||
form.set("amount", String(amount*1e6))
|
form.set("amount", String(amount*1e6))
|
||||||
form.set("network", "mollie")
|
form.set("network", provider.name)
|
||||||
form.set("country", country.alpha2)
|
form.set("country", country.alpha2)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -161,21 +192,32 @@ const format_country = (c: typeof countries.all[0]) => {
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{:else}
|
{:else if provider === null}
|
||||||
|
<CreditDepositNav bind:country={country} bind:provider={provider} bind:vat={vat}/>
|
||||||
|
|
||||||
<div style="display: flex;">
|
<h2>Please select a payment provider</h2>
|
||||||
<button on:click={() => country = null} style="flex: 0 0 auto;">
|
|
||||||
<i class="icon">chevron_left</i>
|
<div class="providers">
|
||||||
Change country
|
{#each providers as p (p.name)}
|
||||||
</button>
|
<button on:click={() => set_provider(p)}>
|
||||||
<div style="flex: 1 1 auto;"></div>
|
<img src="/res/img/payment_providers/{p.icon}.svg" alt={p.label} title={p.label}/>
|
||||||
<div style="flex: 0 0 auto; display: flex; gap: 0.25em; align-items: center;">
|
{p.label}
|
||||||
<span>Paying from</span>
|
</button>
|
||||||
<span style="font-size: 1.5em; line-height: 1em;">{country.emoji}</span>
|
{/each}
|
||||||
<span>{country.name} ({vat}% VAT)</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{:else}
|
||||||
|
<CreditDepositNav bind:country={country} bind:provider={provider} bind:vat={vat}/>
|
||||||
|
|
||||||
|
<p style="text-align: initial;" class="highlight_blue">
|
||||||
|
When paying with cryptocurrencies it is important that you pay the
|
||||||
|
<b>exact amount</b> stated on the order. If you pay too little, the
|
||||||
|
order fails. If you pay too much then the remaining credit will not
|
||||||
|
be added to your account. Pay close attention when sending a payment
|
||||||
|
from an online exchange, sometimes they will subtract the fees from
|
||||||
|
the amount sent which will cause the payment to fail.
|
||||||
|
</p>
|
||||||
|
|
||||||
<form class="amount_grid" on:submit|preventDefault={checkout}>
|
<form class="amount_grid" on:submit|preventDefault={checkout}>
|
||||||
<div class="span3">Please choose an amount</div>
|
<div class="span3">Please choose an amount</div>
|
||||||
{#each amounts as a}
|
{#each amounts as a}
|
||||||
@@ -237,6 +279,19 @@ const format_country = (c: typeof countries.all[0]) => {
|
|||||||
margin: 3px;
|
margin: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.providers {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(10em, 1fr));
|
||||||
|
}
|
||||||
|
.providers > button {
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
.providers > button > img {
|
||||||
|
max-width: 3em;
|
||||||
|
max-height: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
.amount_grid {
|
.amount_grid {
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
gap: 4px;
|
gap: 4px;
|
29
svelte/src/layout/CreditDepositNav.svelte
Normal file
29
svelte/src/layout/CreditDepositNav.svelte
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let country: {name?: string, emoji?: string} = null
|
||||||
|
export let provider: {label: string} = null
|
||||||
|
export let vat = 0
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div style="display: flex;">
|
||||||
|
{#if provider !== null}
|
||||||
|
<button on:click={() => provider = null} style="flex: 0 0 auto;">
|
||||||
|
<i class="icon">chevron_left</i>
|
||||||
|
Change provider
|
||||||
|
</button>
|
||||||
|
{:else if country !== null}
|
||||||
|
<button on:click={() => country = null} style="flex: 0 0 auto;">
|
||||||
|
<i class="icon">chevron_left</i>
|
||||||
|
Change country
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
<div style="flex: 1 1 auto;"></div>
|
||||||
|
<div style="flex: 0 0 auto; display: flex; gap: 0.25em; align-items: center;">
|
||||||
|
<span>Paying from</span>
|
||||||
|
<span style="font-size: 1.5em; line-height: 1em;">{country.emoji}</span>
|
||||||
|
<span>
|
||||||
|
{country.name}
|
||||||
|
({vat}% VAT)
|
||||||
|
{#if provider !== null}with {provider.label}{/if}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
@@ -1,13 +1,12 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import CreditDeposit from "layout/CreditDeposit.svelte";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import Euro from "util/Euro.svelte";
|
import Euro from "util/Euro.svelte";
|
||||||
import { formatDate } from "util/Formatting";
|
import { formatDate } from "util/Formatting";
|
||||||
import LoadingIndicator from "util/LoadingIndicator.svelte";
|
import LoadingIndicator from "util/LoadingIndicator.svelte";
|
||||||
import MollieDeposit from "./MollieDeposit.svelte";
|
|
||||||
|
|
||||||
let loading = false
|
let loading = false
|
||||||
let credit_amount = 10
|
let credit_amount = 10
|
||||||
let tab = "mollie"
|
|
||||||
|
|
||||||
const checkout = async (network = "", amount = 0, country = "") => {
|
const checkout = async (network = "", amount = 0, country = "") => {
|
||||||
loading = true
|
loading = true
|
||||||
@@ -55,7 +54,7 @@ const load_invoices = async () => {
|
|||||||
invoices_tmp.forEach(row => {
|
invoices_tmp.forEach(row => {
|
||||||
row.time = new Date(row.time)
|
row.time = new Date(row.time)
|
||||||
|
|
||||||
if (row.payment_method === "mollie" && row.status === "open") {
|
if (row.status === "open") {
|
||||||
unpaid_invoice = true
|
unpaid_invoice = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -89,61 +88,18 @@ onMount(() => {
|
|||||||
amount={window.user.balance_micro_eur}/>
|
amount={window.user.balance_micro_eur}/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="tab_bar">
|
{#if unpaid_invoice}
|
||||||
<button on:click={() => tab = "mollie"} class:button_highlight={tab === "mollie"}>
|
<div class="highlight_yellow">
|
||||||
<i class="icon">euro</i>
|
<p>
|
||||||
Mollie
|
You still have an unpaid invoice open. Please pay that one
|
||||||
</button>
|
before requesting a new invoice. You can find unpaid
|
||||||
<button on:click={() => tab = "btcpay"} class:button_highlight={tab === "btcpay"}>
|
invoices at the bottom of this page. You can cancel an
|
||||||
<i class="icon">currency_bitcoin</i>
|
invoice by clicking Pay, and then clicking the Back link at
|
||||||
Crypto
|
the bottom of the page.
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{#if tab === "mollie"}
|
|
||||||
{#if unpaid_invoice}
|
|
||||||
<div class="highlight_yellow">
|
|
||||||
<p>
|
|
||||||
You still have an unpaid invoice open. Please pay that one
|
|
||||||
before requesting a new invoice. You can find unpaid
|
|
||||||
invoices at the bottom of this page. You can cancel an
|
|
||||||
invoice by clicking Pay, and then clicking the Back link at
|
|
||||||
the bottom of the page.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<MollieDeposit/>
|
|
||||||
{/if}
|
|
||||||
{:else if tab === "btcpay"}
|
|
||||||
<div class="highlight_border">
|
|
||||||
<p style="text-align: initial">
|
|
||||||
Alternatively you can use Bitcoin, Lightning network (<a
|
|
||||||
href="https://btcpay.pixeldrain.com/embed/uS2mbWjXUuaAqMh8XLjkjwi8oehFuxeBZxekMxv68LN/BTC/ln"
|
|
||||||
target="_blank" rel="noreferrer">node info</a>) and Dogecoin to deposit
|
|
||||||
credits on your pixeldrain account. You must pay the full amount as
|
|
||||||
stated on the invoice, else your payment will fail.
|
|
||||||
</p>
|
</p>
|
||||||
<p style="text-align: initial">
|
|
||||||
Do note that it is not possible to withdraw coins from your
|
|
||||||
pixeldrain account. It's not a wallet. Any amount of money you
|
|
||||||
deposit has to be used up.
|
|
||||||
</p>
|
|
||||||
Deposit amount €
|
|
||||||
<input type="number" bind:value={credit_amount} min="10"/>
|
|
||||||
<br/>
|
|
||||||
Choose payment method:<br/>
|
|
||||||
<button on:click={() => {checkout("btc", credit_amount)}}>
|
|
||||||
<i class="icon">currency_bitcoin</i> Bitcoin
|
|
||||||
</button>
|
|
||||||
<button on:click={() => {checkout("btc_lightning", credit_amount)}}>
|
|
||||||
<i class="icon">bolt</i> Lightning network
|
|
||||||
</button>
|
|
||||||
<button on:click={() => {checkout("doge", credit_amount)}}>
|
|
||||||
<span class="icon_unicode">Ð</span> Dogecoin
|
|
||||||
</button>
|
|
||||||
<button on:click={() => {checkout("xmr", credit_amount)}}>
|
|
||||||
<span class="icon_unicode">M</span> Monero
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
{:else}
|
||||||
|
<CreditDeposit/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<h3 id="invoices">Past invoices</h3>
|
<h3 id="invoices">Past invoices</h3>
|
||||||
@@ -174,7 +130,7 @@ onMount(() => {
|
|||||||
<td>{row.country}</td>
|
<td>{row.country}</td>
|
||||||
<td>{row.payment_method}</td>
|
<td>{row.payment_method}</td>
|
||||||
<td>
|
<td>
|
||||||
{#if row.status === "InvoiceCreated" || row.status === "open"}
|
{#if row.status === "InvoiceCreated" || row.status === "open" || row.status === "CREATED" || row.status === "PAYER_ACTION_REQUIRED"}
|
||||||
Waiting for payment
|
Waiting for payment
|
||||||
{:else if row.status === "InvoiceProcessing"}
|
{:else if row.status === "InvoiceProcessing"}
|
||||||
Payment received, waiting for confirmations
|
Payment received, waiting for confirmations
|
||||||
@@ -189,7 +145,12 @@ onMount(() => {
|
|||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{#if row.status === "New" || row.status === "InvoiceCreated" || row.status === "open"}
|
{#if row.status === "New" ||
|
||||||
|
row.status === "InvoiceCreated" ||
|
||||||
|
row.status === "open" ||
|
||||||
|
row.status === "CREATED" ||
|
||||||
|
row.status === "PAYER_ACTION_REQUIRED"
|
||||||
|
}
|
||||||
<a href="/api/user/pay_invoice/{row.id}" class="button button_highlight">
|
<a href="/api/user/pay_invoice/{row.id}" class="button button_highlight">
|
||||||
<i class="icon">paid</i> Pay
|
<i class="icon">paid</i> Pay
|
||||||
</a>
|
</a>
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from "svelte";
|
|
||||||
import Euro from "util/Euro.svelte"
|
import Euro from "util/Euro.svelte"
|
||||||
import LoadingIndicator from "util/LoadingIndicator.svelte";
|
import LoadingIndicator from "util/LoadingIndicator.svelte";
|
||||||
import SuccessMessage from "util/SuccessMessage.svelte";
|
import SuccessMessage from "util/SuccessMessage.svelte";
|
||||||
@@ -47,20 +46,12 @@ const update = async (plan) => {
|
|||||||
loading = false
|
loading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let checkout_success = false
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
if (window.location.hash === "#checkout_complete") {
|
|
||||||
checkout_success = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<LoadingIndicator loading={loading}/>
|
<LoadingIndicator loading={loading}/>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
{#if checkout_success}
|
{#if window.location.hash === "#checkout_complete"}
|
||||||
<div class="highlight_green">
|
<div class="highlight_green">
|
||||||
<h2>Payment successful!</h2>
|
<h2>Payment successful!</h2>
|
||||||
<p>
|
<p>
|
||||||
@@ -78,6 +69,24 @@ onMount(() => {
|
|||||||
support@pixeldrain.com.
|
support@pixeldrain.com.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
{:else if window.location.hash === "#order_expired"}
|
||||||
|
<div class="highlight_yellow">
|
||||||
|
<h2>Order expired</h2>
|
||||||
|
<p>
|
||||||
|
This order has expired. Please create a new order on the <a
|
||||||
|
href="/user/prepaid/deposit">credit deposit page</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{:else if window.location.hash === "#order_canceled"}
|
||||||
|
<div class="highlight_yellow">
|
||||||
|
<h2>Order canceled</h2>
|
||||||
|
<p>
|
||||||
|
You have chosen to cancel the order. If you still want to
|
||||||
|
proceed with the order you can initiate payment again from the
|
||||||
|
<a href="/user/prepaid/deposit">deposit page</a>. If not then
|
||||||
|
you can let the invoice expire.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<PatreonActivationResult/>
|
<PatreonActivationResult/>
|
||||||
|
@@ -176,6 +176,7 @@ func New(r *httprouter.Router, prefix string, conf Config) (wc *WebController) {
|
|||||||
{GET, "admin/user_management" /* */, wc.serveTemplate("admin", handlerOpts{Auth: true})},
|
{GET, "admin/user_management" /* */, wc.serveTemplate("admin", handlerOpts{Auth: true})},
|
||||||
{GET, "admin/mollie_settlements" /**/, wc.serveTemplate("admin", handlerOpts{Auth: true})},
|
{GET, "admin/mollie_settlements" /**/, wc.serveTemplate("admin", handlerOpts{Auth: true})},
|
||||||
{GET, "admin/paypal_taxes" /* */, wc.serveTemplate("admin", handlerOpts{Auth: true})},
|
{GET, "admin/paypal_taxes" /* */, wc.serveTemplate("admin", handlerOpts{Auth: true})},
|
||||||
|
{GET, "admin/invoices" /* */, wc.serveTemplate("admin", handlerOpts{Auth: true})},
|
||||||
{GET, "admin/globals" /* */, wc.serveForm(wc.adminGlobalsForm, handlerOpts{Auth: true})},
|
{GET, "admin/globals" /* */, wc.serveForm(wc.adminGlobalsForm, handlerOpts{Auth: true})},
|
||||||
{PST, "admin/globals" /* */, wc.serveForm(wc.adminGlobalsForm, handlerOpts{Auth: true})},
|
{PST, "admin/globals" /* */, wc.serveForm(wc.adminGlobalsForm, handlerOpts{Auth: true})},
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user