Introducing bitget-php: a production-grade PHP and Laravel SDK for Bitget UTA v3
DEV Community

Introducing bitget-php: a production-grade PHP and Laravel SDK for Bitget UTA v3

PHP is a strong fit for dashboards, back-office systems, trading utilities, and event-driven services. Yet exchange integrations can still become the most fragile part of the stack. Developers must sign authenticated requests correctly, retain decimal precision, surface actionable API errors, and keep real-time connections healthy when a network or remote service interrupts them. bitget-php is an open-source SDK that aims to make that boundary more dependable. It is a production-oriented PHP 8.2+ client for the Bitget Unified Trading Account (UTA) v3 API, with optional Laravel 10-13 integration. The project’s initial release focuses deliberately on a practical core: market data, account operations, trading operations, and a reconnecting WebSocket client. 1 The library is built for demo-first trading safety. Its own documentation recommends validating new code against Bitget demo credentials before connecting an application to a live account. 1 This article introduces the library from a developer’s perspective: what it covers today, how its design avoids several common integration pitfalls, and how it can become a stable starting point for a PHP-based Bitget integration. The exchange-integration problem is more than HTTP Calling an exchange endpoint may look like a simple POST or GET , but a production integration has a much wider responsibility. A client must consistently apply request signing, make error states intelligible to the application, avoid leaking API credentials through logs, and preserve the exact meaning of a decimal value. It also needs an operational model for real-time transport: what happens after a disconnect, and how does an application recover its subscriptions? bitget-php addresses these concerns at the SDK layer. It uses Guzzle for HTTP transport and allows a GuzzleHttp\Client to be injected, which is helpful when testing, routing requests through a proxy, or applying project-specific transport configuration. It supports PSR-3 logging while using a no-op logger by default, and its documentation states that credentials are never logged. 1 | Design concern | How bitget-php addresses it | |---|---| | Decimal values | Prices, quantities, PnL, and fee values are represented as strings rather than floats. 1 | | API failures | A typed exception hierarchy exposes specific application-level failure paths and the raw Bitget error code. 1 | | HTTP testing and customization | The Guzzle client is injectable instead of being fixed internally. 1 | | Observability | The SDK accepts a PSR-3 logger and defaults to NullLogger ; credentials are not written to logs. 1 | | Real-time reliability | The WebSocket client maintains heartbeat handling, reconnection, and prior subscription recovery. 1 | | Framework flexibility | Laravel support is supplied without making Laravel a requirement for non-Laravel projects. 1 | The approach to decimals deserves special attention. Binary floating-point values cannot precisely represent many decimal fractions, so silently converting exchange quantities or prices into PHP floats can change a value in ways that matter to accounting and order logic. By keeping API-facing numeric values as strings, the SDK leaves exact arithmetic under the developer’s control, including the option to use BCMath or another appropriate decimal strategy. A modern PHP foundation without framework lock-in The package requires PHP 8.2 or newer and uses strict typing throughout its codebase. It also uses readonly constructor properties, a small but important design choice that makes an object’s initialized dependencies harder to mutate accidentally. 1 Although the project offers first-class Laravel 10-13 conveniences-an auto-discovered service provider, publishable configuration, and a Bitget facade-the core SDK does not have a hard illuminate/* dependency outside of Laravel applications. 1 That distinction matters. A Laravel team can adopt the package with familiar configuration conventions, while a CLI worker, Symfony project, or framework-free PHP application can use the same client directly. Installation is a single Composer command: composer require tigusigalpa/bitget-php For a Laravel application, credentials can live in configuration backed by environment variables. For a standalone service, they can be loaded through the project’s own secure configuration mechanism. In either case, API credentials should not be committed to source control or embedded into a front-end application. Phase 1 REST coverage: the useful essentials The project is transparent about its scope. Rather than implying that every endpoint in the Bitget UTA v3 surface is wrapped, it documents precisely which operations are included in Phase 1 and which are not. 2 That is a meaningful advantage for planning: teams can decide early whether the current surface matches their workflow rather than discovering a missing endpoint late in implementation. | Area | Supported SDK methods in Phase 1 | Typical role | |---|---|---| | Public market data | Market::getInstruments() , Market::getTickers() , Market::getOrderBook() | Discover instruments, display prices, and obtain order-book data. 2 | | Private account operations | Account::getAssets() , Account::getSettings() , Account::setLeverage() | Read available assets and settings, then adjust leverage where supported. 2 | | Private trade operations | Trade::placeOrder() , Trade::modifyOrder() , Trade::cancelOrder() , Trade::getOpenOrders() , Trade::getOrderHistory() , Trade::getPositions() | Manage an order lifecycle and reconcile positions. 2 | This is enough to support a well-defined first version of several products: a market-monitoring page, a controlled order-entry service, a bot that watches positions, or an internal trading-operations console. The SDK’s endpoint map also links every listed method to its corresponding Bitget API documentation, helping engineers trace an integration requirement from application code back to the exchange specification. 2 Equally important, the repository documents the current boundaries. In Phase 1, it does not include areas such as transfers, Trading Bot, Copy Trading, RFQ, Fiat, Finance/earn, batch orders, plan/trigger orders, or the other UTA v3 REST endpoints. 2 A reliable promotion should say this plainly: the library is a focused foundation, not an unsupported promise of complete coverage. Safer order code starts in the demo environment The project provides a direct demo-trading mode. When demoTrading: true -or the BITGET_DEMO=true setting-is used with Demo API credentials, REST requests include Bitget’s required paptrading: 1 header. The SDK also exposes demo public and private WebSocket URLs. 1 The following example follows the repository’s safety pattern. An order is both configured for demo trading and guarded by an explicit environment-variable opt-in. The intentionally distant limit price helps prevent an accidental fill during a test. use Tigusigalpa\Bitget\Client; $client = new Client( apiKey: config('bitget.api_key'), secretKey: config('bitget.secret_key'), passphrase: config('bitget.passphrase'), demoTrading: true, ); // Do not send an order unless the explicit gate is enabled. if (getenv('BITGET_ENABLE_TRADING') === '1') { $client->trade()->placeOrder([ 'category' => 'SPOT', 'symbol' => 'BTCUSDT', 'side' => 'buy', 'orderType' => 'limit', 'price' => '10000', 'qty' => '0.001', ]); } That pattern does not replace testing, permissions management, risk limits, or review. It does, however, make the safer path easier to follow. A team can wire up application behavior in a simulated environment, exercise success and failure paths, and remove the deliberate gate only after it has made a conscious production decision. Typed errors make recovery logic explicit Exchange error handling should not collapse every failure into one generic exception. An invalid API key, a rate-limit response, insufficient funds, and an order lookup failure lead to very different recovery actions. bitget-php exposes typed exceptions including AuthenticationException , RateLimitException , InvalidParameterException , InsufficientFundsException , and OrderNotFoundException . Each inherits from the SDK’s exception model and retains Bitget’s raw error code; a general BitgetException also gives access to the raw response. 1 use Tigusigalpa\Bitget\Exceptions\AuthenticationException; use Tigusigalpa\Bitget\Exceptions\InsufficientFundsException; use Tigusigalpa\Bitget\Exceptions\RateLimitException; use Tigusigalpa\Bitget\Exceptions\BitgetException; try { $client->trade()->placeOrder([/* order payload */]); } catch (AuthenticationException $e) { // Rotate or correct credentials; do not retry blindly. } catch (InsufficientFundsException $e) { // Notify the workflow that the required balance or margin is unavailable. } catch (RateLimitException $e) { // Back off before retrying according to your application policy. } catch (BitgetException $e) { // Record $e->bitgetCode and $e->rawResponse for diagnostics. } The value here is architectural. Application code can decide which failures are retriable, which should create an alert, and which should immediately stop a workflow. That leads to clearer observability and safer automation than a single catch-all branch. Real-time data with reconnection built in A WebSocket is often the right channel for reacting to market updates or private fill notifications, but it needs operational safeguards. The SDK’s WebsocketClient uses a pluggable ConnectionInterface . It ships with a synchronous textalk/websocket adapter, while projects using ReactPHP, Amp, or Laravel Octane can implement their own adapter for a non-blocking event loop. 1 Here is the documented shape of a public ticker subscription: use Tigusigalpa\Bitget\WebsocketClient; $ws = new WebsocketClient(WebsocketClient::DEFAULT_PUBLIC_URL); $ws->connect(); $ws->subscribe([ 'instType' => 'SPOT', 'topic' => 'ticker', 'symbol' => 'BTCUSDT', ]); $ws->listen(function (array $push) { echo j

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.