- {{ texts[status].icon }}
+ {{ texts[displayStatus].icon }}
- {{ texts[status].text }}
+ {{ texts[displayStatus].text }}
@@ -78,7 +76,7 @@ export default {
showSnackbar: false,
showPartial: false,
showDialog: true,
- status: "pending",
+ redirected: false,
invoice: {},
store: {},
loading: true,
@@ -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",
@@ -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
},
@@ -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,
@@ -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) {
@@ -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"
+ },
},
}
@@ -279,6 +307,16 @@ $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;
@@ -286,7 +324,36 @@ $dialog-max-height: 100%;
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;
}