Reapply "Add Blockonomics payment capabilities"
This reverts commit 0775299548.
This commit is contained in:
parent
3b1d033a64
commit
f4d0d49e97
|
|
@ -0,0 +1,58 @@
|
|||
<?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\Http\Controllers\Gateways;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request; // Import the Request class
|
||||
use Illuminate\Support\Facades\Http; // Import the Http facade
|
||||
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
|
||||
use BaconQrCode\Renderer\ImageRenderer;
|
||||
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
|
||||
use BaconQrCode\Writer;
|
||||
|
||||
class BlockonomicsController extends Controller
|
||||
{
|
||||
public function getBTCPrice(Request $request)
|
||||
{
|
||||
$currency = $request->query('currency');
|
||||
$response = Http::get("https://www.blockonomics.co/api/price?currency={$currency}");
|
||||
|
||||
if ($response->successful()) {
|
||||
return response()->json(['price' => $response->json('price')]);
|
||||
}
|
||||
|
||||
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,173 @@
|
|||
<?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\PaymentDrivers\Blockonomics;
|
||||
|
||||
use App\Models\Payment;
|
||||
use App\Models\SystemLog;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\PaymentType;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\PaymentDrivers\Common\MethodInterface;
|
||||
use App\PaymentDrivers\BlockonomicsPaymentDriver;
|
||||
use App\PaymentDrivers\Common\LivewireMethodInterface;
|
||||
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
|
||||
|
||||
class Blockonomics implements LivewireMethodInterface
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public function __construct(public BlockonomicsPaymentDriver $blockonomics)
|
||||
{
|
||||
}
|
||||
|
||||
public function authorizeView($data)
|
||||
{
|
||||
}
|
||||
|
||||
public function authorizeRequest($request)
|
||||
{
|
||||
}
|
||||
|
||||
public function authorizeResponse($request)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public function getBTCAddress(): array
|
||||
{
|
||||
$api_key = $this->blockonomics->company_gateway->getConfigField('apiKey');
|
||||
|
||||
if (!$api_key) {
|
||||
return ['success' => false, 'message' => 'Please enter a valid API key'];
|
||||
}
|
||||
|
||||
// $params = config('ninja.environment') == 'development' ? '?reset=1' : '';
|
||||
$url = 'https://www.blockonomics.co/api/new_address';
|
||||
|
||||
$response = Http::withToken($api_key)
|
||||
->post($url, []);
|
||||
|
||||
nlog($response->body());
|
||||
|
||||
if ($response->status() == 401) {
|
||||
return ['success' => false, 'message' => 'API Key is incorrect'];
|
||||
};
|
||||
|
||||
if ($response->successful()) {
|
||||
if (isset($response->object()->address)) {
|
||||
return ['success' => true, 'address' => $response->object()->address];
|
||||
} else {
|
||||
return ['success' => false, 'message' => 'Address not returned'];
|
||||
}
|
||||
} else {
|
||||
return ['success' => false, 'message' => "Could not generate new address (This may be a temporary error. Please try again). \n\n<br><br> If this continues, please ask website administrator to check blockonomics registered email address for error messages"];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function getBTCPrice()
|
||||
{
|
||||
|
||||
$r = Http::get('https://www.blockonomics.co/api/price', ['currency' => $this->blockonomics->client->getCurrencyCode()]);
|
||||
|
||||
return $r->successful() ? $r->object()->price : 'Something went wrong';
|
||||
|
||||
}
|
||||
|
||||
public function paymentData(array $data): array
|
||||
{
|
||||
|
||||
$btc_price = $this->getBTCPrice();
|
||||
$btc_address = $this->getBTCAddress();
|
||||
$data['error'] = null;
|
||||
if (!$btc_address['success']) {
|
||||
$data['error'] = $btc_address['message'];
|
||||
}
|
||||
$fiat_amount = $data['total']['amount_with_fee'];
|
||||
$btc_amount = $fiat_amount / $btc_price;
|
||||
$_invoice = collect($this->blockonomics->payment_hash->data->invoices)->first();
|
||||
$data['gateway'] = $this->blockonomics;
|
||||
$data['company_gateway_id'] = $this->blockonomics->getCompanyGatewayId();
|
||||
$data['amount'] = $fiat_amount;
|
||||
$data['currency'] = $this->blockonomics->client->getCurrencyCode();
|
||||
$data['btc_amount'] = number_format($btc_amount, 10, '.', '');
|
||||
$data['btc_address'] = $btc_address['address'] ?? '';
|
||||
$data['btc_price'] = $btc_price;
|
||||
$data['invoice_number'] = $_invoice->invoice_number;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function livewirePaymentView(array $data): string
|
||||
{
|
||||
return 'gateways.blockonomics.pay_livewire';
|
||||
}
|
||||
|
||||
public function paymentView($data)
|
||||
{
|
||||
$data = $this->paymentData($data);
|
||||
|
||||
return render('gateways.blockonomics.pay', $data);
|
||||
}
|
||||
|
||||
public function paymentResponse(PaymentResponseRequest $request)
|
||||
{
|
||||
$request->validate([
|
||||
'payment_hash' => ['required'],
|
||||
'amount' => ['required'],
|
||||
'currency' => ['required'],
|
||||
'txid' => ['required'],
|
||||
'payment_method_id' => ['required'],
|
||||
]);
|
||||
|
||||
try {
|
||||
$data = [];
|
||||
$fiat_amount = round(($request->btc_price * $request->btc_amount), 2);
|
||||
$data['amount'] = $fiat_amount;
|
||||
$data['currency'] = $request->currency;
|
||||
$data['payment_method_id'] = $request->payment_method_id;
|
||||
$data['payment_type'] = PaymentType::CRYPTO;
|
||||
$data['gateway_type_id'] = GatewayType::CRYPTO;
|
||||
$data['transaction_reference'] = $request->txid;
|
||||
|
||||
$statusId = Payment::STATUS_PENDING;
|
||||
$payment = $this->blockonomics->createPayment($data, $statusId);
|
||||
|
||||
SystemLogger::dispatch(
|
||||
['response' => $payment, 'data' => $data],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||
SystemLog::TYPE_BLOCKONOMICS,
|
||||
$this->blockonomics->client,
|
||||
$this->blockonomics->client->company,
|
||||
);
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
$blockonomics = $this->blockonomics;
|
||||
PaymentFailureMailer::dispatch($blockonomics->client, $blockonomics->payment_hash->data, $blockonomics->client->company, $request->amount);
|
||||
throw new PaymentFailed('Error during Blockonomics payment : ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Not supported yet
|
||||
public function refund(Payment $payment, $amount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
<?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://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Models\Gateway;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\SystemLog;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\PaymentType;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use App\PaymentDrivers\Blockonomics\Blockonomics;
|
||||
use App\Http\Requests\Payments\PaymentWebhookRequest;
|
||||
|
||||
class BlockonomicsPaymentDriver extends BaseDriver
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public $refundable = false; //does this gateway support refunds?
|
||||
|
||||
public $token_billing = false; //does this gateway support token billing?
|
||||
|
||||
public $can_authorise_credit_card = false; //does this gateway support authorizations?
|
||||
|
||||
public $gateway; //initialized gateway
|
||||
|
||||
public $payment_method; //initialized payment method
|
||||
|
||||
public static $methods = [
|
||||
GatewayType::CRYPTO => Blockonomics::class, //maps GatewayType => Implementation class
|
||||
];
|
||||
|
||||
public const SYSTEM_LOG_TYPE = SystemLog::TYPE_BLOCKONOMICS; //define a constant for your gateway ie TYPE_YOUR_CUSTOM_GATEWAY - set the const in the SystemLog model
|
||||
|
||||
public $BASE_URL = 'https://www.blockonomics.co';
|
||||
public $NEW_ADDRESS_URL = 'https://www.blockonomics.co/api/new_address';
|
||||
public $PRICE_URL = 'https://www.blockonomics.co/api/price';
|
||||
public $STORES_URL = 'https://www.blockonomics.co/api/v2/stores';
|
||||
|
||||
public function init()
|
||||
{
|
||||
return $this; /* This is where you boot the gateway with your auth credentials*/
|
||||
}
|
||||
|
||||
/* Returns an array of gateway types for the payment gateway */
|
||||
public function gatewayTypes(): array
|
||||
{
|
||||
$types = [];
|
||||
|
||||
$types[] = GatewayType::CRYPTO;
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
public function setPaymentMethod($payment_method_id)
|
||||
{
|
||||
$class = self::$methods[$payment_method_id];
|
||||
$this->payment_method = new $class($this);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function processPaymentView(array $data)
|
||||
{
|
||||
$this->init();
|
||||
|
||||
return $this->payment_method->paymentView($data); //this is your custom implementation from here
|
||||
}
|
||||
|
||||
public function processPaymentResponse($request)
|
||||
{
|
||||
|
||||
$this->init();
|
||||
|
||||
return $this->payment_method->paymentResponse($request);
|
||||
}
|
||||
|
||||
public function processWebhookRequest(PaymentWebhookRequest $request)
|
||||
{
|
||||
|
||||
$company = $request->getCompany();
|
||||
|
||||
// Re-introduce secret in a later stage if needed.
|
||||
// $url_callback_secret = $request->secret;
|
||||
// $db_callback_secret = $this->company_gateway->getConfigField('callbackSecret');
|
||||
|
||||
// if ($url_callback_secret != $db_callback_secret) {
|
||||
// throw new PaymentFailed('Secret does not match');
|
||||
// }
|
||||
|
||||
$txid = $request->txid;
|
||||
$value = $request->value;
|
||||
$status = $request->status;
|
||||
$addr = $request->addr;
|
||||
|
||||
$payment = Payment::query()
|
||||
->where('company_id', $company->id)
|
||||
->where('transaction_reference', $txid)
|
||||
->firstOrFail();
|
||||
|
||||
if (!$payment) {
|
||||
return response()->json([], 200);
|
||||
// TODO: Implement logic to create new payment in case user sends payment to the address after closing the payment page
|
||||
}
|
||||
|
||||
$statusId = Payment::STATUS_PENDING;
|
||||
|
||||
switch ($status) {
|
||||
case 0:
|
||||
$statusId = Payment::STATUS_PENDING;
|
||||
break;
|
||||
case 1:
|
||||
$statusId = Payment::STATUS_PENDING;
|
||||
break;
|
||||
case 2:
|
||||
$statusId = Payment::STATUS_COMPLETED;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($payment->status_id == $statusId) {
|
||||
return response()->json([], 200);
|
||||
} else {
|
||||
$payment->status_id = $statusId;
|
||||
$payment->save();
|
||||
|
||||
return response()->json([], 200);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function refund(Payment $payment, $amount, $return_client_response = false)
|
||||
{
|
||||
$this->setPaymentMethod(GatewayType::CRYPTO);
|
||||
return $this->payment_method->refund($payment, $amount); //this is your custom implementation from here
|
||||
}
|
||||
|
||||
public function testNewAddressGen($crypto = 'btc', $response): string
|
||||
{
|
||||
$api_key = $this->company_gateway->getConfigField('apiKey');
|
||||
$new_address_reset_url = $this->NEW_ADDRESS_URL . '?reset=1';
|
||||
$new_address_response = Http::withToken($api_key)
|
||||
->post($new_address_reset_url, []);
|
||||
if ($new_address_response->response_code != 200) {
|
||||
return isset($new_address_response->response_message) && $new_address_response->response_message
|
||||
? $new_address_response->response_message
|
||||
: 'Could not generate new address';
|
||||
}
|
||||
|
||||
if (empty($new_address_response->address)) {
|
||||
return 'No address returned from Blockonomics API';
|
||||
}
|
||||
|
||||
return 'ok';
|
||||
}
|
||||
|
||||
public function checkStores($stores): string
|
||||
{
|
||||
if (empty($stores['data'])) {
|
||||
return "Please add a store to your Blockonomics' account";
|
||||
}
|
||||
|
||||
$invoice_ninja_callback_url = $this->company_gateway->webhookUrl();
|
||||
|
||||
$matching_store = null;
|
||||
$store_without_callback = null;
|
||||
$partial_match_store = null;
|
||||
|
||||
foreach ($stores['data'] as $store) {
|
||||
if ($store['http_callback'] === $invoice_ninja_callback_url) {
|
||||
$matching_store = $store;
|
||||
break;
|
||||
}
|
||||
if (empty($store['http_callback'])) {
|
||||
$store_without_callback = $store;
|
||||
continue;
|
||||
}
|
||||
// Check for partial match - only secret or protocol differs
|
||||
// TODO: Implement logic for updating partial matches
|
||||
$store_base_url = preg_replace('/https?:\/\//', '', $store['http_callback']);
|
||||
if (strpos($store_base_url, $invoice_ninja_callback_url) === 0) {
|
||||
$partial_match_store = $store;
|
||||
}
|
||||
}
|
||||
|
||||
if ($matching_store) {
|
||||
$matching_store_wallet = $matching_store['wallets'];
|
||||
if (empty($matching_store_wallet)) {
|
||||
return 'Please add a wallet to your Blockonomics store';
|
||||
}
|
||||
return 'ok';
|
||||
}
|
||||
return "No callback URL from your Blockonomics stores matches your Invoice Ninja webhook";
|
||||
}
|
||||
|
||||
public function auth(): string
|
||||
{
|
||||
try {
|
||||
$api_key = $this->company_gateway->getConfigField('apiKey');
|
||||
|
||||
if(!$api_key) {
|
||||
return 'No API Key';
|
||||
}
|
||||
$get_stores_response = Http::withToken($api_key)
|
||||
->get($this->STORES_URL, ['wallets' => 'true']);
|
||||
$get_stores_response_status = $get_stores_response->status();
|
||||
|
||||
if($get_stores_response_status == 401) {
|
||||
return 'API Key is incorrect';
|
||||
}
|
||||
|
||||
|
||||
if (!$get_stores_response || $get_stores_response_status !== 200) {
|
||||
return 'Could not connect to Blockonomics API';
|
||||
}
|
||||
|
||||
$stores = $get_stores_response->json();
|
||||
$stores_check_result = $this->checkStores($stores);
|
||||
|
||||
return $stores_check_result;
|
||||
} catch (\Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use App\Models\Gateway;
|
||||
use App\Models\GatewayType;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
if(!Gateway::find(65))
|
||||
{
|
||||
|
||||
$fields = new \stdClass;
|
||||
$fields->apiKey = "";
|
||||
$fields->callbackSecret = "";
|
||||
|
||||
$gateway = new Gateway;
|
||||
$gateway->id = 65;
|
||||
$gateway->name = 'Blockonomics';
|
||||
$gateway->key = 'wbhf02us6owgo7p4nfjd0ymssdshks4d';
|
||||
$gateway->provider = 'Blockonomics';
|
||||
$gateway->is_offsite = false;
|
||||
$gateway->fields = \json_encode($fields);
|
||||
|
||||
|
||||
$gateway->visible = true;
|
||||
$gateway->site_url = 'https://blockonomics.co';
|
||||
$gateway->default_gateway_type_id = GatewayType::CRYPTO;
|
||||
$gateway->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,189 @@
|
|||
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_Crypto'), 'card_title' => ctrans('texts.payment_type_Crypto')])
|
||||
|
||||
@section('gateway_head')
|
||||
<meta name="instant-payment" content="yes" />
|
||||
<meta name="amount" content="{{ $amount }}" />
|
||||
<meta name="btc_amount" content="{{ $btc_amount }}" />
|
||||
<meta name="btc_address" content="{{ $btc_address }}" />
|
||||
<meta name="currency" content="{{ $currency }}" />
|
||||
@endsection
|
||||
|
||||
@section('gateway_content')
|
||||
<div class="alert alert-failure mb-4" hidden id="errors"></div>
|
||||
<div class="blockonomics-payment-wrapper">
|
||||
<div class="initial-state">
|
||||
<div class="invoice-info-wrapper">
|
||||
<div class="invoice-number">Invoice #{{$invoice_number}}</div>
|
||||
<div class="invoice-amount">{{$amount}} {{$currency}}</div>
|
||||
</div>
|
||||
<div class="sections-wrapper">
|
||||
<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>
|
||||
</span>
|
||||
<a href="bitcoin:{{$btc_address}}?amount={{$btc_amount}}" target="_blank" id="open-in-wallet-link">Open in Wallet</a>
|
||||
</div>
|
||||
<div class="copy-section">
|
||||
<div class="title">Copy</div>
|
||||
<span>To pay, send bitcoin to this address:</span>
|
||||
<span class="input-wrapper">
|
||||
<input onclick='copyToClipboard("btc-address", this, true)' class="full-width-input" id="btc-address" value="{{$btc_address}}" readonly>
|
||||
<img onclick='copyToClipboard("btc-address", 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>
|
||||
<span>Amount of bitcoin (BTC) to send:</span>
|
||||
<span class="input-wrapper">
|
||||
<div class="full-width-input" id="btc-amount" onclick='copyToClipboard("btc-amount", this, true)'>
|
||||
{{$btc_amount}}
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('client.payments.response') }}" method="post" id="server-response">
|
||||
@csrf
|
||||
<input type="hidden" name="gateway_response">
|
||||
<input type="hidden" name="company_gateway_id" value="{{ $company_gateway_id }}">
|
||||
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}">
|
||||
<input type="hidden" name="token">
|
||||
<input type="hidden" name="amount" value="{{ $amount }}">
|
||||
<input type="hidden" name="btc_price" value="{{ $btc_price }}">
|
||||
<input type="hidden" name="btc_amount" value="{{ $btc_amount }}">
|
||||
<input type="hidden" name="currency" value="{{ $currency }}">
|
||||
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
|
||||
<input type="hidden" name="txid" value="">
|
||||
</form>
|
||||
|
||||
|
||||
<style type="text/css">
|
||||
.sections-wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
/* Mobile devices */
|
||||
@media (max-width: 768px) {
|
||||
flex-direction: column; /* Change to column on smaller screens */
|
||||
}
|
||||
}
|
||||
.copy-section {
|
||||
width: 60%;
|
||||
@media (max-width: 768px) {
|
||||
width: 100%; /* Full width on smaller screens */
|
||||
}
|
||||
}
|
||||
.title {
|
||||
font-size: 17px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
#open-in-wallet-link {
|
||||
text-align: center;
|
||||
text-decoration: underline;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
.invoice-info-wrapper {
|
||||
width: 100%;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.invoice-number {
|
||||
width: 50%;
|
||||
float: left;
|
||||
text-align: left;
|
||||
}
|
||||
.invoice-amount {
|
||||
width: 50%;
|
||||
float: right;
|
||||
text-align: right;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.blockonomics-payment-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
.initial-state {
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
padding: 24px;
|
||||
}
|
||||
.input-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.full-width-input {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
color: #444;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
.icon {
|
||||
cursor: pointer;
|
||||
width: 28px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.icon-refresh::before {
|
||||
content: '\27F3';
|
||||
cursor: pointer;
|
||||
margin-left: 5px;
|
||||
width: 28px;
|
||||
display: flex;
|
||||
font-size: 32px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.btc-value {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
.btc-value-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
}
|
||||
@keyframes rotating {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
.rotating {
|
||||
animation: rotating 2s linear infinite;
|
||||
}
|
||||
</style>
|
||||
@endsection
|
||||
|
||||
@section('gateway_footer')
|
||||
@vite('resources/js/clients/payments/blockonomics.js')
|
||||
@endsection
|
||||
|
|
@ -103,6 +103,7 @@ use App\Http\Controllers\Reports\ClientReportController;
|
|||
use App\Http\Controllers\Reports\CreditReportController;
|
||||
use App\Http\Controllers\Reports\ReportExportController;
|
||||
use App\Http\Controllers\Reports\VendorReportController;
|
||||
use App\Http\Controllers\Gateways\BlockonomicsController;
|
||||
use App\Http\Controllers\Reports\ExpenseReportController;
|
||||
use App\Http\Controllers\Reports\InvoiceReportController;
|
||||
use App\Http\Controllers\Reports\PaymentReportController;
|
||||
|
|
@ -491,6 +492,8 @@ Route::post('api/v1/yodlee/balance', [YodleeController::class, 'balanceWebhook']
|
|||
|
||||
Route::get('api/v1/protected_download/{hash}', [ProtectedDownloadController::class, 'index'])->name('protected_download')->middleware('throttle:300,1');
|
||||
Route::post('api/v1/ppcp/webhook', [PayPalPPCPPaymentDriver::class, 'processWebhookRequest'])->middleware('throttle:1000,1');
|
||||
Route::get('api/v1/get-btc-price', [BlockonomicsController::class, 'getBTCPrice'])->middleware('throttle:1000,1');
|
||||
Route::get('api/v1/get-blockonomics-qr-code', [BlockonomicsController::class, 'getQRCode'])->middleware('throttle:1000,1');
|
||||
|
||||
Route::get('quickbooks/authorize/{token}', [ImportQuickbooksController::class, 'authorizeQuickbooks'])->name('quickbooks.authorize');
|
||||
Route::get('quickbooks/authorized', [ImportQuickbooksController::class, 'onAuthorized'])->name('quickbooks.authorized');
|
||||
|
|
|
|||
|
|
@ -5622,5 +5622,23 @@
|
|||
"GET",
|
||||
"HEAD"
|
||||
]
|
||||
},
|
||||
"generated::Xy7ZkLm8NpQ4Rt5V": {
|
||||
"name": "generated::Xy7ZkLm8NpQ4Rt5V",
|
||||
"domain": null,
|
||||
"action": "App\\Http\\Controllers\\BlockonomicsController@getBTCPrice",
|
||||
"uri": "api/v1/get-btc-price",
|
||||
"method": [
|
||||
"GET"
|
||||
]
|
||||
},
|
||||
"generated::Jk2MnOp3QrS6Tu9W": {
|
||||
"name": "generated::Jk2MnOp3QrS6Tu9W",
|
||||
"domain": null,
|
||||
"action": "App\\Http\\Controllers\\BlockonomicsController@getQRCode",
|
||||
"uri": "api/v1/get-blockonomics-qr-code",
|
||||
"method": [
|
||||
"GET"
|
||||
]
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue