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 PayPalTaxes from "./PayPalTaxes.svelte";
|
||||
import UserBans from "./user_bans/UserBans.svelte";
|
||||
import InvoiceVat from "./InvoiceVAT.svelte";
|
||||
|
||||
let pages = [
|
||||
{
|
||||
@@ -62,6 +63,11 @@ let pages = [
|
||||
title: "Paypal Taxes",
|
||||
icon: "paypal",
|
||||
component: PayPalTaxes,
|
||||
}, {
|
||||
path: "/admin/invoices",
|
||||
title: "Invoices",
|
||||
icon: "receipt",
|
||||
component: InvoiceVat,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import CreditDeposit from "layout/CreditDeposit.svelte";
|
||||
import LoginRegister from "login/LoginRegister.svelte";
|
||||
import MollieDeposit from "user_home/MollieDeposit.svelte";
|
||||
import Euro from "util/Euro.svelte";
|
||||
</script>
|
||||
|
||||
@@ -19,13 +19,13 @@ import Euro from "util/Euro.svelte";
|
||||
balance is <Euro amount={window.user.balance_micro_eur}/>. Use
|
||||
the form below to top up your balance.
|
||||
</p>
|
||||
<MollieDeposit/>
|
||||
<CreditDeposit/>
|
||||
{:else}
|
||||
<p>
|
||||
You are currently logged in as '{window.user.username}'. Use the
|
||||
form below to activate prepaid on this account.
|
||||
</p>
|
||||
<MollieDeposit/>
|
||||
<CreditDeposit/>
|
||||
{/if}
|
||||
</section>
|
||||
</div>
|
||||
|
@@ -4,11 +4,13 @@ import Euro from "util/Euro.svelte";
|
||||
import LoadingIndicator from "util/LoadingIndicator.svelte";
|
||||
import { countries } from "country-data-list";
|
||||
import { get_endpoint, get_misc_vat_rate } from "lib/PixeldrainAPI";
|
||||
import CreditDepositNav from "./CreditDepositNav.svelte";
|
||||
|
||||
let loading = false
|
||||
let amount = 20
|
||||
let country: typeof countries.all[0] = null
|
||||
let country_input = ""
|
||||
let provider: PaymentProvider = null
|
||||
let vat = 0
|
||||
|
||||
const amounts = [10, 20, 50, 100, 200, 500, 1000, 2000, 5000]
|
||||
@@ -19,8 +21,30 @@ onMount(() => {
|
||||
country_input = checkout_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 = [
|
||||
{icon: "paypal", name: "PayPal"},
|
||||
{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 () => {
|
||||
loading = true
|
||||
|
||||
@@ -79,7 +110,7 @@ const checkout = async () => {
|
||||
|
||||
const form = new FormData()
|
||||
form.set("amount", String(amount*1e6))
|
||||
form.set("network", "mollie")
|
||||
form.set("network", provider.name)
|
||||
form.set("country", country.alpha2)
|
||||
|
||||
try {
|
||||
@@ -161,21 +192,32 @@ const format_country = (c: typeof countries.all[0]) => {
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{:else}
|
||||
{:else if provider === null}
|
||||
<CreditDepositNav bind:country={country} bind:provider={provider} bind:vat={vat}/>
|
||||
|
||||
<div style="display: flex;">
|
||||
<button on:click={() => country = null} style="flex: 0 0 auto;">
|
||||
<i class="icon">chevron_left</i>
|
||||
Change country
|
||||
</button>
|
||||
<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)</span>
|
||||
</div>
|
||||
<h2>Please select a payment provider</h2>
|
||||
|
||||
<div class="providers">
|
||||
{#each providers as p (p.name)}
|
||||
<button on:click={() => set_provider(p)}>
|
||||
<img src="/res/img/payment_providers/{p.icon}.svg" alt={p.label} title={p.label}/>
|
||||
{p.label}
|
||||
</button>
|
||||
{/each}
|
||||
</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}>
|
||||
<div class="span3">Please choose an amount</div>
|
||||
{#each amounts as a}
|
||||
@@ -237,6 +279,19 @@ const format_country = (c: typeof countries.all[0]) => {
|
||||
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 {
|
||||
max-width: 500px;
|
||||
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>
|
||||
import CreditDeposit from "layout/CreditDeposit.svelte";
|
||||
import { onMount } from "svelte";
|
||||
import Euro from "util/Euro.svelte";
|
||||
import { formatDate } from "util/Formatting";
|
||||
import LoadingIndicator from "util/LoadingIndicator.svelte";
|
||||
import MollieDeposit from "./MollieDeposit.svelte";
|
||||
|
||||
let loading = false
|
||||
let credit_amount = 10
|
||||
let tab = "mollie"
|
||||
|
||||
const checkout = async (network = "", amount = 0, country = "") => {
|
||||
loading = true
|
||||
@@ -55,7 +54,7 @@ const load_invoices = async () => {
|
||||
invoices_tmp.forEach(row => {
|
||||
row.time = new Date(row.time)
|
||||
|
||||
if (row.payment_method === "mollie" && row.status === "open") {
|
||||
if (row.status === "open") {
|
||||
unpaid_invoice = true
|
||||
}
|
||||
})
|
||||
@@ -89,61 +88,18 @@ onMount(() => {
|
||||
amount={window.user.balance_micro_eur}/>
|
||||
</p>
|
||||
|
||||
<div class="tab_bar">
|
||||
<button on:click={() => tab = "mollie"} class:button_highlight={tab === "mollie"}>
|
||||
<i class="icon">euro</i>
|
||||
Mollie
|
||||
</button>
|
||||
<button on:click={() => tab = "btcpay"} class:button_highlight={tab === "btcpay"}>
|
||||
<i class="icon">currency_bitcoin</i>
|
||||
Crypto
|
||||
</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.
|
||||
{#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>
|
||||
<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>
|
||||
{:else}
|
||||
<CreditDeposit/>
|
||||
{/if}
|
||||
|
||||
<h3 id="invoices">Past invoices</h3>
|
||||
@@ -174,7 +130,7 @@ onMount(() => {
|
||||
<td>{row.country}</td>
|
||||
<td>{row.payment_method}</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
|
||||
{:else if row.status === "InvoiceProcessing"}
|
||||
Payment received, waiting for confirmations
|
||||
@@ -189,7 +145,12 @@ onMount(() => {
|
||||
{/if}
|
||||
</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">
|
||||
<i class="icon">paid</i> Pay
|
||||
</a>
|
||||
|
@@ -1,5 +1,4 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import Euro from "util/Euro.svelte"
|
||||
import LoadingIndicator from "util/LoadingIndicator.svelte";
|
||||
import SuccessMessage from "util/SuccessMessage.svelte";
|
||||
@@ -47,20 +46,12 @@ const update = async (plan) => {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
let checkout_success = false
|
||||
|
||||
onMount(() => {
|
||||
if (window.location.hash === "#checkout_complete") {
|
||||
checkout_success = true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<LoadingIndicator loading={loading}/>
|
||||
|
||||
<section>
|
||||
{#if checkout_success}
|
||||
{#if window.location.hash === "#checkout_complete"}
|
||||
<div class="highlight_green">
|
||||
<h2>Payment successful!</h2>
|
||||
<p>
|
||||
@@ -78,6 +69,24 @@ onMount(() => {
|
||||
support@pixeldrain.com.
|
||||
</p>
|
||||
</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}
|
||||
|
||||
<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/mollie_settlements" /**/, 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})},
|
||||
{PST, "admin/globals" /* */, wc.serveForm(wc.adminGlobalsForm, handlerOpts{Auth: true})},
|
||||
|
||||
|
Reference in New Issue
Block a user