📅  最后修改于: 2023-12-03 15:09:57.399000             🧑  作者: Mango
区块链作为一种去中心化、不可篡改的技术,越来越被人们广泛应用到支付领域。本文将介绍如何在 Laravel 网站上实现区块链支付。
在开始实现之前,需要确保已经安装并配置好以下工具和环境:
首先,我们需要安装一些必要的依赖,包括:
laravelcollective/html
- 用于生成表单bcmath
- 用于高精度计算bitwasp/bitcoin
- 用于生成比特币地址、私钥和交易安装方法如下:
$ composer require "laravelcollective/html" "bitwasp/bitcoin"
$ sudo apt-get install php-bcmath # 或者使用你的操作系统对应的安装工具,如 yum 或 brew
我们需要创建两张数据表,一张用于存储用户余额,另一张用于记录用户的充值和提现记录。
CREATE TABLE `balances` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`balance` decimal(18,2) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `balances_user_id_unique` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `transactions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`type` enum('deposit','withdrawal') NOT NULL,
`amount` decimal(18,2) NOT NULL,
`txid` varchar(64) DEFAULT NULL,
`status` enum('pending','success','failed') NOT NULL DEFAULT 'pending',
`created_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
在用户注册时,我们需要为其生成一个比特币地址,并将其保存在数据库中。
use BitWasp\Bitcoin\Address\AddressCreator;
use BitWasp\Bitcoin\Key\Factory\PrivateKeyFactory;
class UserController extends Controller
{
public function register(Request $request)
{
$privateKeyFactory = new PrivateKeyFactory();
$addressCreator = new AddressCreator();
$privateKey = $privateKeyFactory->generateCompressed();
$address = $addressCreator->fromKey($privateKey)->getAddress();
// 将 $address 和 $privateKey 存入数据库中
}
}
当用户提交提现申请时,我们需要将其余额减去提现金额,生成比特币交易并广播到区块链网络中。
use BitWasp\Bitcoin\Address\AddressCreator;
use BitWasp\Bitcoin\Key\Factory\PrivateKeyFactory;
use BitWasp\Bitcoin\Network\NetworkFactory;
use BitWasp\Bitcoin\Script\ScriptFactory;
use BitWasp\Bitcoin\Transaction\Factory\TxBuilder;
use BitWasp\Bitcoin\Transaction\TransactionFactory;
use BitWasp\Bitcoin\Transaction\TransactionOutput;
use BitWasp\Bitcoin\Transaction\TransactionOutputCollection;
use BitWasp\Bitcoin\Transaction\TransactionOutputInterface;
class UserController extends Controller
{
public function withdraw(Request $request)
{
$privateKeyFactory = new PrivateKeyFactory();
$addressCreator = new AddressCreator();
$network = NetworkFactory::bitcoin();
$privateKey = $privateKeyFactory->fromHex($user->private_key_hex);
$address = $addressCreator->fromString($user->address, $network);
$balance = $user->balance;
$amount = $request->input('amount');
if ($balance < $amount) {
// 余额不足
return response()->json([
'message' => 'Insufficient balance',
'data' => [],
]);
}
$toAddress = $request->input('address');
$fee = 10000; // 手续费
$utxos = $this->getUnspentTransactions($address->getAddress()->getAddress());
$txBuilder = new TxBuilder();
$txBuilder->spendOutput($utxos[0]);
$totalAmount = $amount + $fee;
$changeAmount = $balance - $totalAmount;
$toOutput = new TransactionOutput($amount, $address->getScriptPubKey());
$changeOutput = new TransactionOutput($changeAmount, $address->getScriptPubKey());
$txBuilder->outputs(new TransactionOutputCollection([$toOutput, $changeOutput]));
$transaction = $txBuilder->get();
$transaction = $this->signTransaction($transaction, $privateKey, $utxos);
// 广播交易
$client = new Client('https://blockchain.info/');
$result = $client->sendrawtransaction($transaction->getHex());
if ($result === true) {
// 更新数据库中的余额和提现记录
}
}
private function getUnspentTransactions(string $address)
{
$client = new Client('https://blockchain.info/');
$response = $client->fetch_unspent($address);
$utxos = [];
foreach ($response->getOutputs() as $output) {
$utxos[] = new TransactionOutputInterface($output->getValue(), ScriptFactory::fromHex($output->getScript()), $output->getTxId(), $output->getVout());
}
return $utxos;
}
private function signTransaction(TransactionInterface $transaction, PrivateKeyInterface $privateKey, array $utxos)
{
$signer = new Signer($transaction, Bitcoin::getEcAdapter());
foreach ($utxos as $i => $utxo) {
$input = $transaction->getInput($i);
$input->setScript(ScriptFactory::create()->push($privateKey->getPublicKey()->getBuffer())->op('OP_CHECKSIG'));
$signer->sign($i, $privateKey, $utxo->getScript());
}
return TransactionFactory::fromHex($signer->get()->getHex());
}
}
当用户通过比特币向系统账户充值时,我们需要监听比特币网路上的交易,如果发现收到了该用户的充值交易,则将其余额增加相应的金额,并记录充值记录。
use BitWasp\Bitcoin\Address\AddressCreator;
use BitWasp\Bitcoin\Address\PayToPubKeyHashAddress;
use BitWasp\Bitcoin\Network\NetworkFactory;
use BitWasp\Bitcoin\Script\ScriptFactory;
use BitWasp\Bitcoin\Transaction\Factory\TxBuilder;
use BitWasp\Bitcoin\Transaction\TransactionFactory;
use BitWasp\Bitcoin\Transaction\TransactionOutputCollection;
use BitWasp\Bitcoin\Transaction\TransactionOutputInterface;
use Symfony\Component\Process\Process;
class TransactionListener
{
public function listen()
{
$client = new Client('https://blockchain.info/');
$latestBlockHeight = $client->getblockcount();
while (true) {
$process = new Process("bitcoin-cli -testnet getblockhash $latestBlockHeight");
$process->run();
$blockHash = trim($process->getOutput());
$process = new Process("bitcoin-cli -testnet getblock \"$blockHash\"");
$process->run();
$blockData = json_decode($process->getOutput());
if (isset($blockData->tx)) {
foreach ($blockData->tx as $txid) {
$transaction = $client->gettransaction($txid);
if (count($transaction->getInputs()) !== 1 || count($transaction->getOutputs()) !== 2) {
continue;
}
$fromAddress = $transaction->getInputs()[0]->getScript()->getScriptParser()->getHumanReadable();
if (!startsWith($fromAddress, 'n')) {
continue;
}
$toAddress = $transaction->getOutputs()[1]->getScript()->getScriptParser()->getHumanReadable();
if (!startsWith($toAddress, 'm')) {
continue;
}
$amount = $transaction->getOutputs()[1]->getValue();
$user = User::where('address', $fromAddress)->first();
if (!isset($user)) {
continue;
}
$user->balance += $amount;
$user->save();
Transaction::create([
'user_id' => $user->id,
'type' => 'deposit',
'amount' => $amount,
'txid' => $txid,
'status' => 'success',
]);
}
}
$latestBlockHeight++;
sleep(10);
}
}
}
本文基于 Laravel 框架实现了一个简单的区块链支付系统,并介绍了比特币地址生成、比特币交易签名、交易监听等相关知识点。在实际应用时,还需要注意安全性和效率等问题。