Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
/docs export-ignore
/examples export-ignore
/tests export-ignore
/CONTRIBUTING.md export-ignore
/ROADMAP.md export-ignore
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
Expand Down
10 changes: 8 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,15 @@ jobs:
with:
php-version: ${{ matrix.php }}
extensions: curl
coverage: none
coverage: pcov
ini-values: error_reporting=-1, display_errors=On
- uses: ramsey/composer-install@v3
with:
dependency-versions: ${{ matrix.dependencies }}
- run: vendor/bin/phpunit
- run: vendor/bin/phpunit --coverage-clover=coverage.xml
- name: Upload coverage
if: matrix.php == '8.4' && matrix.dependencies == 'highest'
uses: codecov/codecov-action@v5
with:
files: coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ composer.lock
/local_tests/*
.phpunit.result.cache
.php-cs-fixer.cache
coverage.xml
13 changes: 8 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Entries for 2.2.3 and earlier were reconstructed from the git history in October

## [Unreleased]

## [2.3.0] - 2026-10-18

### Added

- `BitGoSDK::toBaseUnits()` and `fromBaseUnits()` convert between decimal amounts and base units exactly, at any number of decimals (for example 18 for ETH).
Expand All @@ -25,7 +27,7 @@ Entries for 2.2.3 and earlier were reconstructed from the git history in October
- Dev tooling: PHPUnit 9.6, PHPStan (level 6, with a baseline for the existing code) and PHP-CS-Fixer (PSR-12).
- A GitHub Actions CI workflow: `php -l` and PHPUnit on PHP 7.4 to 8.5 with lowest and highest dependencies, plus PHPStan and a coding-standard check.
- Request-shape tests for every public request method, checking the HTTP method, URL, headers and body. Requests go through an internal transport that tests replace with a recorder, so the test suite no longer touches the network.
- Composer scripts: `composer test`, `composer analyse`, `composer cs` and `composer cs:fix`.
- Composer scripts: `composer check` (all checks), `composer test`, `composer analyse`, `composer cs` and `composer cs:fix`.
- `homepage` and `support` (issues, source, security policy) in `composer.json`.

### Changed
Expand All @@ -37,13 +39,13 @@ Entries for 2.2.3 and earlier were reconstructed from the git history in October
- Requests no longer force IPv4. cURL uses IPv4 or IPv6 as the system allows. A local address bound with `setRequestIP()` decides the IP version.
- `BitGoSDK::setRequestIP()` returns `true` if the IP was applied, and `false` on testnet or for an invalid IP.
- **TLS certificates are checked against the system CA store.** The bundled 2022 `cacert.pem` is gone. If PHP finds no system CA store, the Mozilla bundle from the new `composer/ca-bundle` dependency is used.
- `ext-bcmath` is no longer required.
- **The minimum PHP version is now 7.4** (was 7.0).
- `ext-bcmath` is no longer required. `ext-json` is now declared, since the SDK always needed it.
- **The minimum PHP version is now 7.4** (was 7.0). PHP 7.4 and 8.x are supported.
- The project is now licensed under MIT. 2.2.3 and earlier were released under GPL-3.0. The file headers say so too.
- The README lists the current requirements, links to the docs on developers.bitgo.com, explains how to reach Express over TLS and names the minimum Express version (14.1.3).
- `src/` and `examples/` were reformatted to PSR-12. This is whitespace and brace placement only, with no behaviour change.
- The examples run without notices on PHP 8.4, from any directory. They pass BTC amounts to `toSatoshi()` as strings and check for errors after `unlockSession()`.
- Composer dist archives no longer include `tests/`, `examples/`, `docs/` or dotfiles. The examples are still available in the repository.
- Composer dist archives no longer include `tests/`, `examples/`, `docs/`, `CONTRIBUTING.md`, `ROADMAP.md` or dotfiles. The examples are still available in the repository.

### Deprecated

Expand Down Expand Up @@ -229,7 +231,8 @@ These still work and will be removed in 3.0.

- First release on Packagist: a BitGo API v1 client for wallets, addresses, labels, keychains and transactions. The `1.0` tag points to the same commit.

[Unreleased]: https://github.com/neto737/BitGoSDK-PHP/compare/2.2.3...HEAD
[Unreleased]: https://github.com/neto737/BitGoSDK-PHP/compare/2.3.0...HEAD
[2.3.0]: https://github.com/neto737/BitGoSDK-PHP/compare/2.2.3...2.3.0
[2.2.3]: https://github.com/neto737/BitGoSDK-PHP/compare/2.2.2...2.2.3
[2.2.2]: https://github.com/neto737/BitGoSDK-PHP/compare/2.2.1...2.2.2
[2.2.1]: https://github.com/neto737/BitGoSDK-PHP/compare/2.2.0...2.2.1
Expand Down
203 changes: 154 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,105 +1,210 @@
# BitGoSDK PHP

BitGoSDK written in PHP. This SDK contains methods for easily interacting with the BitGo API.

[![Latest Stable Version](https://poser.pugx.org/neto737/bitgosdk-php/version?style=for-the-badge)](https://packagist.org/packages/neto737/bitgosdk-php)
[![Total Downloads](https://poser.pugx.org/neto737/bitgosdk-php/downloads?style=for-the-badge)](https://packagist.org/packages/neto737/bitgosdk-php)
[![Latest Unstable Version](https://poser.pugx.org/neto737/bitgosdk-php/v/unstable?style=for-the-badge)](//packagist.org/packages/neto737/bitgosdk-php)
[![License](https://poser.pugx.org/neto737/bitgosdk-php/license?style=for-the-badge)](https://packagist.org/packages/neto737/bitgosdk-php)
[![PHP Version Require](https://poser.pugx.org/neto737/bitgosdk-php/require/php?style=for-the-badge)](https://packagist.org/packages/neto737/bitgosdk-php)
<h1 align="center">BitGoSDK PHP</h1>

<p align="center">
A PHP client for the <a href="https://www.bitgo.com">BitGo</a> API and BitGo Express.<br>
Manage wallets, addresses, transfers, policies and webhooks, and send coins, from your PHP code.
</p>

<p align="center">
<a href="https://packagist.org/packages/neto737/bitgosdk-php"><img src="https://img.shields.io/packagist/v/neto737/bitgosdk-php?label=packagist" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/neto737/bitgosdk-php"><img src="https://img.shields.io/packagist/dt/neto737/bitgosdk-php" alt="Total Downloads"></a>
<img src="https://img.shields.io/badge/php-%E2%89%A5%207.4-777BB4?logo=php&logoColor=white" alt="PHP 7.4 or later">
<a href="https://github.com/neto737/BitGoSDK-PHP/actions/workflows/ci.yml"><img src="https://github.com/neto737/BitGoSDK-PHP/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></a>
<a href="https://codecov.io/gh/neto737/BitGoSDK-PHP"><img src="https://codecov.io/gh/neto737/BitGoSDK-PHP/branch/main/graph/badge.svg" alt="Coverage"></a>
<a href="LICENSE"><img src="https://img.shields.io/github/license/neto737/BitGoSDK-PHP" alt="License"></a>
</p>

---

## Features

- **Two clients:** `BitGoSDK` talks to the BitGo API for wallets, addresses, transfers, keychains, policies, sharing, approvals and webhooks. `BitGoExpress` talks to your own [BitGo Express](#bitgo-express) instance for anything that needs your keys, such as creating wallets and sending coins.
- **Any BitGo coin,** mainnet or testnet, with `CurrencyCode` constants for 95 coins and tokens and `AddressType` constants for every UTXO address type.
- **Exact amounts:** pass an `int` or a string of digits, and amounts are always sent as strings. `toBaseUnits()` and `fromBaseUnits()` convert at any number of decimals without floats, so 18-decimal ETH amounts don't overflow.
- **Errors you can check:** a failed call returns `['error' => …, 'status' => …]` instead of throwing, so an `isset($r['error'])` check is all you need.
- **TLS verification on by default,** against the system CA store, with a custom CA option for Express or a TLS-inspecting proxy.
- **Request timeouts,** opt-in IPv4 forcing and binding to a local IP.
- **Plain PHP arrays** as results. The only dependencies are `ext-curl` and `composer/ca-bundle`.
- Tested on PHP 7.4 to 8.5.

## Requirements

- PHP 7.4 or newer, with the cURL extension
- PHP 7.4 or later, with the cURL extension
- A BitGo account and an [access token](https://developers.bitgo.com/docs/get-started-access-tokens)
- A [BitGo Express](#bitgo-express) instance, for anything that needs your keys, such as creating wallets or sending
- A [BitGo Express](#bitgo-express) instance, version 14.1.3 or later, for anything that needs your keys

## Installation

Install the SDK with [Composer](https://getcomposer.org/):

```sh
composer require neto737/bitgosdk-php
```

Or add it to the `require` section of your `composer.json`:
## Quick start

```json
"neto737/bitgosdk-php": "^2.3"
```
```php
use neto737\BitGoSDK\BitGoExpress;
use neto737\BitGoSDK\BitGoSDK;
use neto737\BitGoSDK\Enum\CurrencyCode;

Then load Composer's autoloader in your application:
// Calls that don't need your keys go straight to BitGo. The third argument selects testnet.
$bitgo = new BitGoSDK('YOUR_ACCESS_TOKEN', CurrencyCode::BITCOIN_TESTNET, true);
$bitgo->walletId = 'YOUR_WALLET_ID';

```php
require 'vendor/autoload.php';
$wallet = $bitgo->getWallet(); // ['id' => '...', 'label' => '...', 'balanceString' => '...', ...]
$address = $bitgo->createWalletAddress(); // ['address' => '2N...', 'chain' => 10, ...]

use neto737\BitGoSDK\BitGoSDK;
// Sending needs an unlocked session. The OTP is always 000000 on testnet.
$unlock = $bitgo->unlockSession('000000');
if (isset($unlock['error'])) {
exit($unlock['error']);
}

// Anything that signs goes through your BitGo Express instance
$express = new BitGoExpress('localhost', 3080, CurrencyCode::BITCOIN_TESTNET);
$express->accessToken = 'YOUR_ACCESS_TOKEN';
$express->walletId = 'YOUR_WALLET_ID';

$amount = BitGoSDK::toSatoshi('0.001'); // 100000. Pass BTC amounts as strings to avoid float rounding.
$tx = $express->sendTransaction('DESTINATION_ADDRESS', $amount, 'YOUR_WALLET_PASSPHRASE');
```

## Example
More examples are in [`examples/`](examples/).

```php
require 'vendor/autoload.php';
> [!NOTE]
> Requests to Express carry wallet passphrases and private keys. Use plain `http://` only when Express runs on the same host. Otherwise see [Use TLS unless Express is on the same host](#use-tls-unless-express-is-on-the-same-host).

use neto737\BitGoSDK\BitGoSDK;
use neto737\BitGoSDK\Enum\CurrencyCode;
## What's covered

### `BitGoSDK`: the BitGo API

| Area | Methods |
| --- | --- |
| User and session | `getCurrentUserProfile()`, `getSessionInfo()`, `lockSession()`, `unlockSession()`, `logout()` |
| Wallets | `listWallets()`, `getWallet()`, `getWalletByAddress()`, `getTotalBalances()`, `getMaximumSpendable()`, `freezeWallet()` |
| Addresses | `createWalletAddress()`, `getWalletAddress()`, `getWalletAddresses()`, `updateWalletAddress()` |
| Transfers and unspents | `listWalletTransfers()`, `getWalletTransfer()`, `getWalletTransferBySequenceId()`, `getWalletTransactions()`, `getWalletTransaction()`, `listWalletUnspents()` |
| Keychains | `listKeychains()`, `getKeychain()`, `createBitGoKeychain()`, `createBackupKeychain()`, `addKeychain()` |
| Policies | `setVelocityPolicyRule()`, `updateVelocityPolicyRule()`, `removePolicyRule()` |
| Wallet sharing | `listWalletShares()`, `getWalletShare()`, `resendWalletShareInvite()`, `cancelWalletShare()`, `removeWalletUser()` |
| Pending approvals | `listPendingApprovals()`, `getPendingApproval()` |
| Webhooks | Wallet and user webhooks: `list…`, `add…`, `remove…` and `simulate…Webhook()`. `getWebhookPayload()` reads an incoming webhook. |
| Fees and prices | `estimateTransactionFees()`, `getMarketPriceData()` |
| Amounts | `toSatoshi()`, `toBTC()`, `toBaseUnits()`, `fromBaseUnits()` |

### `BitGoExpress`: your BitGo Express instance

$bitgo = new BitGoSDK('YOUR_API_KEY_HERE', CurrencyCode::BITCOIN, false);
$bitgo->walletId = 'YOUR_WALLET_ID_HERE';
| Area | Methods |
| --- | --- |
| Status and login | `ping()`, `pingExpress()`, `login()` |
| Wallets | `generateWallet()`, `addWallet()`, `createKeychain()` |
| Sending | `sendTransaction()`, `sendTransactionToMany()` |
| Build, sign and send in steps | `buildTransaction()`, `signBuildTransaction()`, `sendBuildTransaction()` |
| Unspent management | `consolidateWalletUnspents()`, `fanoutWalletUnspents()`, `sweep()` |
| Sharing and approvals | `shareWallet()`, `acceptWalletShare()`, `updatePendingApproval()` |
| Utilities | `encrypt()`, `decrypt()`, `verifyAddress()` |

$createAddress = $bitgo->createWalletAddress();
Each method's parameters are documented in its docblock. For what each endpoint does, see the [BitGo API reference](https://developers.bitgo.com/reference/overview) and the [Express API reference](https://developers.bitgo.com/reference/express-overview).

## Error handling

Calls don't throw on API or network errors. They return an array with an `error` message and the HTTP `status`, plus any other fields BitGo sent:

```php
$transfer = $bitgo->getWalletTransfer('TRANSFER_ID');

if (isset($transfer['error'])) {
if ($transfer['status'] === 0) {
// no response: timeout, DNS, TLS, ...
} elseif ($transfer['status'] === 404) {
// no such transfer
} else {
// any other non-2xx status, or an invalid JSON body
}
}
```

More examples are in [`examples/`](examples/).
`buildTransaction()` and `signBuildTransaction()` return the same fields as an object.

## BitGo Express

BitGo signs transactions on the client, so sending coins, creating wallets and other calls that use your keys go through `BitGoExpress`, which talks to your own BitGo Express instance. See [Install BitGo Express](https://developers.bitgo.com/docs/get-started-express-install) to run one, and the [Express API reference](https://developers.bitgo.com/reference/express-overview) for its endpoints.
BitGo signs transactions on the client, so sending coins, creating wallets and other calls that use your keys go through `BitGoExpress`, which talks to your own BitGo Express instance. See [Install BitGo Express](https://developers.bitgo.com/docs/get-started-express-install) to run one.

### Use TLS unless Express is on the same host

Requests to Express carry wallet passphrases and private keys. Over plain `http://` that is only safe when Express listens on `localhost`. Otherwise, run Express with a TLS certificate and prefix the hostname with `https://`:
Over plain `http://`, Express is only safe when it listens on `localhost`. Otherwise, run Express with a TLS certificate and prefix the hostname with `https://`:

```php
use neto737\BitGoSDK\BitGoExpress;
use neto737\BitGoSDK\Enum\CurrencyCode;

$express = new BitGoExpress('https://express.internal', 3080, CurrencyCode::BITCOIN);

// For a self-signed or private-CA certificate, trust that certificate or CA file.
$express->setCaFile('/path/to/express.crt');
```

Certificates are verified by default. `setVerifyTls(false)` turns verification off, so use it only for local testing.
`setCaFile()` throws an `InvalidArgumentException` if the file can't be read. Certificates are verified by default. `setVerifyTls(false)` turns verification off, so use it only for local testing.

### Minimum Express version

Use BitGo Express **14.1.3 or newer**. Older versions fail on two changes BitGo made on its side:
Use BitGo Express **14.1.3 or later**. Older versions fail on two changes BitGo made on its side:

- **UTXO withdrawals from cold wallets** must use PSBT in production. BitGo needs Express 14.1.3 or newer for this ([changelog](https://developers.bitgo.com/changelog/20251007)).
- **UTXO withdrawals from cold wallets** must use PSBT in production. BitGo needs Express 14.1.3 or later for this ([changelog](https://developers.bitgo.com/changelog/20251007)).
- **Send-to-many on EVM assets** (`sendTransactionToMany()` on ETH and other EVM coins) uses a new batcher contract since May 22, 2025, so Express releases from before that date can't send it ([changelog](https://developers.bitgo.com/changelog/20250522)).

## Client settings

Both clients have these settings:

```php
$bitgo->setTimeout(30); // request timeout in seconds. Default: 60 (BitGoSDK) or 310 (BitGoExpress)
$bitgo->setConnectTimeout(5); // connect timeout in seconds. Default: 10
$bitgo->setForceIPv4(true); // use IPv4 only. Default: whatever the system allows
$bitgo->setCaFile('/path/to/ca.pem');
```

`BitGoSDK::setRequestIP('203.0.113.10')` sends requests from that local IP. It returns `false` on testnet or for an invalid IP.

### TLS certificates

Both clients check certificates against your system's CA store. If PHP can't find one, they use the Mozilla CA bundle from [`composer/ca-bundle`](https://github.com/composer/ca-bundle), so keep your dependencies up to date. To trust a different CA, for example behind a TLS-inspecting proxy, call `setCaFile('/path/to/ca.pem')`.

## Documentation

- [BitGo developer portal](https://developers.bitgo.com/)
- [BitGo API reference](https://developers.bitgo.com/reference/overview)
- [BitGo API reference](https://developers.bitgo.com/reference/overview) and [Express API reference](https://developers.bitgo.com/reference/express-overview)
- [Webhooks](https://developers.bitgo.com/docs/webhooks-overview) and [policies](https://developers.bitgo.com/docs/policies-overview)

## TLS certificates
**Upgrading from 2.2.x?** 2.3 is a drop-in upgrade, but two behaviours changed: `0` and `false` params are now sent, and errors come back as `['error' => …, 'status' => …]`. Read the [Changed section of CHANGELOG.md](CHANGELOG.md#changed) before you upgrade.

Both clients check certificates against your system's CA store. If PHP can't find one, they use the Mozilla CA bundle from [`composer/ca-bundle`](https://github.com/composer/ca-bundle), so keep your dependencies up to date. To trust a different CA, for example behind a TLS-inspecting proxy, call `setCaFile('/path/to/ca.pem')`.
## Contributing

## License
Bug reports and pull requests are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for the branch model and release process. To run the checks:

MIT. See [LICENSE](LICENSE). Versions 2.2.3 and earlier were released under GPL-3.0.
```sh
composer install
composer check # coding standard, static analysis and tests
```

Each check can also run on its own: `composer test` (PHPUnit), `composer analyse` (PHPStan) and `composer cs` (PHP-CS-Fixer). `composer cs:fix` fixes the coding style. The code follows PSR-12, with 4-space indentation and LF line endings.

The tests replace the HTTP transport with a recorder, so they don't need a BitGo account or network access.

To report a security issue, follow [SECURITY.md](SECURITY.md) instead of opening a public issue.

## Credits

- [Neto Melo](https://github.com/neto737)
Created and maintained by [Neto Melo](https://github.com/neto737).

This is a community project. It is not affiliated with or endorsed by BitGo, Inc.

## Donate
## Support the project :heart:

If this library saves you time, you can support its development via [Liberapay](https://liberapay.com/neto737), [PayPal](https://www.paypal.com/donate/?business=T7RVRCXLZXB58&no_recurring=0&currency_code=USD) or crypto:

```
BTC: bc1pduj90df9cs3md3gym3q809slfv2x5phnpv8xznajys5q3tlulnzqt3flwn
ETH: 0xeef9220639F14E7A0FD825AAAd0574e5a8aD7A4B
LTC: ltc1q508qfkd09vyya6c5zkfx4r248pf3ezj9ngjdr2
```

## License

[![Donate BTC](https://img.shields.io/badge/donate-BTC-ff9900.svg?style=for-the-badge)](https://www.blockchain.com/btc/address/bc1pduj90df9cs3md3gym3q809slfv2x5phnpv8xznajys5q3tlulnzqt3flwn)
[![Donate ETH](https://img.shields.io/badge/donate-ETH-3C3C3D.svg?style=for-the-badge)](https://etherscan.io/address/0xeef9220639F14E7A0FD825AAAd0574e5a8aD7A4B)
[![Donate LTC](https://img.shields.io/badge/donate-LTC-D3D3D3.svg?style=for-the-badge)](https://blockchair.com/litecoin/address/ltc1q508qfkd09vyya6c5zkfx4r248pf3ezj9ngjdr2)
[![Donate with PayPal](https://img.shields.io/badge/donate-PayPal-blue.svg?style=for-the-badge)](https://www.paypal.com/donate/?business=T7RVRCXLZXB58&no_recurring=0&currency_code=USD)
Released under the [MIT License](LICENSE). Versions 2.2.3 and earlier were released under GPL-3.0.
Loading
Loading