Skip to content

Instantly share code, notes, and snippets.

@sandervdo
Last active April 17, 2024 17:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sandervdo/ae253bc88789f4beb68a6ef08f8d1534 to your computer and use it in GitHub Desktop.
Save sandervdo/ae253bc88789f4beb68a6ef08f8d1534 to your computer and use it in GitHub Desktop.
Example php script that let's you connect to the bunq API without the use of the SDKs
<?php
/**
* This scripts gives a quick example on how to connect to the bunq API without the SDKs.
* For documentation on the endpoints / API in general, please see https://doc.bunq.com
*
* Please use the official channels for support;
* - bunq Together (community forum): https://together.bunq.com
* - In-app support: https://bunq.com/link/support
* - Email support: support@bunq.com
* - API support: apisupport@bunq.com
*
* @author Sander van den Oever <sandervdo@gmail.com>
* @date 2018-09-11
* @license https://opensource.org/licenses/GPL-3.0
*/
/**
* Constants.
*/
const URL_API = 'https://public-api.sandbox.bunq.com';
/**
* @return resource[]
*/
function generateKeys(): array
{
$opensslKeyPair = openssl_pkey_new(
[
'digest_alg' => 'sha512',
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
]
);
if ($opensslKeyPair === false) {
die('Error in generating keypair.');
}
return [
'private' => openssl_pkey_get_private($opensslKeyPair),
'public' => openssl_pkey_get_details($opensslKeyPair)['key'],
];
}
/**
* @param resource $publicKey
*
* @return string[]
*/
function callInstallation($publicKey): array
{
$resource = '/v1/installation';
$requestId = uniqid();
$body = [
'client_public_key' => $publicKey,
];
// Initialize.
$curl = curl_init();
// Set request parameters.
curl_setopt($curl, CURLOPT_URL, URL_API . $resource);
curl_setopt($curl, CURLOPT_VERBOSE, 1);
curl_setopt($curl, CURLOPT_HEADER, 1);
curl_setopt(
$curl,
CURLOPT_HTTPHEADER,
[
"Cache-Control: no-cache",
"User-Agent: test",
"X-Bunq-Geolocation: 0 0 0 0 NL",
"X-Bunq-Language: en_US",
"X-Bunq-Region: en_US",
"X-Bunq-Client-Request-Id: " . $requestId,
]
);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($body));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
// Execute. And save response.
$response = curl_exec($curl);
$sizeHeader = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
// Close connection.
curl_close($curl);
$bodyRaw = substr($response, $sizeHeader);
echo $bodyRaw . PHP_EOL . PHP_EOL;
$bodyJson = json_decode($bodyRaw, true);
return [
'header' => substr($response, 0, $sizeHeader),
'body' => [
'id' => $bodyJson['Response'][0]['Id']['id'],
'token' => $bodyJson['Response'][1]['Token'],
'server-public-key' => $bodyJson['Response'][2]['ServerPublicKey']['server_public_key'],
],
];
}
/**
* @param resource $privateKey
* @param string $tokenInstallation
* @param string $apiKey
*
* @return string[]
*/
function callDeviceServer($privateKey, string $tokenInstallation, string $apiKey): array
{
$method = 'POST';
$resource = '/v1/device-server';
$requestId = uniqid();
$body = json_encode(
[
'description' => 'Simple test script',
'secret' => $apiKey,
'permitted_ips' => [],
]
);
$signature = getSignatureString($privateKey, $method, $resource, $requestId, $tokenInstallation, $body);
// Initialize.
$curl = curl_init();
// Set request parameters.
curl_setopt($curl, CURLOPT_URL, URL_API . $resource);
curl_setopt($curl, CURLOPT_VERBOSE, 1);
curl_setopt($curl, CURLOPT_HEADER, 1);
curl_setopt(
$curl,
CURLOPT_HTTPHEADER,
[
"Cache-Control: no-cache",
"User-Agent: test",
"X-Bunq-Client-Authentication: " . $tokenInstallation,
"X-Bunq-Client-Request-Id: " . $requestId,
"X-Bunq-Client-Signature: " . $signature,
"X-Bunq-Geolocation: 0 0 0 0 NL",
"X-Bunq-Language: en_US",
"X-Bunq-Region: en_US",
]
);
curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
// Execute. And save response.
$response = curl_exec($curl);
$sizeHeader = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
// Close connection.
curl_close($curl);
$bodyRaw = substr($response, $sizeHeader);
echo $bodyRaw . PHP_EOL . PHP_EOL;
$bodyJson = json_decode($bodyRaw, true);
return [
'header' => substr($response, 0, $sizeHeader),
'body' => [
'id' => $bodyJson['Response']['Id']['id'],
],
];
}
/**
* @param resource $privateKey
* @param string $tokenInstallation
* @param string $apiKey
*
* @return string[]
*/
function callSessionServer($privateKey, string $tokenInstallation, string $apiKey): array
{
$method = 'POST';
$resource = '/v1/session-server';
$requestId = uniqid();
$body = json_encode(
[
'secret' => $apiKey,
]
);
$signature = getSignatureString($privateKey, $method, $resource, $requestId, $tokenInstallation, $body);
// Initialize.
$curl = curl_init();
// Set request parameters.
curl_setopt($curl, CURLOPT_URL, URL_API . $resource);
curl_setopt($curl, CURLOPT_VERBOSE, 1);
curl_setopt($curl, CURLOPT_HEADER, 1);
curl_setopt(
$curl,
CURLOPT_HTTPHEADER,
[
"Cache-Control: no-cache",
"User-Agent: test",
"X-Bunq-Client-Authentication: " . $tokenInstallation,
"X-Bunq-Client-Request-Id: " . $requestId,
"X-Bunq-Client-Signature: " . $signature,
"X-Bunq-Geolocation: 0 0 0 0 NL",
"X-Bunq-Language: en_US",
"X-Bunq-Region: en_US",
]
);
curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
// Execute. And save response.
$response = curl_exec($curl);
$sizeHeader = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
// Close connection.
curl_close($curl);
$bodyRaw = substr($response, $sizeHeader);
echo $bodyRaw . PHP_EOL . PHP_EOL;
$bodyJson = json_decode($bodyRaw, true);
return [
'header' => substr($response, 0, $sizeHeader),
'body' => [
'id' => $bodyJson['Response'][0]['Id']['id'],
'token' => $bodyJson['Response'][1]['Token']['token'],
'user-company' => isset($bodyJson['Response'][2]['UserCompany']) ? $bodyJson['Response'][2]['UserCompany'] : null,
'user-person' => isset($bodyJson['Response'][2]['UserPerson']) ? $bodyJson['Response'][2]['UserPerson'] : null,
'user-api-key' => isset($bodyJson['Response'][2]['UserApiKey']) ? $bodyJson['Response'][2]['UserApiKey'] : null,
],
];
}
/**
* @param resource $privateKey
* @param int $userId
* @param string $tokenSession
*
* @return string[]
*/
function callMonetaryAccountListing($privateKey, int $userId, string $tokenSession): array
{
$method = 'GET';
$resource = '/v1/user/' . $userId . '/monetary-account';
$requestId = uniqid();
$body = json_encode([]);
$signature = getSignatureString($privateKey, $method, $resource, $requestId, $tokenSession, $body);
// Initialize.
$curl = curl_init();
// Set request parameters.
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($curl, CURLOPT_URL, URL_API . $resource);
curl_setopt($curl, CURLOPT_VERBOSE, 1);
curl_setopt($curl, CURLOPT_HEADER, 1);
curl_setopt(
$curl,
CURLOPT_HTTPHEADER,
[
"Cache-Control: no-cache",
"User-Agent: test",
"X-Bunq-Client-Authentication: " . $tokenSession,
"X-Bunq-Client-Request-Id: " . $requestId,
"X-Bunq-Client-Signature: " . $signature,
"X-Bunq-Geolocation: 0 0 0 0 NL",
"X-Bunq-Language: en_US",
"X-Bunq-Region: en_US",
]
);
curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
// Execute. And save response.
$response = curl_exec($curl);
$sizeHeader = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
// Close connection.
curl_close($curl);
$bodyRaw = substr($response, $sizeHeader);
echo $bodyRaw . PHP_EOL . PHP_EOL;
$bodyJson = json_decode($bodyRaw, true);
return [
'header' => substr($response, 0, $sizeHeader),
'body' => $bodyJson,
];
}
/**
* @param resource $privateKey
* @param string $method
* @param string $resource
* @param string $requestId
* @param string $authentication
* @param string $bodyJson
*
* @return string
*/
function getSignatureString(
$privateKey,
string $method,
string $resource,
string $requestId,
string $authentication,
string $bodyJson
): string {
$stringToSign = <<<SIGNING_TEMPLATE
$method $resource
Cache-Control: no-cache
User-Agent: test
X-Bunq-Client-Authentication: $authentication
X-Bunq-Client-Request-Id: $requestId
X-Bunq-Geolocation: 0 0 0 0 NL
X-Bunq-Language: en_US
X-Bunq-Region: en_US
$bodyJson
SIGNING_TEMPLATE;
openssl_sign($stringToSign, $signature, $privateKey, OPENSSL_ALGO_SHA256);
return base64_encode($signature);
}
/***********************************************************************************************
* API KEY BELOW *
***********************************************************************************************/
$apiKey = '---INSERT YOUR API KEY HERE---';
/***********************************************************************************************
* API KEY ABOVE *
***********************************************************************************************/
// STEP 1: Key-exchange.
$keypair = generateKeys();
$responseInstallation = callInstallation($keypair['public']);
// Info necessary for next call.
$bunqPublicKey = $responseInstallation['body']['server-public-key'];
$tokenInstallation = $responseInstallation['body']['token']['token'];
// STEP 2: Create Device Server.
$responseDeviceServer = callDeviceServer($keypair['private'], $tokenInstallation, $apiKey);
// STEP 3: Create Session Server.
$responseSessionServer = callSessionServer($keypair['private'], $tokenInstallation, $apiKey);
$tokenSession = $responseSessionServer['body']['token'];
if (isset($responseSessionServer['body']['user-company'])) {
// Business accounts.
$userId = $responseSessionServer['body']['user-company']['id'];
} elseif (isset($responseSessionServer['body']['user-person'])) {
// Personal accounts.
$userId = $responseSessionServer['body']['user-person']['id'];
} elseif (isset($responseSessionServer['body']['user-api-key'])) {
// This user type is used for OAuth connections.
$userId = $responseSessionServer['body']['user-api-key']['id'];
} else {
die(vsprintf('Unexpected User type for session: "%s"', [$responseSessionServer['body']['id']]));
}
// Run your API calls, e.g. retrieve all monetary accounts.
$responseMonetaryAccountListing = callMonetaryAccountListing($keypair['private'], $userId, $tokenSession);
var_dump($responseMonetaryAccountListing);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment