/** * Execute the console command. * * @return mixed */ public function handle() { $request = Request::create('/api/blocknotify', 'GET', ['key' => Mint\Settings::getVal('api_key'), 'blockhash' => $this->argument('blockhash')]); Request::replace($request->input()); $response = Route::dispatch($request)->getContent(); if ($this->option('debug')) { $this->info($response); } }
/** * Execute the console command. * * @return mixed */ public function handle() { $pid = (int) getmypid(); $txid = $this->argument('txid'); Log::info('ApiCallback(txid: ' . $txid . ', pid:' . $pid . '): Running...'); /* Acquire lock to prevent double execution */ $fp = fopen(storage_path('locks/mint-' . $txid . '.lock'), 'w'); if (!flock($fp, LOCK_EX | LOCK_NB)) { Log::error('ApiCallback(txid: ' . $txid . ', pid:' . $pid . '): Task is already running! Halting...'); exit; } $request = Request::create('/api/callback', 'GET', ['key' => Mint\Settings::getVal('api_key'), 'txid' => $this->argument('txid')]); Request::replace($request->input()); $response = Route::dispatch($request)->getContent(); if ($this->option('debug')) { $this->info($response); } /* Release lock after finishing execution */ flock($fp, LOCK_UN); fclose($fp); unlink(storage_path('locks/mint-' . $txid . '.lock')); }
/** * Send from address or address account * * @return mixed */ private function sendFrom($from_address, $to_address, $note = '', Converter $amount, Converter $network_fee) { $from_address_model = Mint\Address::getAddress($from_address); $is_internal = $this->isUserAddress($to_address); $merchant_fee = Converter::btc(Mint\Settings::getVal('merchant_fee')); /* Calculate real amount to be deducted from users account. */ $full_amount = !$is_internal ? Converter::satoshi($this->bcsum($amount->satoshi, $network_fee->satoshi, $merchant_fee->satoshi)) : $amount; /* If it's users address check balance without fee */ if ($from_address_model->balance < $full_amount->satoshi) { throw new JsonException(trans('error.nofunds')); } if (!$is_internal) { /* Set transaction fee and deduct it from the payment amount */ $this->bitcoin_core->settxfee((double) $network_fee->btc); $amount = Converter::btc(bcsub($amount->btc, $network_fee->btc, 8)); /* Pay merchant fee and deduct it from the payment amount if any */ if ($merchant_fee->satoshi > 0) { $fee_address = $this->getFeeAddress(); $this->sendFrom($from_address_model->address, $fee_address, 'merchant fee', $merchant_fee, Converter::btc(0)); $amount = Converter::btc(bcsub($amount->btc, $merchant_fee->btc, 8)); } /* Refund merchant fee if payment fails */ try { $tx_id = $this->bitcoin_core->sendfrom($from_address, $to_address, (double) $amount->btc, 1, $note); } catch (JsonException $e) { $this->sendFrom($fee_address, $from_address_model->address, 'merchant fee refund', $merchant_fee, Converter::btc(0)); throw new JsonException($e->getMessage()); } $tx_info = $this->bitcoin_core->gettransaction($tx_id); $tx_fee = Converter::btc(abs($tx_info['fee'])); } else { /* Internal transaction. No txid returned and no fee required. */ $tx_id = 0; $tx_fee = Converter::btc(0); $merchant_fee = Converter::btc(0); $to_address_model = Mint\Address::getAddress($to_address); /* Move currency between user accounts. */ $response = $this->bitcoin_core->move($from_address_model->address, $to_address_model->address, (double) $amount->btc, 1, $note); if (!$response) { throw new JsonException(trans('error.movefailed')); } /* Update every balance, because we won't be getting callback on move */ Mint\Address::updateAddressBalance($from_address_model, -$amount->satoshi); $address_model = Mint\Address::updateAddressBalance($to_address_model, $amount->satoshi); /* Update user balance if this transaction is fee */ $user_balance = Mint\Balance::getBalance($this->user->id); if ($to_address_model->address == $this->getFeeAddress()) { $user_balance = Mint\Balance::updateUserBalance($this->user, -$amount->satoshi); } /* Add bogus transaction to db and send callback */ foreach (['send', 'receive'] as $type) { $common_data = ['tx_id' => '0000000000000000000000000000000000000000000000000000000000000000', 'user_id' => $this->user->id, 'address_from' => $from_address_model->address, 'address_to' => $to_address_model->address, 'crypto_amount' => $type == 'send' ? -$amount->satoshi : $amount->satoshi, 'confirmations' => Mint\Settings::getVal('min_confirmations'), 'network_fee' => 0, 'merchant_fee' => 0, 'tx_time' => time(), 'tx_timereceived' => time(), 'user_balance' => $user_balance->balance, 'address_balance' => $address_model->balance, 'bitcoind_balance' => $this->bitcoin_core->getbalance(), 'note' => $note, 'transaction_type' => 'internal-' . $type]; $transaction_model = Mint\Transaction::insertNewTransaction($common_data); if (!empty($this->user->callback_url)) { $this->fetchUrl($this->user->callback_url, $common_data, $transaction_model); } } } return ['tx_id' => $tx_id, 'tx_fee' => $tx_fee, 'merchant_fee' => $merchant_fee, 'amount' => $amount, 'from_address' => $from_address, 'to_address' => $to_address, 'is_internal' => $tx_id === 0]; }
/** * Send from address or address account * * @return mixed */ private function sendFrom($from_address, $to_address, $note = '', Converter $amount, Converter $network_fee, Converter $merchant_fee, $method = 'sendfrom') { $from_address_model = Mint\Address::getAddress($from_address); if ($method == 'move') { if (!$this->isUserAddress($to_address)) { $method = 'sendfrom'; } } /* Check balance */ if ($from_address_model->balance < $amount->satoshi) { throw new JsonException(trans('error.nofunds')); } /* Pay merchant fee if any */ if ($merchant_fee->satoshi > 0) { $fee_address = $this->getFeeAddress(); $this->sendFrom($from_address_model->address, $fee_address, 'merchant fee', $merchant_fee, Converter::btc(0), Converter::btc(0), 'move'); } try { if ($method == 'move') { $to_address_model = Mint\Address::getAddress($to_address); /* Move currency between user accounts. */ $response = $this->bitcoin_core->move($from_address_model->address, $to_address_model->address, (double) $amount->btc, 1, $note); if (!$response) { throw new JsonException(trans('error.movefailed')); } /* Internal transaction. No txid returned and no network fee required. */ $tx_id = 0; $tx_fee = Converter::btc(0); $send_amount = Converter::satoshi(bcadd($amount->satoshi, $merchant_fee->satoshi)); DB::beginTransaction(); /* Update every balance, because we won't be getting callback on move */ Mint\Address::updateAddressBalance($from_address_model, -$send_amount->satoshi); $address_model = Mint\Address::updateAddressBalance($to_address_model, $send_amount->satoshi); switch (true) { case $to_address_model->address == $this->getFeeAddress(): /* Update user balance if this transaction is fee */ $user_balance = Mint\Balance::updateUserBalance($this->user, -$send_amount->satoshi); $types = ['send']; break; case $from_address_model->address == $this->getFeeAddress(): /* Update user balance if this transaction is fee */ $user_balance = Mint\Balance::updateUserBalance($this->user, $send_amount->satoshi); $types = ['receive']; break; default: $user_balance = Mint\Balance::getBalance($this->user->id); $types = ['send', 'receive']; break; } DB::commit(); list($usecs, $secs) = explode(' ', microtime()); $txid = $this->getInternalTxId($from_address_model->address, $to_address_model->address, $amount->satoshi, (double) $secs + (double) $usecs); foreach ($types as $type) { $common_data = ['tx_id' => $txid, 'user_id' => $this->user->id, 'address_from' => $from_address_model->address, 'address_to' => $to_address_model->address, 'crypto_amount' => $type == 'send' ? -$amount->satoshi : $amount->satoshi, 'confirmations' => Mint\Settings::getVal('min_confirmations'), 'network_fee' => 0, 'merchant_fee' => $merchant_fee->satoshi, 'tx_time' => (int) $secs, 'tx_timereceived' => (int) $secs, 'tx_category' => $type, 'user_balance' => $user_balance->balance, 'address_balance' => $address_model->balance, 'bitcoind_balance' => $this->bitcoin_core->getbalance(), 'note' => $note, 'transaction_type' => 'internal']; $transaction_model = Mint\Transaction::insertNewTransaction($common_data); if (!empty($this->user->callback_url)) { $this->fetchUrl($this->user->callback_url, $common_data, $transaction_model); } } } else { /* Set transaction fee and deduct it from the payment amount */ $this->bitcoin_core->settxfee((double) $network_fee->btc); $total_amount = Converter::btc(bcsub($amount->btc, $network_fee->btc, 8)); $tx_id = $this->bitcoin_core->sendfrom($from_address, $to_address, (double) $amount->btc, 1, $note); $tx_info = $this->bitcoin_core->gettransaction($tx_id); $tx_fee = Converter::btc(abs($tx_info['fee'])); } } catch (JsonException $e) { /* Refund merchant fee if payment fails */ $this->sendFrom($fee_address, $from_address_model->address, 'merchant fee refund', $merchant_fee, Converter::btc(0), Converter::btc(0), 'move'); throw new JsonException($e->getMessage()); } return ['tx_id' => $tx_id, 'tx_fee' => $tx_fee, 'merchant_fee' => $merchant_fee, 'amount' => $amount, 'from_address' => $from_address, 'to_address' => $to_address, 'is_internal' => $tx_id === 0]; }