Update prepaid checkout to allow all countries
This commit is contained in:
13
svelte/package-lock.json
generated
13
svelte/package-lock.json
generated
@@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"behave-js": "^1.5.0",
|
||||
"chart.js": "^4.4.6",
|
||||
"country-data-list": "^1.4.0",
|
||||
"pure-color": "^1.3.0",
|
||||
"rollup-plugin-includepaths": "^0.2.4",
|
||||
"svelte-preprocess": "^6.0.3",
|
||||
@@ -2302,9 +2303,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001677",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz",
|
||||
"integrity": "sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==",
|
||||
"version": "1.0.30001712",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001712.tgz",
|
||||
"integrity": "sha512-MBqPpGYYdQ7/hfKiet9SCI+nmN5/hp4ZzveOJubl5DTAMa5oggjAuoi0Z4onBpKPFI2ePGnQuQIzF3VxDjDJig==",
|
||||
"devOptional": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -2436,6 +2437,12 @@
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/country-data-list": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/country-data-list/-/country-data-list-1.4.0.tgz",
|
||||
"integrity": "sha512-3i1Q1onE9MaqYPdtTUzZRZJY7lBXWpud3eWXrw3ckJyvOzXcsFALiy0Z4Giksy5ECpq9ajYijMPxfRZvVP65UA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
|
@@ -23,6 +23,7 @@
|
||||
"dependencies": {
|
||||
"behave-js": "^1.5.0",
|
||||
"chart.js": "^4.4.6",
|
||||
"country-data-list": "^1.4.0",
|
||||
"pure-color": "^1.3.0",
|
||||
"rollup-plugin-includepaths": "^0.2.4",
|
||||
"svelte-preprocess": "^6.0.3",
|
||||
|
@@ -159,7 +159,8 @@ const report_description = () => {
|
||||
<p>
|
||||
If you think this file violates pixeldrain's
|
||||
<a href="/abuse">content policy</a> you can report it for moderation
|
||||
with this form. Please submit copyright infringement notices through our
|
||||
with this form. For copyright infringement notices or urgent matters
|
||||
please use our
|
||||
<a href="/abuse#toc_2">abuse e-mail address</a>.
|
||||
</p>
|
||||
<form on:submit={submit} style="width: 100%" class="report_form">
|
||||
|
@@ -32,7 +32,7 @@ $: transfer_left = $stats.limits.transfer_limit - $stats.limits.transfer_limit_u
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
<a href="/#pro" target="_blank" class="button button_highlight" rel="noreferrer">
|
||||
<a href="/user/prepaid/deposit" target="_blank" class="button button_highlight" rel="noreferrer">
|
||||
<i class="icon">bolt</i> Upgrade your account
|
||||
</a>
|
||||
to disable the transfer limit
|
||||
|
@@ -71,7 +71,7 @@ let file = {
|
||||
<button on:click={() => {dispatch("download")}}>
|
||||
<i class="icon">download</i> Download
|
||||
</button>
|
||||
<a href="/#pro" target="_blank" class="button button_highlight">
|
||||
<a href="/user/prepaid/deposit" target="_blank" class="button button_highlight">
|
||||
<i class="icon">bolt</i> Upgrade your account
|
||||
</a>
|
||||
</IconBlock>
|
||||
|
@@ -37,8 +37,8 @@ export let icon_href = ""
|
||||
least {formatDuration((file_size/($stats.limits.speed_limit))*1000, 0)}
|
||||
</li>
|
||||
<li>
|
||||
<a href="/#pro" target="_blank" class="button button_highlight">
|
||||
<i class="icon">bolt</i> Upgrade your account
|
||||
<a href="/user/prepaid/deposit" target="_blank" class="button button_highlight">
|
||||
<i class="icon">bolt</i> Get Premium
|
||||
</a>
|
||||
and earn my eternal gratitude
|
||||
{#if !window.user_authenticated}
|
||||
|
@@ -114,3 +114,14 @@ export const put_user = async (data: Object) => {
|
||||
((window as any).user as User)[key] = data[key]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export type VATRate = {
|
||||
name: string,
|
||||
vat: number,
|
||||
alpha2: string,
|
||||
alpha3: string,
|
||||
}
|
||||
export const get_misc_vat_rate = async (country_code: string) => {
|
||||
return await check_response(await fetch(get_endpoint() + "/misc/vat_rate/" + country_code)) as VATRate
|
||||
}
|
||||
|
@@ -1,62 +1,74 @@
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
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";
|
||||
|
||||
let loading = false
|
||||
let amount = 20
|
||||
let country = null
|
||||
let country: typeof countries.all[0] = null
|
||||
let country_input = ""
|
||||
let vat = 0
|
||||
|
||||
$: credit_micro = amount*1e6
|
||||
$: vat_micro = country === null ? 0 : (amount*1e6)*(country.vat/100)
|
||||
|
||||
let countries = [
|
||||
{name: "Austria", flag: "🇦🇹", vat: 20},
|
||||
{name: "Belgium", flag: "🇧🇪", vat: 21},
|
||||
{name: "Bulgaria", flag: "🇧🇬", vat: 20},
|
||||
{name: "Croatia", flag: "🇭🇷", vat: 25},
|
||||
{name: "Cyprus", flag: "🇨🇾", vat: 19},
|
||||
{name: "Czechia", flag: "🇨🇿", vat: 21},
|
||||
{name: "Denmark", flag: "🇩🇰", vat: 25},
|
||||
{name: "Estonia", flag: "🇪🇪", vat: 20},
|
||||
{name: "Finland", flag: "🇫🇮", vat: 24},
|
||||
{name: "France", flag: "🇫🇷", vat: 20},
|
||||
{name: "Germany", flag: "🇩🇪", vat: 19},
|
||||
{name: "Greece", flag: "🇬🇷", vat: 24},
|
||||
{name: "Hungary", flag: "🇭🇺", vat: 27},
|
||||
{name: "Ireland", flag: "🇮🇪", vat: 23},
|
||||
{name: "Italy", flag: "🇮🇹", vat: 22},
|
||||
{name: "Latvia", flag: "🇱🇻", vat: 21},
|
||||
{name: "Lithuania", flag: "🇱🇹", vat: 21},
|
||||
{name: "Luxembourg", flag: "🇱🇺", vat: 16},
|
||||
{name: "Malta", flag: "🇲🇹", vat: 18},
|
||||
{name: "Netherlands", flag: "🇳🇱", vat: 21},
|
||||
{name: "Poland", flag: "🇵🇱", vat: 23},
|
||||
{name: "Portugal", flag: "🇵🇹", vat: 23},
|
||||
{name: "Romania", flag: "🇷🇴", vat: 19},
|
||||
{name: "Slovakia", flag: "🇸🇰", vat: 20},
|
||||
{name: "Slovenia", flag: "🇸🇮", vat: 22},
|
||||
{name: "Spain", flag: "🇪🇸", vat: 21},
|
||||
{name: "Sweden", flag: "🇸🇪", vat: 25},
|
||||
{name: "Other", flag: "🌐", vat: 0},
|
||||
]
|
||||
|
||||
let amounts = [10, 20, 50, 100, 200, 500, 1000, 2000, 5000]
|
||||
|
||||
const set_country = (c) => {
|
||||
country = c
|
||||
window.localStorage.setItem("checkout_country", c.name)
|
||||
}
|
||||
const amounts = [10, 20, 50, 100, 200, 500, 1000, 2000, 5000]
|
||||
|
||||
onMount(() => {
|
||||
const country_name = window.localStorage.getItem("checkout_country")
|
||||
for (let i = 0; i < countries.length; i++) {
|
||||
if (countries[i].name === country_name) {
|
||||
country = countries[i]
|
||||
}
|
||||
const checkout_country = window.localStorage.getItem("checkout_country")
|
||||
if (countries[checkout_country] !== undefined) {
|
||||
country_input = checkout_country
|
||||
set_country()
|
||||
}
|
||||
})
|
||||
|
||||
const payment_providers = [
|
||||
{icon: "paypal", name: "PayPal"},
|
||||
{icon: "creditcard", name: "Credit/debit"},
|
||||
{icon: "apple_pay", name: "Apple Pay"},
|
||||
{icon: "google_pay", name: "Google Pay"},
|
||||
{icon: "bancomat", name: "Bancomat"},
|
||||
{icon: "bancontact", name: "Bancontact"},
|
||||
{icon: "belfius", name: "Belfius"},
|
||||
{icon: "blik", name: "Blik"},
|
||||
{icon: "eps", name: "EPS"},
|
||||
{icon: "ideal", name: "iDEAL"},
|
||||
{icon: "ideal_in3", name: "iDeal in3"},
|
||||
{icon: "kbc", name: "KBC"},
|
||||
{icon: "mb_way", name: "MB Way"},
|
||||
{icon: "multibanco", name: "Multibanco"},
|
||||
{icon: "p24", name: "Przelewy24"},
|
||||
{icon: "riverty", name: "Riverty"},
|
||||
{icon: "satispay", name: "Satispay"},
|
||||
{icon: "sepa", name: "SEPA Transfer"},
|
||||
{icon: "twint", name: "Twint"},
|
||||
]
|
||||
|
||||
const set_country = async (e?: Event) => {
|
||||
loading = true
|
||||
if (e !== undefined) {
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
if (countries[country_input] === undefined) {
|
||||
alert("Please enter a valid country code")
|
||||
return
|
||||
}
|
||||
const c = countries[country_input]
|
||||
|
||||
// Cache the value for next checkout
|
||||
window.localStorage.setItem("checkout_country", c.alpha3)
|
||||
|
||||
try {
|
||||
const vat_rate = await get_misc_vat_rate(c.alpha3)
|
||||
vat = vat_rate.vat*100
|
||||
country = c
|
||||
} catch (err) {
|
||||
alert(err)
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
const checkout = async () => {
|
||||
loading = true
|
||||
|
||||
@@ -66,13 +78,13 @@ const checkout = async () => {
|
||||
}
|
||||
|
||||
const form = new FormData()
|
||||
form.set("amount", amount*1e6)
|
||||
form.set("amount", String(amount*1e6))
|
||||
form.set("network", "mollie")
|
||||
form.set("country", country.name)
|
||||
form.set("country", country.alpha2)
|
||||
|
||||
try {
|
||||
const resp = await fetch(
|
||||
window.api_endpoint+"/user/invoice",
|
||||
get_endpoint()+"/user/invoice",
|
||||
{method: "POST", body: form},
|
||||
)
|
||||
if(resp.status >= 400) {
|
||||
@@ -87,6 +99,19 @@ const checkout = async () => {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
const format_country = (c: typeof countries.all[0]) => {
|
||||
let str = ""
|
||||
if (c.emoji !== undefined) {
|
||||
str += c.emoji + " "
|
||||
} else {
|
||||
str += "🌐 "
|
||||
}
|
||||
str += c.name+" "
|
||||
str += "("+c.alpha2+", "
|
||||
str += c.alpha3+")"
|
||||
return str
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="highlight_border">
|
||||
@@ -97,62 +122,43 @@ const checkout = async () => {
|
||||
<div>
|
||||
Please pick your country of residence
|
||||
</div>
|
||||
<div class="countries">
|
||||
{#each countries as c}
|
||||
<button on:click={() => set_country(c)}>
|
||||
<span class="icon_unicode">{c.flag}</span>
|
||||
<span>{c.name}</span>
|
||||
</button>
|
||||
{/each}
|
||||
<div>
|
||||
<form on:submit|preventDefault={set_country} class="country_form">
|
||||
<div class="country_search">
|
||||
<datalist id="country_picker">
|
||||
{#each countries.all.filter(c => c.status === "assigned") as c}
|
||||
<option value={c.alpha2}>{format_country(c)}</option>
|
||||
{/each}
|
||||
</datalist>
|
||||
<input
|
||||
bind:value={country_input}
|
||||
type="text"
|
||||
list="country_picker"
|
||||
placeholder="Search for country"
|
||||
style="flex: 1 1 auto;">
|
||||
<button type="submit" class="button_highlight" style="flex: 0 0 auto;">
|
||||
<i class="icon">send</i>
|
||||
Continue
|
||||
</button>
|
||||
</div>
|
||||
<select bind:value={country_input} on:dblclick={set_country} style="padding: 0;" size="10">
|
||||
{#each countries.all.filter(c => c.status === "assigned") as c}
|
||||
<option value={c.alpha2}>{format_country(c)}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
<hr/>
|
||||
<div>
|
||||
We support the following payment processors
|
||||
</div>
|
||||
<div class="processors">
|
||||
<div>
|
||||
<img class="bankicon" src="/res/img/payment_providers/paypal.svg" alt="PayPal" title="PayPal"/>
|
||||
PayPal
|
||||
</div>
|
||||
<div>
|
||||
<img class="bankicon" src="/res/img/payment_providers/ideal.svg" alt="iDEAL" title="iDEAL"/>
|
||||
iDEAL
|
||||
</div>
|
||||
<div>
|
||||
<img class="bankicon" src="/res/img/payment_providers/klarna.svg" alt="Klarna" title="Klarna"/>
|
||||
Klarna
|
||||
</div>
|
||||
<div>
|
||||
<img class="bankicon" src="/res/img/payment_providers/bancontact.svg" alt="Bancontact" title="Bancontact"/>
|
||||
Bancontact
|
||||
</div>
|
||||
<div>
|
||||
<img class="bankicon" src="/res/img/payment_providers/banktransfer.svg" alt="SEPA" title="SEPA"/>
|
||||
SEPA
|
||||
</div>
|
||||
<div>
|
||||
<img class="bankicon" src="/res/img/payment_providers/sofort.svg" alt="SOFORT" title="SOFORT"/>
|
||||
SOFORT
|
||||
</div>
|
||||
<div>
|
||||
<img class="bankicon" src="/res/img/payment_providers/kbc.svg" alt="KBC/CBC" title="CBC"/>
|
||||
KBC/CBC
|
||||
</div>
|
||||
<div>
|
||||
<img class="bankicon" src="/res/img/payment_providers/belfius.svg" alt="Belfius" title="Belfius"/>
|
||||
Belfius
|
||||
</div>
|
||||
<div>
|
||||
<img class="bankicon" src="/res/img/payment_providers/giropay.svg" alt="Giropay" title="Giropay"/>
|
||||
Giropay
|
||||
</div>
|
||||
<div>
|
||||
<img class="bankicon" src="/res/img/payment_providers/eps.svg" alt="EPS" title="EPS"/>
|
||||
EPS
|
||||
</div>
|
||||
<div>
|
||||
<img class="bankicon" src="/res/img/payment_providers/przelewy24.svg" alt="Przelewy24" title="Przelewy24"/>
|
||||
Przelewy24
|
||||
</div>
|
||||
{#each payment_providers as p (p.name)}
|
||||
<div>
|
||||
<img class="bankicon" src="/res/img/payment_providers/{p.icon}.svg" alt={p.name} title={p.name}/>
|
||||
{p.name}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{:else}
|
||||
@@ -165,8 +171,8 @@ const checkout = async () => {
|
||||
<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.flag}</span>
|
||||
<span>{country.name} ({country.vat}% VAT)</span>
|
||||
<span style="font-size: 1.5em; line-height: 1em;">{country.emoji}</span>
|
||||
<span>{country.name} ({vat}% VAT)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -189,25 +195,40 @@ const checkout = async () => {
|
||||
|
||||
<div class="span2" style="text-align: initial;">
|
||||
Total including VAT:
|
||||
<Euro amount={credit_micro + vat_micro}/>
|
||||
<Euro amount={(amount*1e6) + (amount*1e6)*(vat/100)}/>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="button_highlight">
|
||||
<i class="icon">paid</i> Checkout
|
||||
</button>
|
||||
</form>
|
||||
<hr/>
|
||||
<p style="text-align: initial;">
|
||||
This Pixeldrain premium plan costs €1 per month, but due to
|
||||
processing fees we can't accept payments less than €10. So your
|
||||
deposit will give roughly 10 months of premium service depending on
|
||||
usage. You can track your spending on the <a
|
||||
href="/user/prepaid/transactions">transactions page</a>.
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.countries {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||
.country_form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: auto;
|
||||
width: 600px;
|
||||
max-width: 100%;
|
||||
}
|
||||
.country_search {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.processors {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
}
|
||||
.processors > div {
|
||||
display: flex;
|
||||
|
Reference in New Issue
Block a user