Merge pull request #10975 from cnohall/improve-blockonomics-callback-logic

Improve blockonomics callback logic
This commit is contained in:
David Bomba 2025-06-05 15:33:46 +10:00 committed by GitHub
commit f5dc292ef5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 76 additions and 32 deletions

View File

@ -29,6 +29,7 @@ use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
class Blockonomics implements LivewireMethodInterface class Blockonomics implements LivewireMethodInterface
{ {
use MakesHash; use MakesHash;
private string $test_txid = 'WarningThisIsAGeneratedTestPaymentAndNotARealBitcoinTransaction';
public function __construct(public BlockonomicsPaymentDriver $blockonomics) public function __construct(public BlockonomicsPaymentDriver $blockonomics)
{ {
@ -124,6 +125,7 @@ class Blockonomics implements LivewireMethodInterface
return render('gateways.blockonomics.pay', $data); return render('gateways.blockonomics.pay', $data);
} }
public function paymentResponse(PaymentResponseRequest $request) public function paymentResponse(PaymentResponseRequest $request)
{ {
$request->validate([ $request->validate([
@ -132,20 +134,50 @@ class Blockonomics implements LivewireMethodInterface
'currency' => ['required'], 'currency' => ['required'],
'txid' => ['required'], 'txid' => ['required'],
'payment_method_id' => ['required'], 'payment_method_id' => ['required'],
'btc_address' => ['required'],
'btc_amount' => ['required'],
'btc_price' => ['required'],
// Setting status to required will break the payment process
// because sometimes the status is returned as 0 which is falsy
// and the validation will fail.
// 'status' => ['required'],
]); ]);
try { try {
$data = []; $data = [];
$fiat_amount = round(($request->btc_price * $request->btc_amount), 2); $fiat_amount = round(($request->btc_price * $request->btc_amount / 100000000), 2);
$data['amount'] = $fiat_amount; $data['amount'] = $fiat_amount;
$data['currency'] = $request->currency;
$data['payment_method_id'] = $request->payment_method_id; $data['payment_method_id'] = $request->payment_method_id;
$data['payment_type'] = PaymentType::CRYPTO; $data['payment_type'] = PaymentType::CRYPTO;
$data['gateway_type_id'] = GatewayType::CRYPTO; $data['gateway_type_id'] = GatewayType::CRYPTO;
$data['transaction_reference'] = $request->txid;
$statusId = Payment::STATUS_PENDING; // Append a random value to the transaction reference for test payments
// to prevent duplicate entries in the database.
// This ensures the payment hashed_id remains unique.
$testTxid = $this->test_txid;
$data['transaction_reference'] = ($request->txid === $testTxid)
? $request->txid . bin2hex(random_bytes(16))
: $request->txid;
$statusId;
switch ($request->status) {
case 0:
$statusId = Payment::STATUS_PENDING;
break;
case 1:
$statusId = Payment::STATUS_PENDING;
break;
case 2:
$statusId = Payment::STATUS_COMPLETED;
break;
default:
$statusId = Payment::STATUS_PENDING;
}
$payment = $this->blockonomics->createPayment($data, $statusId); $payment = $this->blockonomics->createPayment($data, $statusId);
$payment->private_notes = "{$request->btc_address} - {$request->btc_amount}";
$payment->save();
SystemLogger::dispatch( SystemLogger::dispatch(
['response' => $payment, 'data' => $data], ['response' => $payment, 'data' => $data],

View File

@ -50,6 +50,7 @@ class BlockonomicsPaymentDriver extends BaseDriver
public $NEW_ADDRESS_URL = 'https://www.blockonomics.co/api/new_address'; public $NEW_ADDRESS_URL = 'https://www.blockonomics.co/api/new_address';
public $PRICE_URL = 'https://www.blockonomics.co/api/price'; public $PRICE_URL = 'https://www.blockonomics.co/api/price';
public $STORES_URL = 'https://www.blockonomics.co/api/v2/stores'; public $STORES_URL = 'https://www.blockonomics.co/api/v2/stores';
private string $test_txid = 'WarningThisIsAGeneratedTestPaymentAndNotARealBitcoinTransaction';
public function init() public function init()
{ {
@ -106,17 +107,22 @@ class BlockonomicsPaymentDriver extends BaseDriver
$status = $request->status; $status = $request->status;
$addr = $request->addr; $addr = $request->addr;
$payment = Payment::query() if ($txid === $this->test_txid) {
->where('company_id', $company->id) $payment = Payment::query()
->where('transaction_reference', $txid) ->where('company_id', $company->id)
->firstOrFail(); ->where('private_notes', "$addr - $value")
->firstOrFail();
if (!$payment) { } else {
return response()->json([], 200); $payment = Payment::query()
// TODO: Implement logic to create new payment in case user sends payment to the address after closing the payment page ->where('company_id', $company->id)
->where('transaction_reference', $txid)
->firstOrFail();
} }
$statusId = Payment::STATUS_PENDING; // Already completed payment, no need to update status
if ($payment->status_id == Payment::STATUS_COMPLETED) {
return response()->json([], 200);
}
switch ($status) { switch ($status) {
case 0: case 0:
@ -128,16 +134,16 @@ class BlockonomicsPaymentDriver extends BaseDriver
case 2: case 2:
$statusId = Payment::STATUS_COMPLETED; $statusId = Payment::STATUS_COMPLETED;
break; break;
default:
$statusId = Payment::STATUS_PENDING;
} }
if ($payment->status_id == $statusId) { if ($payment->status_id !== $statusId) {
return response()->json([], 200);
} else {
$payment->status_id = $statusId; $payment->status_id = $statusId;
$payment->save(); $payment->save();
return response()->json([], 200);
} }
return response()->json([], 200);
} }

View File

@ -161,14 +161,18 @@ class Blockonomics {
ws.onmessage = function (event) { ws.onmessage = function (event) {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
console.log('Payment status:', data.status); const { status, txid, value } = data || {};
const isPaymentUnconfirmed = data.status === 0; console.log('Payment status:', status);
const isPaymentPartiallyConfirmed = data.status === 1; const isPaymentUnconfirmed = status === 0;
const isPaymentConfirmed = data.status === 2; const isPaymentPartiallyConfirmed = status === 1;
const isPaymentConfirmed = status === 2;
// Confirmation status: 0 = unconfirmed, 1 = partially confirmed, 2 = confirmed // Confirmation status: 0 = unconfirmed, 1 = partially confirmed, 2 = confirmed
// If any of the statuses are true, submit the form and redirect // If any of the statuses are true, submit the form and redirect
if (isPaymentUnconfirmed || isPaymentPartiallyConfirmed || isPaymentConfirmed) { if (isPaymentUnconfirmed || isPaymentPartiallyConfirmed || isPaymentConfirmed) {
document.querySelector('input[name="txid"]').value = data.txid || ''; document.querySelector('input[name="txid"]').value = txid || '';
document.querySelector('input[name="status"]').value = status || '';
document.querySelector('input[name="btc_amount"]').value = value || '';
document.querySelector('input[name="btc_address"]').value = btcAddress || '';
document.getElementById('server-response').submit(); document.getElementById('server-response').submit();
} }
} }

View File

@ -60,6 +60,7 @@
<input type="hidden" name="currency" value="{{ $currency }}"> <input type="hidden" name="currency" value="{{ $currency }}">
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}"> <input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
<input type="hidden" name="txid" value=""> <input type="hidden" name="txid" value="">
<input type="hidden" name="btc_address" value="{{ $btc_address }}" />
</form> </form>

View File

@ -1,5 +1,4 @@
<div class="rounded-lg border bg-card text-card-foreground shadow-sm overflow-hidden py-5 bg-white sm:gap-4" id="blockonomics-payment"> <div class="rounded-lg border bg-card text-card-foreground shadow-sm overflow-hidden py-5 bg-white sm:gap-4" id="blockonomics-payment">
<meta name="amount" content="{{ $amount }}" /> <meta name="amount" content="{{ $amount }}" />
<meta name="btc_amount" content="{{ $btc_amount }}" /> <meta name="btc_amount" content="{{ $btc_amount }}" />
<meta name="btc_address" content="{{ $btc_address }}" /> <meta name="btc_address" content="{{ $btc_address }}" />
@ -58,6 +57,8 @@
<input type="hidden" name="amount" value="{{ $amount }}"> <input type="hidden" name="amount" value="{{ $amount }}">
<input type="hidden" name="btc_price" value="{{ $btc_price }}"> <input type="hidden" name="btc_price" value="{{ $btc_price }}">
<input type="hidden" name="btc_amount" value="{{ $btc_amount }}"> <input type="hidden" name="btc_amount" value="{{ $btc_amount }}">
<input type="hidden" name="btc_address" value="{{ $btc_address }}">
<input type="hidden" name="status" value="">
<input type="hidden" name="currency" value="{{ $currency }}"> <input type="hidden" name="currency" value="{{ $currency }}">
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}"> <input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
<input type="hidden" name="txid" value=""> <input type="hidden" name="txid" value="">