Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 13 additions & 14 deletions components/TabbedCheckout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
{{
expiringSoon
? "Invoice expiring soon..."
: underpaid
? "Invoice underpaid..."
: "Awaiting payment..."
}}
</p>
Expand Down Expand Up @@ -431,20 +433,13 @@
<v-card flat class="pa-0 ma-0">
<v-card-text>
<div v-if="!zeroAmountInvoice">
<p class="d-flex justify-center">Amount</p>
<p
class="d-flex justify-center"
:class="
getAmountClass(
itemv.amount.length +
itemv.symbol.length +
1
)
"
>
{{ itemv.amount }}
{{ itemv.symbol.toUpperCase() }}
</p>
<div class="d-flex align-center justify-center">
<display-field
:title="`Amount in ${itemv.symbol.toUpperCase()}`"
:value="itemv.amount"
class="flex-grow-1"
/>
</div>
<v-divider />
</div>
<UIExtensionSlot
Expand Down Expand Up @@ -623,6 +618,10 @@ export default {
type: Boolean,
default: false,
},
underpaid: {
type: Boolean,
default: false,
},
invoice: {
type: Object,
default() {
Expand Down
135 changes: 101 additions & 34 deletions pages/i/_id.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,33 +26,31 @@
</div>
<div v-else>
<TabbedCheckout
v-if="status === 'pending'"
v-if="displayStatus === 'pending' || displayStatus === 'underpaid' || displayStatus === 'waiting_confirmation'"
:checkout-page="true"
:invoice.sync="invoice"
:store="store"
:underpaid="displayStatus === 'underpaid'"
class="px-0 pb-0"
@closedialog="closeDialog"
/>
<v-card-text v-else class="no-overflow py-12">
<close-button class="mt-n8" @closedialog="closeDialog" />
<div
:class="colorClass(texts[status].icon)"
:class="[colorClass(texts[displayStatus].icon), displayStatus === 'confirmed' ? 'confirming' : '']"
class="d-flex justify-center success-circle success-icon"
>
<v-icon
:color="
['mdi-check', 'mdi-cash-refund'].includes(texts[status].icon)
? 'green'
: 'red'
"
class="d-flex justify-center"
v-if="displayStatus !== 'confirmed'"
:color="getIconColor(texts[displayStatus].icon)"
class="d-flex justify-center icon-static"
>
{{ texts[status].icon }}
{{ texts[displayStatus].icon }}
</v-icon>
</div>
<div class="d-flex justify-center pt-4">
<div class="text-subtitle-1 font-weight-bold">
{{ texts[status].text }}
{{ texts[displayStatus].text }}
</div>
</div>
</v-card-text>
Expand All @@ -78,7 +76,7 @@ export default {
showSnackbar: false,
showPartial: false,
showDialog: true,
status: "pending",
redirected: false,
invoice: {},
store: {},
loading: true,
Expand All @@ -92,13 +90,21 @@ export default {
icon: "mdi-close",
text: "This invoice has been marked as invalid",
},
paid: {
icon: "mdi-check",
text: "This invoice has been paid",
waiting_confirmation: {
icon: "mdi-clock-outline",
text: "Payment received, awaiting confirmation...",
},
underpaid: {
icon: "mdi-alert-circle-outline",
text: "Invoice underpaid. Please send the remaining amount.",
},
pending: {
icon: "",
text: "Awaiting payment...",
},
confirmed: {
icon: "mdi-check",
text: "This invoice has been paid",
text: "Payment received, awaiting confirmations...",
},
complete: {
icon: "mdi-check",
Expand All @@ -115,6 +121,25 @@ export default {
},
}
},
computed: {
displayStatus() {
if (!this.invoice || !this.invoice.status) return 'pending'
if (this.invoice.status === 'pending' && this.invoice.exception_status === 'paid_partial') return 'underpaid'
if (this.invoice.status === 'paid') return 'confirmed'
return this.invoice.status
},
isFinalStatus() {
return ['complete', 'expired', 'invalid', 'refunded'].includes(this.displayStatus)
}
},
watch: {
displayStatus(newStatus) {
if (!this.redirected && this.isFinalStatus && this.invoice.redirect_url) {
this.redirected = true
this.$utils.redirectTo(this.invoice.redirect_url)
}
}
},
beforeCreate() {
this.$vuetify.theme.dark = false // dark theme unsupported here
},
Expand Down Expand Up @@ -164,24 +189,30 @@ export default {
this.websocket = new WebSocket(url)
this.websocket.onmessage = (event) => {
const data = JSON.parse(event.data)
const status = data.status
if (
status === "pending" &&
this.invoice.sent_amount !== data.sent_amount
) {
// received partial payment
this.invoice.exception_status = data.exception_status
this.invoice.sent_amount = data.sent_amount
this.invoice.paid_currency = data.paid_currency
this.invoice.payment_id = data.payment_id
const oldStatus = this.invoice.status
const oldSentAmount = this.invoice.sent_amount

// Update invoice data
this.invoice.status = data.status
this.invoice.exception_status = data.exception_status
this.invoice.sent_amount = data.sent_amount
this.invoice.paid_currency = data.paid_currency
this.invoice.payment_id = data.payment_id

// Show partial payment notification if needed
if (data.status === 'pending' &&
data.exception_status === 'paid_partial' &&
oldSentAmount !== data.sent_amount) {
this.$bus.$emit("showDetails")
this.showPartial = true
}
if (this.invoice.status !== status) {

// Notify parent window of status change
if (oldStatus !== data.status) {
window.parent.postMessage(
{
invoice_id: this.invoice.id,
status,
status: data.status,
exception_status: data.exception_status,
sent_amount: data.sent_amount,
paid_currency: data.paid_currency,
Expand All @@ -190,13 +221,6 @@ export default {
"*"
)
}
if (
["paid", "confirmed", "complete"].includes(status) &&
this.invoice.redirect_url
) {
this.$utils.redirectTo(this.invoice.redirect_url)
}
this.status = status
}
},
colorClass(icon) {
Expand All @@ -213,6 +237,10 @@ export default {
? baseURL.replace(/\/+$/, "") + "/" + relativeURL.replace(/^\/+/, "")
: baseURL
},
getIconColor(icon) {
if (["mdi-check", "mdi-cash-refund"].includes(icon)) return "green"
return "red"
},
},
}
</script>
Expand Down Expand Up @@ -279,14 +307,53 @@ $dialog-max-height: 100%;
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
}
}

@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

.success-circle {
border-radius: 50%;
height: 154px;
width: 154px;
margin: 0 auto;
border-width: 2px;
border-style: solid;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}

.icon-static {
position: relative;
z-index: 1;
}

.success-circle.confirming {
border-style: dashed;
animation: rotate 2s linear infinite;
}

.success-circle.confirming::before {
content: '';
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
border-radius: 50%;
border: 2px solid transparent;
border-top-color: currentColor;
border-right-color: currentColor;
animation: rotate 2s linear infinite;
}

.green-color {
border-color: #13e5b6;
}
Expand Down