mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-03-12 10:30:47 +01:00
Checkout v2: Payment processing state (#4778)
This commit is contained in:
parent
de9ac9fd43
commit
45141d1391
9 changed files with 187 additions and 72 deletions
|
@ -140,7 +140,7 @@ namespace BTCPayServer.Tests
|
||||||
Assert.DoesNotContain("Please send", paymentInfo.Text);
|
Assert.DoesNotContain("Please send", paymentInfo.Text);
|
||||||
TestUtils.Eventually(() =>
|
TestUtils.Eventually(() =>
|
||||||
{
|
{
|
||||||
var expiredSection = s.Driver.FindElement(By.Id("expired"));
|
var expiredSection = s.Driver.FindElement(By.Id("unpaid"));
|
||||||
Assert.True(expiredSection.Displayed);
|
Assert.True(expiredSection.Displayed);
|
||||||
Assert.Contains("Invoice Expired", expiredSection.Text);
|
Assert.Contains("Invoice Expired", expiredSection.Text);
|
||||||
});
|
});
|
||||||
|
@ -181,12 +181,27 @@ namespace BTCPayServer.Tests
|
||||||
{
|
{
|
||||||
Assert.Contains("Created transaction",
|
Assert.Contains("Created transaction",
|
||||||
s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).Text);
|
s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).Text);
|
||||||
s.Server.ExplorerNode.Generate(1);
|
s.Server.ExplorerNode.Generate(2);
|
||||||
paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo"));
|
paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo"));
|
||||||
Assert.Contains("The invoice hasn't been paid in full", paymentInfo.Text);
|
Assert.Contains("The invoice hasn't been paid in full", paymentInfo.Text);
|
||||||
Assert.Contains("Please send", paymentInfo.Text);
|
Assert.Contains("Please send", paymentInfo.Text);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Pay full amount
|
||||||
|
var amountDue = s.Driver.FindElement(By.Id("AmountDue")).GetAttribute("data-amount-due");
|
||||||
|
s.Driver.FindElement(By.Id("FakePayAmount")).FillIn(amountDue);
|
||||||
|
s.Driver.FindElement(By.Id("FakePay")).Click();
|
||||||
|
|
||||||
|
// Processing
|
||||||
|
TestUtils.Eventually(() =>
|
||||||
|
{
|
||||||
|
var processingSection = s.Driver.WaitForElement(By.Id("processing"));
|
||||||
|
Assert.True(processingSection.Displayed);
|
||||||
|
Assert.Contains("Payment Sent", processingSection.Text);
|
||||||
|
Assert.Contains("Your payment has been received and is now processing", processingSection.Text);
|
||||||
|
Assert.True(s.Driver.ElementDoesNotExist(By.Id("confetti")));
|
||||||
|
});
|
||||||
|
|
||||||
// Mine
|
// Mine
|
||||||
s.Driver.FindElement(By.Id("Mine")).Click();
|
s.Driver.FindElement(By.Id("Mine")).Click();
|
||||||
TestUtils.Eventually(() =>
|
TestUtils.Eventually(() =>
|
||||||
|
@ -195,16 +210,12 @@ namespace BTCPayServer.Tests
|
||||||
s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).Text);
|
s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).Text);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pay full amount
|
// Settled
|
||||||
var amountDue = s.Driver.FindElement(By.Id("AmountDue")).GetAttribute("data-amount-due");
|
|
||||||
s.Driver.FindElement(By.Id("FakePayAmount")).FillIn(amountDue);
|
|
||||||
s.Driver.FindElement(By.Id("FakePay")).Click();
|
|
||||||
TestUtils.Eventually(() =>
|
TestUtils.Eventually(() =>
|
||||||
{
|
{
|
||||||
s.Server.ExplorerNode.Generate(1);
|
var settledSection = s.Driver.WaitForElement(By.Id("settled"));
|
||||||
var paidSection = s.Driver.WaitForElement(By.Id("paid"));
|
Assert.True(settledSection.Displayed);
|
||||||
Assert.True(paidSection.Displayed);
|
Assert.Contains("Invoice Paid", settledSection.Text);
|
||||||
Assert.Contains("Invoice Paid", paidSection.Text);
|
|
||||||
});
|
});
|
||||||
s.Driver.FindElement(By.Id("confetti"));
|
s.Driver.FindElement(By.Id("confetti"));
|
||||||
s.Driver.FindElement(By.Id("ReceiptLink"));
|
s.Driver.FindElement(By.Id("ReceiptLink"));
|
||||||
|
|
|
@ -80,15 +80,13 @@ namespace BTCPayServer.Controllers
|
||||||
}
|
}
|
||||||
return UnprocessableEntity(new
|
return UnprocessableEntity(new
|
||||||
{
|
{
|
||||||
ErrorMessage = response.ErrorDetail,
|
ErrorMessage = response.ErrorDetail
|
||||||
AmountRemaining = invoice.Price
|
|
||||||
});
|
});
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return UnprocessableEntity(new
|
return UnprocessableEntity(new
|
||||||
{
|
{
|
||||||
ErrorMessage = $"Payment method {paymentMethodId} is not supported",
|
ErrorMessage = $"Payment method {paymentMethodId} is not supported"
|
||||||
AmountRemaining = invoice.Price
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,8 +95,7 @@ namespace BTCPayServer.Controllers
|
||||||
{
|
{
|
||||||
return BadRequest(new
|
return BadRequest(new
|
||||||
{
|
{
|
||||||
ErrorMessage = e.Message,
|
ErrorMessage = e.Message
|
||||||
AmountRemaining = invoice.Price
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -829,6 +829,15 @@ namespace BTCPayServer.Controllers
|
||||||
NetworkFeeMode.Never => 0,
|
NetworkFeeMode.Never => 0,
|
||||||
_ => throw new NotImplementedException()
|
_ => throw new NotImplementedException()
|
||||||
},
|
},
|
||||||
|
RequiredConfirmations = invoice.SpeedPolicy switch
|
||||||
|
{
|
||||||
|
SpeedPolicy.HighSpeed => 0,
|
||||||
|
SpeedPolicy.MediumSpeed => 1,
|
||||||
|
SpeedPolicy.LowMediumSpeed => 2,
|
||||||
|
SpeedPolicy.LowSpeed => 6,
|
||||||
|
_ => null
|
||||||
|
},
|
||||||
|
ReceivedConfirmations = invoice.GetAllBitcoinPaymentData(false).FirstOrDefault()?.ConfirmationCount,
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
Status = invoice.StatusString,
|
Status = invoice.StatusString,
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||||
|
|
|
@ -77,5 +77,7 @@ namespace BTCPayServer.Models.InvoicingModels
|
||||||
public string ReceiptLink { get; set; }
|
public string ReceiptLink { get; set; }
|
||||||
public bool AltcoinsBuild { get; set; }
|
public bool AltcoinsBuild { get; set; }
|
||||||
public CheckoutType CheckoutType { get; set; }
|
public CheckoutType CheckoutType { get; set; }
|
||||||
|
public int? RequiredConfirmations { get; set; }
|
||||||
|
public long? ReceivedConfirmations { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
@model PaymentModel
|
@model PaymentModel
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#checkout-cheating form + form { margin-top: var(--btcpay-space-l); }
|
||||||
|
</style>
|
||||||
<main id="checkout-cheating" class="shadow-lg" v-cloak v-if="display">
|
<main id="checkout-cheating" class="shadow-lg" v-cloak v-if="display">
|
||||||
<section>
|
<section>
|
||||||
<p id="CheatSuccessMessage" class="alert alert-success text-break" v-if="successMessage" v-text="successMessage"></p>
|
<p id="CheatSuccessMessage" class="alert alert-success text-break" v-if="successMessage" v-text="successMessage"></p>
|
||||||
<p id="CheatErrorMessage" class="alert alert-danger text-break" v-if="errorMessage" v-text="errorMessage"></p>
|
<p id="CheatErrorMessage" class="alert alert-danger text-break" v-if="errorMessage" v-text="errorMessage"></p>
|
||||||
<form id="test-payment" :action="`/i/${invoiceId}/test-payment`" method="post" v-on:submit.prevent="handleFormSubmit($event, 'paying')" v-if="displayPayment">
|
<form id="test-payment" :action="`/i/${invoiceId}/test-payment`" method="post" v-on:submit.prevent="handleFormSubmit($event, 'paying')" v-if="displayPayment">
|
||||||
<input name="CryptoCode" type="hidden" value="@Model.CryptoCode">
|
<input name="CryptoCode" type="hidden" :value="cryptoCode">
|
||||||
<input name="PaymentMethodId" type="hidden" :value="paymentMethodId">
|
<input name="PaymentMethodId" type="hidden" :value="paymentMethodId">
|
||||||
<label for="FakePayAmount" class="control-label form-label">Fake a @Model.CryptoCode payment for testing</label>
|
<label for="FakePayAmount" class="control-label form-label">Fake a {{cryptoCode}} payment for testing</label>
|
||||||
<div class="d-flex gap-2 mb-2">
|
<div class="d-flex gap-2 mb-2">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="FakePayAmount" name="Amount" type="number" step="0.00000001" min="0" class="form-control" placeholder="Amount" v-model="amountRemaining" :disabled="paying || paymentMethodId === 'BTC_LightningLike'"/>
|
<input id="FakePayAmount" name="Amount" type="number" :step="isSats ? '1' : '0.00000001'" min="0" class="form-control" placeholder="Amount" v-model="amountRemaining" :disabled="paying || paymentMethodId === 'BTC_LightningLike'"/>
|
||||||
<div id="test-payment-crypto-code" class="input-group-addon input-group-text">@Model.CryptoCode</div>
|
<div id="test-payment-crypto-code" class="input-group-addon input-group-text" v-text="cryptoCode"></div>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-secondary flex-shrink-0 px-3 w-100px" type="submit" :disabled="paying" id="FakePay">Pay</button>
|
<button class="btn btn-secondary flex-shrink-0 px-3 w-100px" type="submit" :disabled="paying" id="FakePay">Pay</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<form id="mine-block" :action="`/i/${invoiceId}/mine-blocks`" method="post" class="mt-4" v-on:submit.prevent="handleFormSubmit($event, 'mining')" v-if="displayMine">
|
<form id="mine-block" :action="`/i/${invoiceId}/mine-blocks`" method="post" v-on:submit.prevent="handleFormSubmit($event, 'mining')" v-if="displayMine">
|
||||||
<label for="BlockCount" class="control-label form-label">Mine to test processing and settlement</label>
|
<label for="BlockCount" class="control-label form-label">Mine to test processing and settlement</label>
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
@ -26,7 +29,7 @@
|
||||||
<button class="btn btn-secondary flex-shrink-0 px-3 w-100px" type="submit" :disabled="mining" id="Mine">Mine</button>
|
<button class="btn btn-secondary flex-shrink-0 px-3 w-100px" type="submit" :disabled="mining" id="Mine">Mine</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<form id="expire-invoice" :action="`/i/${invoiceId}/expire`" method="post" class="mt-4" v-on:submit.prevent="handleFormSubmit($event, 'expiring')" v-if="displayExpire">
|
<form id="expire-invoice" :action="`/i/${invoiceId}/expire`" method="post" v-on:submit.prevent="handleFormSubmit($event, 'expiring')" v-if="displayExpire">
|
||||||
<label for="ExpirySeconds" class="control-label form-label">Expire invoice in …</label>
|
<label for="ExpirySeconds" class="control-label form-label">Expire invoice in …</label>
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
@ -49,27 +52,32 @@
|
||||||
paying: false,
|
paying: false,
|
||||||
mining: false,
|
mining: false,
|
||||||
expiring: false,
|
expiring: false,
|
||||||
amountRemaining: parseFloat(this.btcDue)
|
amountRemaining: this.btcDue
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
invoiceId: String,
|
invoiceId: String,
|
||||||
paymentMethodId: String,
|
paymentMethodId: String,
|
||||||
|
cryptoCode: String,
|
||||||
btcDue: Number,
|
btcDue: Number,
|
||||||
isPaid: Boolean
|
isProcessing: Boolean,
|
||||||
|
isSettled: Boolean
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
display() {
|
display() {
|
||||||
return this.successMessage || this.errorMessage || this.displayPayment || this.displayMine || this.displayExpire;
|
return this.successMessage || this.errorMessage || this.displayPayment || this.displayMine || this.displayExpire;
|
||||||
},
|
},
|
||||||
displayPayment () {
|
displayPayment () {
|
||||||
return !this.isPaid;
|
return !this.isSettled && !this.isProcessing;
|
||||||
},
|
},
|
||||||
displayExpire () {
|
displayExpire () {
|
||||||
return !this.isPaid;
|
return !this.isSettled && !this.isProcessing;
|
||||||
},
|
},
|
||||||
displayMine () {
|
displayMine () {
|
||||||
return this.paymentMethodId === 'BTC';
|
return this.paymentMethodId === 'BTC';
|
||||||
|
},
|
||||||
|
isSats () {
|
||||||
|
return this.cryptoCode === 'sats';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -76,7 +76,14 @@
|
||||||
<vc:icon symbol="caret-down" />
|
<vc:icon symbol="caret-down" />
|
||||||
</button>
|
</button>
|
||||||
<div id="PaymentDetails" class="payment-details" v-collapsible="displayPaymentDetails">
|
<div id="PaymentDetails" class="payment-details" v-collapsible="displayPaymentDetails">
|
||||||
<payment-details :srv-model="srvModel" :is-active="isActive" class="pb-4"></payment-details>
|
<payment-details
|
||||||
|
:srv-model="srvModel"
|
||||||
|
:is-active="isActive"
|
||||||
|
:order-amount="orderAmount"
|
||||||
|
:btc-paid="btcPaid"
|
||||||
|
:btc-due="btcDue"
|
||||||
|
:show-recommended-fee="showRecommendedFee"
|
||||||
|
class="pb-4" />
|
||||||
</div>
|
</div>
|
||||||
@if (displayedPaymentMethods.Count > 1 || hasPaymentPlugins)
|
@if (displayedPaymentMethods.Count > 1 || hasPaymentPlugins)
|
||||||
{
|
{
|
||||||
|
@ -99,7 +106,41 @@
|
||||||
<component v-if="paymentMethodComponent" :is="paymentMethodComponent" :model="srvModel" />
|
<component v-if="paymentMethodComponent" :is="paymentMethodComponent" :model="srvModel" />
|
||||||
</section>
|
</section>
|
||||||
<section id="result" v-else>
|
<section id="result" v-else>
|
||||||
<div id="paid" v-if="isPaid">
|
<div v-if="isProcessing" id="processing" key="processing">
|
||||||
|
<div class="top">
|
||||||
|
<span class="icn">
|
||||||
|
<vc:icon symbol="payment-sent" />
|
||||||
|
</span>
|
||||||
|
<h4 v-t="'payment_sent'"></h4>
|
||||||
|
<div id="PaymentDetails" class="payment-details">
|
||||||
|
<dl class="mb-0">
|
||||||
|
<div>
|
||||||
|
<dt v-t="'invoice_id'"></dt>
|
||||||
|
<dd v-text="srvModel.invoiceId" :data-clipboard="srvModel.invoiceId" :data-clipboard-confirm="$t('copy_confirm')"></dd>
|
||||||
|
</div>
|
||||||
|
<div v-if="srvModel.orderId">
|
||||||
|
<dt v-t="'order_id'"></dt>
|
||||||
|
<dd v-text="srvModel.orderId" :data-clipboard="srvModel.orderId" :data-clipboard-confirm="$t('copy_confirm')"></dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
<payment-details
|
||||||
|
:srv-model="srvModel"
|
||||||
|
:is-active="isActive"
|
||||||
|
:order-amount="orderAmount"
|
||||||
|
:btc-paid="btcPaid"
|
||||||
|
:btc-due="btcDue"
|
||||||
|
:show-recommended-fee="showRecommendedFee"
|
||||||
|
v-collapsible="displayPaymentDetails" />
|
||||||
|
</div>
|
||||||
|
<button class="d-flex align-items-center gap-1 btn btn-link payment-details-button" type="button" :aria-expanded="displayPaymentDetails ? 'true' : 'false'" v-on:click="displayPaymentDetails = !displayPaymentDetails">
|
||||||
|
<span class="fw-semibold" v-t="'view_details'"></span>
|
||||||
|
<vc:icon symbol="caret-down" />
|
||||||
|
</button>
|
||||||
|
<p class="text-center mt-3" v-t="'payment_sent_body'"></p>
|
||||||
|
<p class="text-center" v-if="srvModel.receivedConfirmations !== null && srvModel.requiredConfirmations != null" v-t="{ path: 'payment_sent_confirmations', args: { cryptoCode: realCryptoCode, receivedConfirmations: srvModel.receivedConfirmations, requiredConfirmations: srvModel.requiredConfirmations } }"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="isSettled" id="settled" key="settled">
|
||||||
<div class="top">
|
<div class="top">
|
||||||
<span class="icn">
|
<span class="icn">
|
||||||
<div id="confetti" v-if="srvModel.celebratePayment" v-on:click="celebratePayment(5000)"></div>
|
<div id="confetti" v-if="srvModel.celebratePayment" v-on:click="celebratePayment(5000)"></div>
|
||||||
|
@ -117,7 +158,14 @@
|
||||||
<dd v-text="srvModel.orderId" :data-clipboard="srvModel.orderId" data-clipboard-hover="start"></dd>
|
<dd v-text="srvModel.orderId" :data-clipboard="srvModel.orderId" data-clipboard-hover="start"></dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
<payment-details :srv-model="srvModel" :is-active="isActive" class="mb-5"></payment-details>
|
<payment-details
|
||||||
|
:srv-model="srvModel"
|
||||||
|
:is-active="isActive"
|
||||||
|
:order-amount="orderAmount"
|
||||||
|
:btc-paid="btcPaid"
|
||||||
|
:btc-due="btcDue"
|
||||||
|
:show-recommended-fee="showRecommendedFee"
|
||||||
|
class="mb-5" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
|
@ -126,7 +174,7 @@
|
||||||
<button v-else-if="isModal" class="btn btn-secondary rounded-pill w-100" v-on:click="close" v-t="'Close'"></button>
|
<button v-else-if="isModal" class="btn btn-secondary rounded-pill w-100" v-on:click="close" v-t="'Close'"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="expired" v-if="isUnpayable">
|
<div v-if="isInvalid" id="unpaid" key="unpaid">
|
||||||
<div class="top">
|
<div class="top">
|
||||||
<span class="icn">
|
<span class="icn">
|
||||||
<vc:icon symbol="invoice-expired" />
|
<vc:icon symbol="invoice-expired" />
|
||||||
|
@ -143,7 +191,14 @@
|
||||||
<dd v-text="srvModel.orderId" :data-clipboard="srvModel.orderId" data-clipboard-hover="start"></dd>
|
<dd v-text="srvModel.orderId" :data-clipboard="srvModel.orderId" data-clipboard-hover="start"></dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
<payment-details :srv-model="srvModel" :is-active="isActive" v-collapsible="displayPaymentDetails"></payment-details>
|
<payment-details
|
||||||
|
:srv-model="srvModel"
|
||||||
|
:is-active="isActive"
|
||||||
|
:order-amount="orderAmount"
|
||||||
|
:btc-paid="btcPaid"
|
||||||
|
:btc-due="btcDue"
|
||||||
|
:show-recommended-fee="showRecommendedFee"
|
||||||
|
v-collapsible="displayPaymentDetails" />
|
||||||
</div>
|
</div>
|
||||||
<button class="d-flex align-items-center gap-1 btn btn-link payment-details-button" type="button" :aria-expanded="displayPaymentDetails ? 'true' : 'false'" v-on:click="displayPaymentDetails = !displayPaymentDetails">
|
<button class="d-flex align-items-center gap-1 btn btn-link payment-details-button" type="button" :aria-expanded="displayPaymentDetails ? 'true' : 'false'" v-on:click="displayPaymentDetails = !displayPaymentDetails">
|
||||||
<span class="fw-semibold" v-t="'view_details'"></span>
|
<span class="fw-semibold" v-t="'view_details'"></span>
|
||||||
|
@ -160,7 +215,7 @@
|
||||||
</main>
|
</main>
|
||||||
@if (Env.CheatMode)
|
@if (Env.CheatMode)
|
||||||
{
|
{
|
||||||
<checkout-cheating invoice-id="@Model.InvoiceId" :btc-due="srvModel.btcDue" :is-paid="isPaid" :payment-method-id="pmId"></checkout-cheating>
|
<checkout-cheating invoice-id="@Model.InvoiceId" :btc-due="btcDue" :is-settled="isSettled" :is-processing="isProcessing" :payment-method-id="pmId" :crypto-code="srvModel.cryptoCode"></checkout-cheating>
|
||||||
}
|
}
|
||||||
<footer class="store-footer">
|
<footer class="store-footer">
|
||||||
<a class="store-powered-by" href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
|
<a class="store-powered-by" href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
|
||||||
|
|
|
@ -148,10 +148,11 @@ section dl > div dd {
|
||||||
width: 1.5rem;
|
width: 1.5rem;
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
}
|
}
|
||||||
#result #paid .top .icn .icon {
|
#result #settled .top .icn .icon,
|
||||||
|
#result #processing .top .icn .icon {
|
||||||
color: var(--btcpay-primary);
|
color: var(--btcpay-primary);
|
||||||
}
|
}
|
||||||
#result #expired .top .icn .icon {
|
#result #unpaid .top .icn .icon {
|
||||||
color: var(--btcpay-body-text-muted);
|
color: var(--btcpay-body-text-muted);
|
||||||
}
|
}
|
||||||
#DefaultLang {
|
#DefaultLang {
|
||||||
|
|
|
@ -36,8 +36,11 @@ Vue.directive('collapsible', {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const STATUS_PAID = ['complete', 'confirmed', 'paid'];
|
// These are the legacy states, see InvoiceEntity
|
||||||
const STATUS_UNPAYABLE = ['expired', 'invalid'];
|
const STATUS_PAYABLE = ['new'];
|
||||||
|
const STATUS_PAID = ['paid'];
|
||||||
|
const STATUS_SETTLED = ['complete', 'confirmed'];
|
||||||
|
const STATUS_INVALID = ['expired', 'invalid'];
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
function computeStartingLanguage() {
|
function computeStartingLanguage() {
|
||||||
|
@ -82,21 +85,11 @@ const PaymentDetails = {
|
||||||
template: '#payment-details',
|
template: '#payment-details',
|
||||||
props: {
|
props: {
|
||||||
srvModel: Object,
|
srvModel: Object,
|
||||||
isActive: Boolean
|
isActive: Boolean,
|
||||||
},
|
showRecommendedFee: Boolean,
|
||||||
computed: {
|
orderAmount: Number,
|
||||||
orderAmount () {
|
btcPaid: Number,
|
||||||
return parseFloat(this.srvModel.orderAmount);
|
btcDue: Number
|
||||||
},
|
|
||||||
btcDue () {
|
|
||||||
return parseFloat(this.srvModel.btcDue);
|
|
||||||
},
|
|
||||||
btcPaid () {
|
|
||||||
return parseFloat(this.srvModel.btcPaid);
|
|
||||||
},
|
|
||||||
showRecommendedFee () {
|
|
||||||
return this.isActive && this.srvModel.showRecommendedFee && this.srvModel.feeRate;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,18 +111,22 @@ function initApp() {
|
||||||
emailAddressInputInvalid: false,
|
emailAddressInputInvalid: false,
|
||||||
paymentMethodId: null,
|
paymentMethodId: null,
|
||||||
endData: null,
|
endData: null,
|
||||||
isModal: srvModel.isModal
|
isModal: srvModel.isModal,
|
||||||
|
pollTimeoutID: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isUnpayable () {
|
isInvalid () {
|
||||||
return STATUS_UNPAYABLE.includes(this.srvModel.status);
|
return STATUS_INVALID.includes(this.srvModel.status);
|
||||||
},
|
},
|
||||||
isPaid () {
|
isSettled () {
|
||||||
|
return STATUS_SETTLED.includes(this.srvModel.status);
|
||||||
|
},
|
||||||
|
isProcessing () {
|
||||||
return STATUS_PAID.includes(this.srvModel.status);
|
return STATUS_PAID.includes(this.srvModel.status);
|
||||||
},
|
},
|
||||||
isActive () {
|
isActive () {
|
||||||
return !this.isUnpayable && !this.isPaid;
|
return STATUS_PAYABLE.includes(this.srvModel.status);
|
||||||
},
|
},
|
||||||
showInfo () {
|
showInfo () {
|
||||||
return this.showTimer || this.showPaymentDueInfo;
|
return this.showTimer || this.showPaymentDueInfo;
|
||||||
|
@ -141,16 +138,16 @@ function initApp() {
|
||||||
return this.btcPaid > 0 && this.btcDue > 0;
|
return this.btcPaid > 0 && this.btcDue > 0;
|
||||||
},
|
},
|
||||||
showRecommendedFee () {
|
showRecommendedFee () {
|
||||||
return this.isActive() && this.srvModel.showRecommendedFee && this.srvModel.feeRate;
|
return this.isActive && this.srvModel.showRecommendedFee && this.srvModel.feeRate;
|
||||||
},
|
},
|
||||||
orderAmount () {
|
orderAmount () {
|
||||||
return parseFloat(this.srvModel.orderAmount);
|
return this.asNumber(this.srvModel.orderAmount);
|
||||||
},
|
},
|
||||||
btcDue () {
|
btcDue () {
|
||||||
return parseFloat(this.srvModel.btcDue);
|
return this.asNumber(this.srvModel.btcDue);
|
||||||
},
|
},
|
||||||
btcPaid () {
|
btcPaid () {
|
||||||
return parseFloat(this.srvModel.btcPaid);
|
return this.asNumber(this.srvModel.btcPaid);
|
||||||
},
|
},
|
||||||
pmId () {
|
pmId () {
|
||||||
return this.paymentMethodId || this.srvModel.paymentMethodId;
|
return this.paymentMethodId || this.srvModel.paymentMethodId;
|
||||||
|
@ -181,13 +178,26 @@ function initApp() {
|
||||||
},
|
},
|
||||||
isPluginPaymentMethod () {
|
isPluginPaymentMethod () {
|
||||||
return !this.paymentMethodIds.includes(this.pmId);
|
return !this.paymentMethodIds.includes(this.pmId);
|
||||||
|
},
|
||||||
|
realCryptoCode () {
|
||||||
|
return this.srvModel.cryptoCode.toLowerCase() === 'sats' ? 'BTC' : this.srvModel.cryptoCode;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
isPaid: function (newValue, oldValue) {
|
isProcessing: function (newValue, oldValue) {
|
||||||
|
if (newValue === true && oldValue === false) {
|
||||||
|
// poll from here on
|
||||||
|
this.listenForConfirmations();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isSettled: function (newValue, oldValue) {
|
||||||
if (newValue === true && oldValue === false) {
|
if (newValue === true && oldValue === false) {
|
||||||
const duration = 5000;
|
const duration = 5000;
|
||||||
const self = this;
|
const self = this;
|
||||||
|
// stop polling
|
||||||
|
if (this.pollTimeoutID) {
|
||||||
|
clearTimeout(this.pollTimeoutID);
|
||||||
|
}
|
||||||
// celebration!
|
// celebration!
|
||||||
Vue.nextTick(function () {
|
Vue.nextTick(function () {
|
||||||
self.celebratePayment(duration);
|
self.celebratePayment(duration);
|
||||||
|
@ -208,9 +218,12 @@ function initApp() {
|
||||||
mounted () {
|
mounted () {
|
||||||
this.updateData(this.srvModel);
|
this.updateData(this.srvModel);
|
||||||
this.updateTimer();
|
this.updateTimer();
|
||||||
if (this.isActive) {
|
if (this.isActive || this.isProcessing) {
|
||||||
this.listenIn();
|
this.listenIn();
|
||||||
}
|
}
|
||||||
|
if (this.isProcessing) {
|
||||||
|
this.listenForConfirmations();
|
||||||
|
}
|
||||||
updateLanguageSelect();
|
updateLanguageSelect();
|
||||||
window.parent.postMessage('loaded', '*');
|
window.parent.postMessage('loaded', '*');
|
||||||
},
|
},
|
||||||
|
@ -224,6 +237,9 @@ function initApp() {
|
||||||
changeLanguage (e) {
|
changeLanguage (e) {
|
||||||
updateLanguage(e.target.value);
|
updateLanguage(e.target.value);
|
||||||
},
|
},
|
||||||
|
asNumber (val) {
|
||||||
|
return parseFloat(val.replace(/\s/g, '')); // e.g. sats are formatted with spaces: 1 000 000
|
||||||
|
},
|
||||||
padTime (val) {
|
padTime (val) {
|
||||||
return val.toString().padStart(2, '0');
|
return val.toString().padStart(2, '0');
|
||||||
},
|
},
|
||||||
|
@ -257,13 +273,26 @@ function initApp() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// fallback in case there is no websocket support
|
// fallback in case there is no websocket support
|
||||||
(function watcher() {
|
if (!socket || socket.readyState !== 1) {
|
||||||
setTimeout(async function () {
|
this.pollUpdates(2000, socket)
|
||||||
if (socket === null || socket.readyState !== 1) {
|
|
||||||
await updateFn();
|
|
||||||
}
|
}
|
||||||
watcher();
|
},
|
||||||
}, 2000);
|
listenForConfirmations () {
|
||||||
|
this.pollUpdates(30000);
|
||||||
|
},
|
||||||
|
pollUpdates (interval, socket) {
|
||||||
|
const self = this;
|
||||||
|
const updateFn = this.fetchData;
|
||||||
|
if (self.pollTimeoutID) {
|
||||||
|
clearTimeout(self.pollTimeoutID);
|
||||||
|
}
|
||||||
|
(function pollFn() {
|
||||||
|
self.pollTimeoutID = setTimeout(async function () {
|
||||||
|
if (!socket || socket.readyState !== 1) {
|
||||||
|
await updateFn();
|
||||||
|
pollFn();
|
||||||
|
}
|
||||||
|
}, interval);
|
||||||
})();
|
})();
|
||||||
},
|
},
|
||||||
async fetchData () {
|
async fetchData () {
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
"address": "Address",
|
"address": "Address",
|
||||||
"lightning": "Lightning",
|
"lightning": "Lightning",
|
||||||
"payment_link": "Payment Link",
|
"payment_link": "Payment Link",
|
||||||
|
"payment_sent": "Payment Sent",
|
||||||
|
"payment_sent_body": "Your payment has been received and is now processing.",
|
||||||
|
"payment_sent_confirmations": "Currently it has {{receivedConfirmations}} confirmations. Once it receives {{requiredConfirmations}} confirmations on the {{cryptoCode}} blockchain, the status will be updated to settled.",
|
||||||
"invoice_paid": "Invoice Paid",
|
"invoice_paid": "Invoice Paid",
|
||||||
"invoice_expired": "Invoice Expired",
|
"invoice_expired": "Invoice Expired",
|
||||||
"invoice_expired_body": "An invoice is only valid for {{minutes}} minutes.\n\nReturn to {{storeName}} if you would like to resubmit a payment.",
|
"invoice_expired_body": "An invoice is only valid for {{minutes}} minutes.\n\nReturn to {{storeName}} if you would like to resubmit a payment.",
|
||||||
|
@ -34,5 +37,5 @@
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
"copy_confirm": "Copied",
|
"copy_confirm": "Copied",
|
||||||
"powered_by": "Powered by",
|
"powered_by": "Powered by",
|
||||||
"conversion_body": "This service is provided by 3rd party. Please keep in mind that we have no control over how providers will forward your funds. Invoice will only be marked paid once funds are received on {{cryptoCode}} Blockchain."
|
"conversion_body": "This service is provided by 3rd party. Please keep in mind that we have no control over how providers will forward your funds. Invoice will only be marked paid once funds are received on the {{cryptoCode}} blockchain."
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue