mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-03-11 01:35:22 +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);
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
var expiredSection = s.Driver.FindElement(By.Id("expired"));
|
||||
var expiredSection = s.Driver.FindElement(By.Id("unpaid"));
|
||||
Assert.True(expiredSection.Displayed);
|
||||
Assert.Contains("Invoice Expired", expiredSection.Text);
|
||||
});
|
||||
|
@ -181,12 +181,27 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
Assert.Contains("Created transaction",
|
||||
s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).Text);
|
||||
s.Server.ExplorerNode.Generate(1);
|
||||
s.Server.ExplorerNode.Generate(2);
|
||||
paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo"));
|
||||
Assert.Contains("The invoice hasn't been paid in full", 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
|
||||
s.Driver.FindElement(By.Id("Mine")).Click();
|
||||
TestUtils.Eventually(() =>
|
||||
|
@ -194,17 +209,13 @@ namespace BTCPayServer.Tests
|
|||
Assert.Contains("Mined 1 block",
|
||||
s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).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();
|
||||
|
||||
// Settled
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
s.Server.ExplorerNode.Generate(1);
|
||||
var paidSection = s.Driver.WaitForElement(By.Id("paid"));
|
||||
Assert.True(paidSection.Displayed);
|
||||
Assert.Contains("Invoice Paid", paidSection.Text);
|
||||
var settledSection = s.Driver.WaitForElement(By.Id("settled"));
|
||||
Assert.True(settledSection.Displayed);
|
||||
Assert.Contains("Invoice Paid", settledSection.Text);
|
||||
});
|
||||
s.Driver.FindElement(By.Id("confetti"));
|
||||
s.Driver.FindElement(By.Id("ReceiptLink"));
|
||||
|
|
|
@ -80,15 +80,13 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
return UnprocessableEntity(new
|
||||
{
|
||||
ErrorMessage = response.ErrorDetail,
|
||||
AmountRemaining = invoice.Price
|
||||
ErrorMessage = response.ErrorDetail
|
||||
});
|
||||
|
||||
default:
|
||||
return UnprocessableEntity(new
|
||||
{
|
||||
ErrorMessage = $"Payment method {paymentMethodId} is not supported",
|
||||
AmountRemaining = invoice.Price
|
||||
ErrorMessage = $"Payment method {paymentMethodId} is not supported"
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -97,8 +95,7 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
return BadRequest(new
|
||||
{
|
||||
ErrorMessage = e.Message,
|
||||
AmountRemaining = invoice.Price
|
||||
ErrorMessage = e.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -829,6 +829,15 @@ namespace BTCPayServer.Controllers
|
|||
NetworkFeeMode.Never => 0,
|
||||
_ => 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
|
||||
Status = invoice.StatusString,
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
|
|
@ -77,5 +77,7 @@ namespace BTCPayServer.Models.InvoicingModels
|
|||
public string ReceiptLink { get; set; }
|
||||
public bool AltcoinsBuild { get; set; }
|
||||
public CheckoutType CheckoutType { get; set; }
|
||||
public int? RequiredConfirmations { get; set; }
|
||||
public long? ReceivedConfirmations { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
@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">
|
||||
<section>
|
||||
<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>
|
||||
<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">
|
||||
<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="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'"/>
|
||||
<div id="test-payment-crypto-code" class="input-group-addon input-group-text">@Model.CryptoCode</div>
|
||||
<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" v-text="cryptoCode"></div>
|
||||
</div>
|
||||
<button class="btn btn-secondary flex-shrink-0 px-3 w-100px" type="submit" :disabled="paying" id="FakePay">Pay</button>
|
||||
</div>
|
||||
</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>
|
||||
<div class="d-flex gap-2">
|
||||
<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>
|
||||
</div>
|
||||
</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>
|
||||
<div class="d-flex gap-2">
|
||||
<div class="input-group">
|
||||
|
@ -49,27 +52,32 @@
|
|||
paying: false,
|
||||
mining: false,
|
||||
expiring: false,
|
||||
amountRemaining: parseFloat(this.btcDue)
|
||||
amountRemaining: this.btcDue
|
||||
}
|
||||
},
|
||||
props: {
|
||||
invoiceId: String,
|
||||
paymentMethodId: String,
|
||||
cryptoCode: String,
|
||||
btcDue: Number,
|
||||
isPaid: Boolean
|
||||
isProcessing: Boolean,
|
||||
isSettled: Boolean
|
||||
},
|
||||
computed: {
|
||||
display() {
|
||||
return this.successMessage || this.errorMessage || this.displayPayment || this.displayMine || this.displayExpire;
|
||||
},
|
||||
displayPayment () {
|
||||
return !this.isPaid;
|
||||
return !this.isSettled && !this.isProcessing;
|
||||
},
|
||||
displayExpire () {
|
||||
return !this.isPaid;
|
||||
return !this.isSettled && !this.isProcessing;
|
||||
},
|
||||
displayMine () {
|
||||
return this.paymentMethodId === 'BTC';
|
||||
},
|
||||
isSats () {
|
||||
return this.cryptoCode === 'sats';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -76,7 +76,14 @@
|
|||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
<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>
|
||||
@if (displayedPaymentMethods.Count > 1 || hasPaymentPlugins)
|
||||
{
|
||||
|
@ -99,11 +106,45 @@
|
|||
<component v-if="paymentMethodComponent" :is="paymentMethodComponent" :model="srvModel" />
|
||||
</section>
|
||||
<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">
|
||||
<span class="icn">
|
||||
<div id="confetti" v-if="srvModel.celebratePayment" v-on:click="celebratePayment(5000)"></div>
|
||||
<vc:icon symbol="payment-complete"/>
|
||||
<vc:icon symbol="payment-complete" />
|
||||
</span>
|
||||
<h4 v-t="'invoice_paid'"></h4>
|
||||
<div id="PaymentDetails" class="payment-details">
|
||||
|
@ -117,16 +158,23 @@
|
|||
<dd v-text="srvModel.orderId" :data-clipboard="srvModel.orderId" data-clipboard-hover="start"></dd>
|
||||
</div>
|
||||
</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 class="buttons">
|
||||
<a v-if="srvModel.receiptLink" class="btn btn-primary rounded-pill w-100" :href="srvModel.receiptLink" :target="isModal ? '_top' : null" v-t="'view_receipt'" id="ReceiptLink"></a>
|
||||
<a v-if="storeLink" class="btn btn-secondary rounded-pill w-100" :href="storeLink" :target="isModal ? '_top' : null" v-html="$t('return_to_store', { storeName: srvModel.storeName })" id="StoreLink"></a>
|
||||
<a v-if="storeLink" class="btn btn-secondary rounded-pill w-100" :href="storeLink" :target="isModal ? '_top' : null" v-html="$t('return_to_store', { storeName: srvModel.storeName })" id="StoreLink"></a>
|
||||
<button v-else-if="isModal" class="btn btn-secondary rounded-pill w-100" v-on:click="close" v-t="'Close'"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="expired" v-if="isUnpayable">
|
||||
<div v-if="isInvalid" id="unpaid" key="unpaid">
|
||||
<div class="top">
|
||||
<span class="icn">
|
||||
<vc:icon symbol="invoice-expired" />
|
||||
|
@ -143,7 +191,14 @@
|
|||
<dd v-text="srvModel.orderId" :data-clipboard="srvModel.orderId" data-clipboard-hover="start"></dd>
|
||||
</div>
|
||||
</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>
|
||||
<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>
|
||||
|
@ -160,7 +215,7 @@
|
|||
</main>
|
||||
@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">
|
||||
<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;
|
||||
height: 1.5rem;
|
||||
}
|
||||
#result #paid .top .icn .icon {
|
||||
#result #settled .top .icn .icon,
|
||||
#result #processing .top .icn .icon {
|
||||
color: var(--btcpay-primary);
|
||||
}
|
||||
#result #expired .top .icn .icon {
|
||||
#result #unpaid .top .icn .icon {
|
||||
color: var(--btcpay-body-text-muted);
|
||||
}
|
||||
#DefaultLang {
|
||||
|
|
|
@ -36,8 +36,11 @@ Vue.directive('collapsible', {
|
|||
}
|
||||
});
|
||||
|
||||
const STATUS_PAID = ['complete', 'confirmed', 'paid'];
|
||||
const STATUS_UNPAYABLE = ['expired', 'invalid'];
|
||||
// These are the legacy states, see InvoiceEntity
|
||||
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);
|
||||
|
||||
function computeStartingLanguage() {
|
||||
|
@ -82,21 +85,11 @@ const PaymentDetails = {
|
|||
template: '#payment-details',
|
||||
props: {
|
||||
srvModel: Object,
|
||||
isActive: Boolean
|
||||
},
|
||||
computed: {
|
||||
orderAmount () {
|
||||
return parseFloat(this.srvModel.orderAmount);
|
||||
},
|
||||
btcDue () {
|
||||
return parseFloat(this.srvModel.btcDue);
|
||||
},
|
||||
btcPaid () {
|
||||
return parseFloat(this.srvModel.btcPaid);
|
||||
},
|
||||
showRecommendedFee () {
|
||||
return this.isActive && this.srvModel.showRecommendedFee && this.srvModel.feeRate;
|
||||
},
|
||||
isActive: Boolean,
|
||||
showRecommendedFee: Boolean,
|
||||
orderAmount: Number,
|
||||
btcPaid: Number,
|
||||
btcDue: Number
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,18 +111,22 @@ function initApp() {
|
|||
emailAddressInputInvalid: false,
|
||||
paymentMethodId: null,
|
||||
endData: null,
|
||||
isModal: srvModel.isModal
|
||||
isModal: srvModel.isModal,
|
||||
pollTimeoutID: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isUnpayable () {
|
||||
return STATUS_UNPAYABLE.includes(this.srvModel.status);
|
||||
isInvalid () {
|
||||
return STATUS_INVALID.includes(this.srvModel.status);
|
||||
},
|
||||
isPaid () {
|
||||
isSettled () {
|
||||
return STATUS_SETTLED.includes(this.srvModel.status);
|
||||
},
|
||||
isProcessing () {
|
||||
return STATUS_PAID.includes(this.srvModel.status);
|
||||
},
|
||||
isActive () {
|
||||
return !this.isUnpayable && !this.isPaid;
|
||||
return STATUS_PAYABLE.includes(this.srvModel.status);
|
||||
},
|
||||
showInfo () {
|
||||
return this.showTimer || this.showPaymentDueInfo;
|
||||
|
@ -141,16 +138,16 @@ function initApp() {
|
|||
return this.btcPaid > 0 && this.btcDue > 0;
|
||||
},
|
||||
showRecommendedFee () {
|
||||
return this.isActive() && this.srvModel.showRecommendedFee && this.srvModel.feeRate;
|
||||
return this.isActive && this.srvModel.showRecommendedFee && this.srvModel.feeRate;
|
||||
},
|
||||
orderAmount () {
|
||||
return parseFloat(this.srvModel.orderAmount);
|
||||
return this.asNumber(this.srvModel.orderAmount);
|
||||
},
|
||||
btcDue () {
|
||||
return parseFloat(this.srvModel.btcDue);
|
||||
return this.asNumber(this.srvModel.btcDue);
|
||||
},
|
||||
btcPaid () {
|
||||
return parseFloat(this.srvModel.btcPaid);
|
||||
return this.asNumber(this.srvModel.btcPaid);
|
||||
},
|
||||
pmId () {
|
||||
return this.paymentMethodId || this.srvModel.paymentMethodId;
|
||||
|
@ -181,13 +178,26 @@ function initApp() {
|
|||
},
|
||||
isPluginPaymentMethod () {
|
||||
return !this.paymentMethodIds.includes(this.pmId);
|
||||
},
|
||||
realCryptoCode () {
|
||||
return this.srvModel.cryptoCode.toLowerCase() === 'sats' ? 'BTC' : this.srvModel.cryptoCode;
|
||||
}
|
||||
},
|
||||
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) {
|
||||
const duration = 5000;
|
||||
const self = this;
|
||||
// stop polling
|
||||
if (this.pollTimeoutID) {
|
||||
clearTimeout(this.pollTimeoutID);
|
||||
}
|
||||
// celebration!
|
||||
Vue.nextTick(function () {
|
||||
self.celebratePayment(duration);
|
||||
|
@ -208,9 +218,12 @@ function initApp() {
|
|||
mounted () {
|
||||
this.updateData(this.srvModel);
|
||||
this.updateTimer();
|
||||
if (this.isActive) {
|
||||
if (this.isActive || this.isProcessing) {
|
||||
this.listenIn();
|
||||
}
|
||||
if (this.isProcessing) {
|
||||
this.listenForConfirmations();
|
||||
}
|
||||
updateLanguageSelect();
|
||||
window.parent.postMessage('loaded', '*');
|
||||
},
|
||||
|
@ -224,6 +237,9 @@ function initApp() {
|
|||
changeLanguage (e) {
|
||||
updateLanguage(e.target.value);
|
||||
},
|
||||
asNumber (val) {
|
||||
return parseFloat(val.replace(/\s/g, '')); // e.g. sats are formatted with spaces: 1 000 000
|
||||
},
|
||||
padTime (val) {
|
||||
return val.toString().padStart(2, '0');
|
||||
},
|
||||
|
@ -257,13 +273,26 @@ function initApp() {
|
|||
}
|
||||
}
|
||||
// fallback in case there is no websocket support
|
||||
(function watcher() {
|
||||
setTimeout(async function () {
|
||||
if (socket === null || socket.readyState !== 1) {
|
||||
if (!socket || socket.readyState !== 1) {
|
||||
this.pollUpdates(2000, socket)
|
||||
}
|
||||
},
|
||||
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();
|
||||
}
|
||||
watcher();
|
||||
}, 2000);
|
||||
}, interval);
|
||||
})();
|
||||
},
|
||||
async fetchData () {
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
"address": "Address",
|
||||
"lightning": "Lightning",
|
||||
"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_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.",
|
||||
|
@ -34,5 +37,5 @@
|
|||
"copy": "Copy",
|
||||
"copy_confirm": "Copied",
|
||||
"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