211 lines
4.1 KiB
Vue
211 lines
4.1 KiB
Vue
<template>
|
|
<div class="invoice-form">
|
|
<div class="invoice-form__header">
|
|
<div>
|
|
<h2>Payment details</h2>
|
|
|
|
<p class="invoice-form__order-id">
|
|
# {{ invoice.orderId }}
|
|
</p>
|
|
</div>
|
|
|
|
<InvoiceFormTimer
|
|
:expiration-time="expirationTime"
|
|
/>
|
|
</div>
|
|
|
|
<div class="invoice-form__body">
|
|
<Transition
|
|
name="shift"
|
|
mode="out-in"
|
|
>
|
|
<Component :is="stepComponent" />
|
|
</Transition>
|
|
</div>
|
|
|
|
<p class="invoice-form__network-alert">
|
|
Адрес работает только для получения Tether (USDT) в сети TRC20
|
|
</p>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useSessionStorage } from '@vueuse/core'
|
|
import {
|
|
InvoiceFormStepExpired,
|
|
InvoiceFormStepPending,
|
|
InvoiceFormStepRequisites,
|
|
InvoiceFormStepSuccess,
|
|
InvoiceFormStepUnderpaid,
|
|
InvoiceFormStepWaitingRequisites,
|
|
} from '#components'
|
|
|
|
definePageMeta({
|
|
validate: async (route) => {
|
|
try {
|
|
await $api(`/payments/${route.params.invoiceId}`, {
|
|
method: 'get',
|
|
})
|
|
|
|
return true
|
|
}
|
|
catch {
|
|
return false
|
|
}
|
|
},
|
|
})
|
|
|
|
const route = useRoute()
|
|
const payedFlag = useSessionStorage(`invoice-${route.params.invoiceId}`, false, { initOnMounted: true })
|
|
|
|
interface InvoiceTransfer {
|
|
txID: string
|
|
recipient: string
|
|
sender: string
|
|
blockchainCode: string
|
|
}
|
|
|
|
interface PublicInvoice {
|
|
id: string
|
|
orderId: string
|
|
amount: number | string
|
|
collectedAmount: number | string
|
|
currencyCode: string
|
|
expiresAt: string
|
|
blockchainCode?: string
|
|
availableBlockchains: string[]
|
|
walletAddress?: string
|
|
transfers?: InvoiceTransfer[]
|
|
}
|
|
|
|
const { data: invoice, refresh } = await useAsyncData<PublicInvoice>('public-invoice', async () => {
|
|
const nuxtApp = useNuxtApp()
|
|
|
|
const invoice = await $api<PublicInvoice>(`/payments/${route.params.invoiceId}`, {
|
|
method: 'get',
|
|
})
|
|
|
|
if (new Date(invoice.expiresAt) < new Date() || invoice.walletAddress)
|
|
return invoice
|
|
|
|
try {
|
|
const selectedBlockchain = await nuxtApp.runWithContext(async () => {
|
|
return $api<Pick<PublicInvoice, 'expiresAt' | 'walletAddress'>>(`/payments/${route.params.invoiceId}`, {
|
|
method: 'put',
|
|
body: {
|
|
blockchainCode: invoice!.availableBlockchains[0],
|
|
},
|
|
})
|
|
})
|
|
|
|
return {
|
|
...invoice,
|
|
...selectedBlockchain,
|
|
}
|
|
}
|
|
catch (e) {
|
|
console.error(e)
|
|
|
|
return invoice
|
|
}
|
|
})
|
|
|
|
const expirationTime = computed(() => {
|
|
return new Date(invoice.value?.expiresAt ?? 0).getTime()
|
|
})
|
|
|
|
const { isPassed } = useTimer(expirationTime)
|
|
|
|
const stepComponent = computed(() => {
|
|
const status = invoice.value?.status
|
|
|
|
switch (status) {
|
|
case 'pending':
|
|
return InvoiceFormStepWaitingRequisites
|
|
|
|
case 'awaiting_payment':
|
|
if (!isPassed.value)
|
|
return InvoiceFormStepRequisites
|
|
else
|
|
return InvoiceFormStepPending
|
|
|
|
case 'completed':
|
|
return InvoiceFormStepSuccess
|
|
|
|
case 'underpaid':
|
|
return InvoiceFormStepUnderpaid
|
|
|
|
case 'overpaid':
|
|
return InvoiceFormStepSuccess
|
|
// return InvoiceFormStepOverpaid
|
|
|
|
default:
|
|
return InvoiceFormStepExpired
|
|
}
|
|
})
|
|
|
|
const intervalId = setInterval(refresh, 5000)
|
|
|
|
watch(payedFlag, () => {
|
|
refresh()
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
clearInterval(intervalId)
|
|
})
|
|
|
|
provide('public-invoice', { invoice, expirationTime, payedFlag })
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.invoice-form {
|
|
width: 532px;
|
|
margin: 0 auto;
|
|
|
|
&__header {
|
|
display: flex;
|
|
align-items: flex-end;
|
|
justify-content: space-between;
|
|
padding: 0 16px;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
&__order-id {
|
|
@include txt-r-sb;
|
|
|
|
color: $clr-grey-500;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
&__body {
|
|
background: $clr-white;
|
|
border-radius: 12px;
|
|
padding: 16px;
|
|
text-align: center;
|
|
}
|
|
|
|
&__network-alert {
|
|
@include txt-i-m;
|
|
|
|
text-align: center;
|
|
color: $clr-grey-400;
|
|
padding: 8px 24px;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
[data-status-illustration] {
|
|
animation: rotate .8s cubic-bezier(.25,.75,.5,1.25);
|
|
}
|
|
|
|
@keyframes rotate {
|
|
from {
|
|
transform: rotateZ(0deg);
|
|
}
|
|
|
|
to {
|
|
transform: rotateZ(360deg);
|
|
}
|
|
}
|
|
}
|
|
</style>
|