mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2026-03-03 02:57:01 +00:00
Refactor blockonomics btc + qr code presentation
This commit is contained in:
@@ -34,25 +34,5 @@ class BlockonomicsController extends Controller
|
||||
return response()->json(['error' => 'Unable to fetch BTC price'], 500);
|
||||
}
|
||||
|
||||
public function getQRCode(Request $request)
|
||||
{
|
||||
$qr_string = $request->query('qr_string');
|
||||
$svg = $this->getPaymentQrCodeRaw($qr_string);
|
||||
return response($svg)->header('Content-Type', 'image/svg+xml');
|
||||
}
|
||||
|
||||
private function getPaymentQrCodeRaw($qr_string)
|
||||
{
|
||||
|
||||
$renderer = new ImageRenderer(
|
||||
new RendererStyle(150, margin: 0),
|
||||
new SvgImageBackEnd()
|
||||
);
|
||||
$writer = new Writer($renderer);
|
||||
|
||||
$qr = $writer->writeString($qr_string, 'utf-8');
|
||||
|
||||
return $qr;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Livewire\BillingPortal\Payments;
|
||||
|
||||
use Livewire\Component;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class BlockonomicsPriceDisplay extends Component
|
||||
{
|
||||
public $currency;
|
||||
|
||||
public $btc_price;
|
||||
|
||||
public $btc_amount;
|
||||
|
||||
public $countdown = '10:00';
|
||||
|
||||
public $is_refreshing = false;
|
||||
|
||||
protected $listeners = ['refresh-btc-price' => 'refreshBTCPrice'];
|
||||
|
||||
public function mount($currency, $btc_price, $btc_amount)
|
||||
{
|
||||
$this->currency = $currency;
|
||||
$this->btc_price = $btc_price;
|
||||
$this->btc_amount = $btc_amount;
|
||||
// Countdown will be initialized in the JavaScript @script section
|
||||
}
|
||||
|
||||
public function refreshBTCPrice()
|
||||
{
|
||||
$this->is_refreshing = true;
|
||||
|
||||
nlog('Refreshing BTC price');
|
||||
try {
|
||||
$response = Http::get('https://www.blockonomics.co/api/price', [
|
||||
'currency' => $this->currency,
|
||||
]);
|
||||
|
||||
if ($response->successful()) {
|
||||
$price = $response->object()->price ?? null;
|
||||
|
||||
if ($price) {
|
||||
$this->btc_price = $price;
|
||||
$this->btc_amount = number_format($this->btc_amount / $price, 10);
|
||||
|
||||
// Reset the countdown
|
||||
$this->startCountdown();
|
||||
$this->dispatch('btc-price-updated', [
|
||||
'price' => $price,
|
||||
'amount' => $this->btc_amount,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
nlog($response->body());
|
||||
} catch (\Exception $e) {
|
||||
nlog('Failed to refresh BTC price: ' . $e->getMessage());
|
||||
} finally {
|
||||
$this->is_refreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function startCountdown()
|
||||
{
|
||||
$this->countdown = '10:00';
|
||||
$this->dispatch('start-countdown', ['duration' => 600]);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return render('components.livewire.blockonomics-price-display');
|
||||
}
|
||||
}
|
||||
83
app/Livewire/BillingPortal/Payments/BlockonomicsQrCode.php
Normal file
83
app/Livewire/BillingPortal/Payments/BlockonomicsQrCode.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Livewire\BillingPortal\Payments;
|
||||
|
||||
use BaconQrCode\Writer;
|
||||
use Livewire\Component;
|
||||
use BaconQrCode\Renderer\ImageRenderer;
|
||||
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
|
||||
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
|
||||
|
||||
class BlockonomicsQrCode extends Component
|
||||
{
|
||||
public $btc_address;
|
||||
|
||||
public $btc_amount;
|
||||
|
||||
public $qr_code_svg = '';
|
||||
|
||||
public $is_loading = false;
|
||||
|
||||
public $error_message = '';
|
||||
|
||||
public function mount($btc_address, $btc_amount)
|
||||
{
|
||||
$this->btc_address = $btc_address;
|
||||
$this->btc_amount = $btc_amount;
|
||||
$this->fetchQRCode();
|
||||
}
|
||||
|
||||
public function fetchQRCode($newBtcAmount = null)
|
||||
{
|
||||
$this->is_loading = true;
|
||||
$this->error_message = '';
|
||||
|
||||
try {
|
||||
$btcAmount = $newBtcAmount ?? $this->btc_amount;
|
||||
$qrString = "bitcoin:{$this->btc_address}?amount={$btcAmount}";
|
||||
|
||||
$this->qr_code_svg = $this->getPaymentQrCodeRaw($qrString);
|
||||
} catch (\Exception $e) {
|
||||
$this->error_message = 'Error generating QR code';
|
||||
} finally {
|
||||
$this->is_loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function getPaymentQrCodeRaw($qr_string)
|
||||
{
|
||||
|
||||
$renderer = new ImageRenderer(
|
||||
new RendererStyle(150, margin: 0),
|
||||
new SvgImageBackEnd()
|
||||
);
|
||||
$writer = new Writer($renderer);
|
||||
|
||||
$qr = $writer->writeString($qr_string, 'utf-8');
|
||||
|
||||
return $qr;
|
||||
|
||||
}
|
||||
|
||||
public function updateQRCode($btcAmount)
|
||||
{
|
||||
$this->btc_amount = $btcAmount;
|
||||
$this->fetchQRCode($btcAmount);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return render('components.livewire.blockonomics-qr-code');
|
||||
}
|
||||
}
|
||||
14
public/build/assets/blockonomics-bab011a6.js
vendored
Normal file
14
public/build/assets/blockonomics-bab011a6.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
var h=Object.defineProperty;var y=(i,e,t)=>e in i?h(i,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):i[e]=t;var u=(i,e,t)=>(y(i,typeof e!="symbol"?e+"":e,t),t);import{i as p,w}from"./wait-8f4ae121.js";/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/class b{constructor(){u(this,"startTimer",()=>{});this.copyToClipboard=this.copyToClipboard.bind(this),this.refreshBTCPrice=this.refreshBTCPrice.bind(this),this.fetchAndDisplayQRCode=this.fetchAndDisplayQRCode.bind(this),this.startTimer=this.startTimer.bind(this)}copyToClipboard(e,t,n){const o=n?t.nextElementSibling:t,c=o.src,s=document.createElement("input"),r=document.getElementById(e),{value:a,innerText:d}=r||{},l=a||d;s.value=l,document.body.appendChild(s),s.select(),document.execCommand("copy"),document.body.removeChild(s),o.src="data:image/svg+xml;base64,"+btoa(`
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.04706 14C4.04706 8.55609 8.46025 4.1429 13.9042 4.1429C19.3482 4.1429 23.7613 8.55609 23.7613 14C23.7613 19.444 19.3482 23.8572 13.9042 23.8572C8.46025 23.8572 4.04706 19.444 4.04706 14Z" stroke="#000" stroke-width="2.19048" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9.52325 14L12.809 17.2858L18.2852 11.8096" stroke="#000" stroke-width="2.19048" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
`),setTimeout(()=>{o.src=c},5e3)}async fetchAndDisplayQRCode(){}async refreshBTCPrice(){const e=document.querySelector(".icon-refresh");e.classList.add("rotating"),document.getElementsByClassName("btc-value")[0].innerHTML="Refreshing...";const t=async()=>{try{const n=document.querySelector('meta[name="currency"]').content,o=await fetch(`/api/v1/get-btc-price?currency=${n}`);if(!o.ok)throw new Error("Network response was not ok");return(await o.json()).price}catch(n){console.error("There was a problem with the BTC price fetch operation:",n)}};try{const n=await t();if(n){const o=document.querySelector('meta[name="currency"]').content;document.getElementsByClassName("btc-value")[0].innerHTML="1 BTC = "+(n||"N/A")+" "+o+", updates in <span id='countdown'></span>";const c=(document.querySelector('meta[name="amount"]').content/n).toFixed(10);document.querySelector('input[name="btc_price"]').value=n,document.querySelector('input[name="btc_amount"]').value=c,document.getElementById("btc-amount").textContent=c;const s=document.querySelector('meta[name="btc_address"]').content,r=document.getElementById("qr-code-link"),a=document.getElementById("open-in-wallet-link");r.href=`bitcoin:${s}?amount=${c}`,a.href=`bitcoin:${s}?amount=${c}`,await this.fetchAndDisplayQRCode(c),this.startTimer(600)}}finally{e.classList.remove("rotating")}}handle(){window.copyToClipboard=this.copyToClipboard,window.refreshBTCPrice=this.refreshBTCPrice,window.fetchAndDisplayQRCode=this.fetchAndDisplayQRCode,window.startTimer=this.startTimer;const e=()=>{const t=document.querySelector('meta[name="btc_address"]').content,n=`wss://www.blockonomics.co/payment/${t}`,o=new WebSocket(n);o.onmessage=function(c){const s=JSON.parse(c.data),{status:r,txid:a,value:d}=s||{};console.log("Payment status:",r),(r===0||r===1||r===2)&&(document.querySelector('input[name="txid"]').value=a||"",document.querySelector('input[name="status"]').value=r||"",document.querySelector('input[name="btc_amount"]').value=d||"",document.querySelector('input[name="btc_address"]').value=t||"",document.getElementById("server-response").submit())}};startTimer(600),e(),fetchAndDisplayQRCode()}}function m(){new b().handle(),window.bootBlockonomics=m}p()?m():w("#blockonomics-payment").then(()=>m());
|
||||
14
public/build/assets/blockonomics-c3966bec.js
vendored
14
public/build/assets/blockonomics-c3966bec.js
vendored
@@ -1,14 +0,0 @@
|
||||
var h=Object.defineProperty;var y=(a,t,e)=>t in a?h(a,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[t]=e;var l=(a,t,e)=>(y(a,typeof t!="symbol"?t+"":t,e),e);import{i as w,w as p}from"./wait-8f4ae121.js";/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/class f{constructor(){l(this,"startTimer",t=>{const e=new Date().getTime()+t*1e3;document.getElementById("countdown").innerHTML="10:00 min";const o=()=>{const c=new Date().getTime(),n=e-c;if(document.getElementsByClassName("btc-value")[0].innerHTML.includes("Refreshing"))return;if(n<0){refreshBTCPrice();return}const s=Math.floor(n%(1e3*60*60)/(1e3*60)),i=Math.floor(n%(1e3*60)/1e3),d=String(s).padStart(2,"0"),m=String(i).padStart(2,"0");document.getElementById("countdown").innerHTML=d+":"+m+" min"};clearInterval(window.countdownInterval),window.countdownInterval=setInterval(o,1e3)});this.copyToClipboard=this.copyToClipboard.bind(this),this.refreshBTCPrice=this.refreshBTCPrice.bind(this),this.fetchAndDisplayQRCode=this.fetchAndDisplayQRCode.bind(this),this.startTimer=this.startTimer.bind(this)}copyToClipboard(t,e,o){const c=o?e.nextElementSibling:e,n=c.src,r=document.createElement("input"),s=document.getElementById(t),{value:i,innerText:d}=s||{},m=i||d;r.value=m,document.body.appendChild(r),r.select(),document.execCommand("copy"),document.body.removeChild(r),c.src="data:image/svg+xml;base64,"+btoa(`
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.04706 14C4.04706 8.55609 8.46025 4.1429 13.9042 4.1429C19.3482 4.1429 23.7613 8.55609 23.7613 14C23.7613 19.444 19.3482 23.8572 13.9042 23.8572C8.46025 23.8572 4.04706 19.444 4.04706 14Z" stroke="#000" stroke-width="2.19048" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9.52325 14L12.809 17.2858L18.2852 11.8096" stroke="#000" stroke-width="2.19048" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
`),setTimeout(()=>{c.src=n},5e3)}async fetchAndDisplayQRCode(t=null){try{const e=document.querySelector('meta[name="btc_address"]').content,c=encodeURIComponent(`bitcoin:${e}?amount=${t||"{{$btc_amount}}"}`),n=await fetch(`/api/v1/get-blockonomics-qr-code?qr_string=${c}`);if(!n.ok)throw new Error(`HTTP error! status: ${n.status}`);const r=await n.text();document.getElementById("qrcode-container").innerHTML=r}catch(e){console.error("Error fetching QR code:",e),document.getElementById("qrcode-container").textContent="Error loading QR code"}}async refreshBTCPrice(){const t=document.querySelector(".icon-refresh");t.classList.add("rotating"),document.getElementsByClassName("btc-value")[0].innerHTML="Refreshing...";const e=async()=>{try{const o=document.querySelector('meta[name="currency"]').content,c=await fetch(`/api/v1/get-btc-price?currency=${o}`);if(!c.ok)throw new Error("Network response was not ok");return(await c.json()).price}catch(o){console.error("There was a problem with the BTC price fetch operation:",o)}};try{const o=await e();if(o){const c=document.querySelector('meta[name="currency"]').content;document.getElementsByClassName("btc-value")[0].innerHTML="1 BTC = "+(o||"N/A")+" "+c+", updates in <span id='countdown'></span>";const n=(document.querySelector('meta[name="amount"]').content/o).toFixed(10);document.querySelector('input[name="btc_price"]').value=o,document.querySelector('input[name="btc_amount"]').value=n,document.getElementById("btc-amount").textContent=n;const r=document.querySelector('meta[name="btc_address"]').content,s=document.getElementById("qr-code-link"),i=document.getElementById("open-in-wallet-link");s.href=`bitcoin:${r}?amount=${n}`,i.href=`bitcoin:${r}?amount=${n}`,await this.fetchAndDisplayQRCode(n),this.startTimer(600)}}finally{t.classList.remove("rotating")}}handle(){window.copyToClipboard=this.copyToClipboard,window.refreshBTCPrice=this.refreshBTCPrice,window.fetchAndDisplayQRCode=this.fetchAndDisplayQRCode,window.startTimer=this.startTimer;const t=()=>{const e=document.querySelector('meta[name="btc_address"]').content,o=`wss://www.blockonomics.co/payment/${e}`,c=new WebSocket(o);c.onmessage=function(n){const r=JSON.parse(n.data),{status:s,txid:i,value:d}=r||{};console.log("Payment status:",s),(s===0||s===1||s===2)&&(document.querySelector('input[name="txid"]').value=i||"",document.querySelector('input[name="status"]').value=s||"",document.querySelector('input[name="btc_amount"]').value=d||"",document.querySelector('input[name="btc_address"]').value=e||"",document.getElementById("server-response").submit())}};startTimer(600),t(),fetchAndDisplayQRCode()}}function u(){new f().handle(),window.bootBlockonomics=u}w()?u():p("#blockonomics-payment").then(()=>u());
|
||||
@@ -99,7 +99,7 @@
|
||||
"src": "resources/js/clients/payments/authorize-credit-card-payment.js"
|
||||
},
|
||||
"resources/js/clients/payments/blockonomics.js": {
|
||||
"file": "assets/blockonomics-c3966bec.js",
|
||||
"file": "assets/blockonomics-bab011a6.js",
|
||||
"imports": [
|
||||
"_wait-8f4ae121.js"
|
||||
],
|
||||
|
||||
48
resources/js/clients/payments/blockonomics.js
vendored
48
resources/js/clients/payments/blockonomics.js
vendored
@@ -50,50 +50,14 @@ class Blockonomics {
|
||||
}
|
||||
|
||||
|
||||
async fetchAndDisplayQRCode (newBtcAmount = null) {
|
||||
try {
|
||||
const btcAddress = document.querySelector('meta[name="btc_address"]').content;
|
||||
const btcAmount = newBtcAmount || '{{$btc_amount}}';
|
||||
const qrString = encodeURIComponent(`bitcoin:${btcAddress}?amount=${btcAmount}`);
|
||||
const response = await fetch(`/api/v1/get-blockonomics-qr-code?qr_string=${qrString}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const svgText = await response.text();
|
||||
document.getElementById('qrcode-container').innerHTML = svgText;
|
||||
} catch (error) {
|
||||
console.error('Error fetching QR code:', error);
|
||||
document.getElementById('qrcode-container').textContent = 'Error loading QR code';
|
||||
}
|
||||
// QR code fetching is now handled by Livewire component (BlockonomicsQRCode)
|
||||
async fetchAndDisplayQRCode () {
|
||||
// This method is deprecated - use Livewire component instead
|
||||
};
|
||||
|
||||
startTimer = (seconds) => {
|
||||
const countDownDate = new Date().getTime() + seconds * 1000;
|
||||
document.getElementById("countdown").innerHTML = "10" + ":" + "00" + " min";
|
||||
|
||||
const updateCountdown = () => {
|
||||
const now = new Date().getTime();
|
||||
const distance = countDownDate - now;
|
||||
|
||||
const isRefreshing = document.getElementsByClassName("btc-value")[0].innerHTML.includes("Refreshing");
|
||||
if (isRefreshing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (distance < 0) {
|
||||
refreshBTCPrice();
|
||||
return;
|
||||
}
|
||||
|
||||
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
|
||||
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
|
||||
const formattedMinutes = String(minutes).padStart(2, '0');
|
||||
const formattedSeconds = String(seconds).padStart(2, '0');
|
||||
document.getElementById("countdown").innerHTML = formattedMinutes + ":" + formattedSeconds + " min";
|
||||
}
|
||||
|
||||
clearInterval(window.countdownInterval);
|
||||
window.countdownInterval = setInterval(updateCountdown, 1000);
|
||||
// Countdown timer is now handled by Livewire component (BlockonomicsPriceDisplay)
|
||||
startTimer = () => {
|
||||
// This method is deprecated - use Livewire component instead
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
<div class="btc-value-wrapper">
|
||||
<div class="btc-value">
|
||||
1 BTC = {{ $btc_price }} {{ $currency }}, updates in <span id="countdown-livewire">{{ $countdown }}</span>
|
||||
</div>
|
||||
<span class="icon-refresh {{ $is_refreshing ? 'rotating' : '' }}" wire:click="refreshBTCPrice"
|
||||
{{ $is_refreshing ? 'style="pointer-events: none;"' : '' }}></span>
|
||||
|
||||
<style>
|
||||
.btc-value-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.btc-value {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.icon-refresh {
|
||||
cursor: pointer;
|
||||
margin-left: 5px;
|
||||
width: 28px;
|
||||
display: flex;
|
||||
font-size: 32px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
|
||||
@keyframes rotating {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.rotating {
|
||||
animation: rotating 2s linear infinite;
|
||||
}
|
||||
</style>
|
||||
|
||||
@script
|
||||
<script>
|
||||
let countdownInterval = null;
|
||||
let countdownDate = null;
|
||||
|
||||
const updateCountdown = () => {
|
||||
if (!countdownDate) return;
|
||||
|
||||
const now = new Date().getTime();
|
||||
const distance = countdownDate - now;
|
||||
|
||||
if (distance <= 0) {
|
||||
clearInterval(countdownInterval);
|
||||
countdownInterval = null;
|
||||
$wire.refreshBTCPrice();
|
||||
return;
|
||||
}
|
||||
|
||||
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
|
||||
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
|
||||
|
||||
if (!isNaN(minutes) && !isNaN(seconds)) {
|
||||
const formattedMinutes = String(minutes).padStart(2, '0');
|
||||
const formattedSeconds = String(seconds).padStart(2, '0');
|
||||
|
||||
const countdownElement = document.getElementById('countdown-livewire');
|
||||
if (countdownElement) {
|
||||
countdownElement.textContent = formattedMinutes + ':' + formattedSeconds;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const startCountdownTimer = ({ duration }) => {
|
||||
clearInterval(countdownInterval);
|
||||
countdownInterval = null;
|
||||
countdownDate = new Date().getTime() + duration * 1000;
|
||||
updateCountdown();
|
||||
countdownInterval = setInterval(updateCountdown, 1000);
|
||||
};
|
||||
|
||||
$wire.on('start-countdown', startCountdownTimer);
|
||||
|
||||
// Listen for updates after price refresh
|
||||
$wire.on('btc-price-updated', () => {
|
||||
startCountdownTimer({ duration: 600 });
|
||||
});
|
||||
|
||||
// Initialize countdown on component mount with a small delay to ensure DOM is ready
|
||||
setTimeout(() => {
|
||||
startCountdownTimer({ duration: 600 });
|
||||
}, 100);
|
||||
</script>
|
||||
@endscript
|
||||
</div>
|
||||
@@ -0,0 +1,41 @@
|
||||
<div class="qrcode-wrapper">
|
||||
@if ($error_message)
|
||||
<div class="error-message">{{ $error_message }}</div>
|
||||
@elseif ($is_loading)
|
||||
<div class="loading-message">Loading QR code...</div>
|
||||
@else
|
||||
<a href="bitcoin:{{ $btc_address }}?amount={{ $btc_amount }}" id="qr-code-link" target="_blank">
|
||||
<div id="qrcode-container">
|
||||
{!! $qr_code_svg !!}
|
||||
</div>
|
||||
</a>
|
||||
@endif
|
||||
|
||||
<style>
|
||||
.qrcode-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: #d32f2f;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.loading-message {
|
||||
color: #666;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#qrcode-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
@@ -20,9 +20,10 @@
|
||||
<div class="scan-section">
|
||||
<div class="title">Scan</div>
|
||||
<span class="input-wrapper">
|
||||
<a href="bitcoin:{{$btc_address}}?amount={{$btc_amount}}" id="qr-code-link" target="_blank">
|
||||
<div id="qrcode-container"></div>
|
||||
</a>
|
||||
<livewire:billing-portal.payments.blockonomics-qr-code
|
||||
:btc_address="$btc_address"
|
||||
:btc_amount="$btc_amount"
|
||||
/>
|
||||
</span>
|
||||
<a href="bitcoin:{{$btc_address}}?amount={{$btc_amount}}" target="_blank" id="open-in-wallet-link">Open in Wallet</a>
|
||||
</div>
|
||||
@@ -40,10 +41,11 @@
|
||||
</div>
|
||||
<img onclick='copyToClipboard("btc-amount", this)' src="{{ 'data:image/svg+xml;base64,' . base64_encode('<svg width="22" height="24" viewBox="0 0 22 24" fill="none" xmlns="http://www.w3.org/2000/svg" ><path d="M15.5 1H3.5C2.4 1 1.5 1.9 1.5 3V17H3.5V3H15.5V1ZM18.5 5H7.5C6.4 5 5.5 5.9 5.5 7V21C5.5 22.1 6.4 23 7.5 23H18.5C19.6 23 20.5 22.1 20.5 21V7C20.5 5.9 19.6 5 18.5 5ZM18.5 21H7.5V7H18.5V21Z" fill="#000"/></svg>') }}" class="icon" alt="Copy Icon">
|
||||
</span>
|
||||
<div class="btc-value-wrapper">
|
||||
<div class="btc-value">1 BTC = {{$btc_price}} {{$currency}}, updates in <span id="countdown"></span></div>
|
||||
<span class="icon-refresh" onclick='refreshBTCPrice()'></span>
|
||||
</div>
|
||||
<livewire:billing-portal.payments.blockonomics-price-display
|
||||
:currency="$currency"
|
||||
:btc_price="$btc_price"
|
||||
:btc_amount="$btc_amount"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,9 +19,10 @@
|
||||
<div class="scan-section">
|
||||
<div class="title">Scan</div>
|
||||
<span class="input-wrapper">
|
||||
<a href="bitcoin:{{$btc_address}}?amount={{$btc_amount}}" id="qr-code-link" target="_blank">
|
||||
<div id="qrcode-container"></div>
|
||||
</a>
|
||||
<livewire:billing-portal.payments.blockonomics-qr-code
|
||||
:btc_address="$btc_address"
|
||||
:btc_amount="$btc_amount"
|
||||
/>
|
||||
</span>
|
||||
<a href="bitcoin:{{$btc_address}}?amount={{$btc_amount}}" target="_blank" id="open-in-wallet-link">Open in Wallet</a>
|
||||
</div>
|
||||
@@ -39,10 +40,11 @@
|
||||
</div>
|
||||
<img onclick='copyToClipboard("btc-amount", this)' src="{{ 'data:image/svg+xml;base64,' . base64_encode('<svg width="22" height="24" viewBox="0 0 22 24" fill="none" xmlns="http://www.w3.org/2000/svg" ><path d="M15.5 1H3.5C2.4 1 1.5 1.9 1.5 3V17H3.5V3H15.5V1ZM18.5 5H7.5C6.4 5 5.5 5.9 5.5 7V21C5.5 22.1 6.4 23 7.5 23H18.5C19.6 23 20.5 22.1 20.5 21V7C20.5 5.9 19.6 5 18.5 5ZM18.5 21H7.5V7H18.5V21Z" fill="#000"/></svg>') }}" class="icon" alt="Copy Icon">
|
||||
</span>
|
||||
<div class="btc-value-wrapper">
|
||||
<div class="btc-value">1 BTC = {{$btc_price}} {{$currency}}, updates in <span id="countdown"></span></div>
|
||||
<span class="icon-refresh" onclick='refreshBTCPrice()'></span>
|
||||
</div>
|
||||
<livewire:billing-portal.payments.blockonomics-price-display
|
||||
:currency="$currency"
|
||||
:btc_price="$btc_price"
|
||||
:btc_amount="$btc_amount"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user