bunq / sdk_php Goto Github PK
View Code? Open in Web Editor NEWPHP SDK for bunq API
License: MIT License
PHP SDK for bunq API
License: MIT License
toJson
and fromJson
methods allowing to save context to or load from JSON are presenttoJson
nor fromJson
When I use the ApiContext::create()
function I get a cURL error. Does anybody know why this error shows up or how to fix it?
An unexpected error occurred: cURL error 0: The cURL request was retried 3 times and did not succeed. The most likely reason for the failure is that cURL was unable to rewind the body of the request and subsequent retries resulted in the same error. Turn on the debug option to see what went wrong. See https://bugs.php.net/bug.php?id=47204 for more information. (see http://curl.haxx.se/libcurl/c/libcurl-errors.html)
Update: I get the same issue when I use the install command
The scripts used to create an API context throw errors after inserting the API-key.
Error:
"Fatal error: Cannot use isset() on the result of an expression (you can use "null !== expression" instead) in /my/project/path/vendor/bunq/sdk_php/src/Model/BunqModel.php on line 180"
"vendor/bin/bunq-install"
in the project root folder, the error will be thrown after entering the enviroment and API-key."api_context_save_example.php"
.Some feedback on the current approach:
public
, protected
, private
) of constants, which seem to be used extensively in the codecomposer.json
should be omittedcomposer.lock
should probably not be committed to repository and part of .gitignore
.idea
folder should probably not be committed to repository and part of .gitignore
lib
folder should "probably" be renamed to src
lib
suggests 3rd party code, while src
is "your" code...X-Bunq-Server-Response-Id
header.logs
When I try to use the User::get
function a User object will return but its completely empty.
I did some further debugging and it seems like the Response body is completely empty (The status code is still 200 😟 )
Im using the Sandbox Environment and the request url I'm trying to call is sandbox.public.api.bunq.com/v1/user/3427
BunqModel
to_json
no logs
$apiContext = ApiContext::restore(BUNQ_CONFIG);
$certificateList = CertificatePinned::listing($apiContext, BUNQ_USER_ID);
Provides an error:
Argument 1 passed to bunq\Model\BunqModel::createListFromResponseArray() must be of the type array, string given, called in vendor\bunq\sdk_php\lib\Model\BunqModel.php on line 160
ApiClient
tries to get the version
from composer.json
. However, the version
is not present there anymore since #26.Phpstan does not support PHP 5.6, we do. Therefore it is not possible for us to use it.
Fatal error: Cannot use bunq\Model\Generated\Object\Avatar as Avatar because the name is already in use in /sdk_php/src/Model/Generated/MonetaryAccountBank.php on line 8
When using XAMPP on a local windows development machine I get the following error:
Error: An unexpected error occurred with status code "400" and message "The request signature is invalid."
Code used:
$environmentType = BunqEnumApiEnvironmentType::SANDBOX();
$apiKey = '### Your API Key ###';
$deviceDescription = '### Your device description ###';
$permittedIps = ['my.local.ip.address'];
$apiContext = ApiContext::create(
$environmentType,
$apiKey,
$deviceDescription,
$permittedIps
);
This does not happen on Linux machines. It seems to be a problem with the session-server call, the first call that has to be signed.
Tested
check box and says which issue the PR will close.no logs
I'm trying to integrate the Bunq API in my Laravel project. The installation is flawless, however when I try to create a request I get an error "HTTP Response Code: 400. The request signature is invalid."
Has this something to do with the fact that I'm using Laravel Valet on my computer?
HTTP Response Code: 400. The request signature is invalid.
The Bunq API expects requests to be initiated from a fixed / whitelisted IP address, which is difficult in a "cloud" environment, with ever changing IP addresses.
A proxy can circumvent this problem and ensure a fixed IP address is used. However the current initialisation of the Guzzle client does not allow to set the proxy
option.
Also see this issue which discusses a similar situation: link0/bunq#27
BunqResponse
object which, besides the response model, includes the response headers and paginationcreateFromJsonString
No logs
no logs
no logs
ensureSesiosActive()
$apiContext->ensureSessionActive();
$apiContext->save(static::FILENAME_CONTEXT_CONFIG);
Which means you will be saving an unchanged context on each call
$response.getValue()
has a strict type in doc block, and the IDE auto-completion works on it$response.getValue()
doesn't have a strict type in doc block and therefore IDE auto-completion doesn't work on itcomposer install
with PHP 5.6 for the SDKcomposer install
fails, because PHP 5.6 is not supportedcomposer install
succeedsApiContext
with an UserPerson
API keytoJson()
method on this contextJSON
representation of the ApiContext
Return value of bunq\Model\Core\SessionServer::getUserCompany() must be an instance of bunq\Model\Generated\Endpoint\UserCompany, null returned
/Users/khellemun/GitHub/bunq/sdk_php/src/Model/Core/SessionServer.php:79
/Users/khellemun/GitHub/bunq/sdk_php/src/Context/SessionContext.php:76
/Users/khellemun/GitHub/bunq/sdk_php/src/Context/SessionContext.php:64
/Users/khellemun/GitHub/bunq/sdk_php/src/Context/SessionContext.php:52
/Users/khellemun/GitHub/bunq/sdk_php/src/Context/ApiContext.php:164
/Users/khellemun/GitHub/bunq/sdk_php/src/Context/ApiContext.php:123
/Users/khellemun/GitHub/bunq/sdk_php/src/Context/ApiContext.php:110
/Users/khellemun/GitHub/bunq/sdk_php/tests/BunqSdkTestBase.php:64
/Users/khellemun/GitHub/bunq/sdk_php/tests/Context/ApiContextTest.php:22
The scripts in composer.json still refer to lib
, which got changed to src
.
The purpose of this issue is to introduce https://zappr.opensource.zalan.do so that quality control on pull requests can be assured.
The following configuration should be used:
autobranch:
pattern: 'bunq/sdk_php#{number}-{title}'
length: 100
commit:
message:
patterns:
- '([A-Za-z0-9 ]+)\. (\(bunq\/sdk_php#[0-9]+\))'
specification:
title:
minimum-length:
enabled: true
length: 8
body:
minimum-length:
enabled: true
length: 8
contains-url: true
contains-issue-number: true
template:
differs-from-body: true
pull-request:
labels:
additional: true
required:
- Reviewed by André
- Can be merged
$callbackModel = NotificationUrl::fromJson($CallbackJson);
$callbackModel->getReferencedObject();
if (!$callbackModel isntanceof CallbackModelImExcpeting) {
throw new Exception();
}
no logs
no logs
A composer install
results in:
[Composer\Json\JsonValidationException]
"./composer.json" does not match the expected JSON schema:
- authors[0] : String value found, but an object is required
TokenQrRequestIdeal
using the SDK.TokenQrRequestIdeal
object is returned.RequestResponse
object, but the SDK tries to cast it to a TokenQrRequestIdeal
, resulting in an error.Together topic: https://together.bunq.com/topic/does-tokenqrrequestidealcreate-code-wrong
Report from user @china1688:
Dear Bunqer,
i am testing TokenQrRequestIdeal class, when i call TokenQrRequestIdeal::create, there is always error as below:
PHP Notice: Undefined index: TokenQrRequestIdeal in D:\xampp\htdocs\bunq_test\vendor\bunq\sdk_php\src\Model\Core\BunqModel.php on line 127
Notice: Undefined index: TokenQrRequestIdeal in D:\xampp\htdocs\bunq_test\vendor\bunq\sdk_php\src\Model\Core\BunqModel.php on line 127
i check the function of TokenQrRequestIdeal::create, the coding is different from Payment::create, RequestInquiry::create, IdealMerchantTransaction::create,
then i guess the code of TokenQrRequestIdeal::create as below mark in bold is wrong coding?
/******************TokenQrRequestIdeal::create ************************************
public static function create(ApiContext $apiContext, array $requestMap, int $userId, array $customHeaders = []): BunqResponseTokenQrRequestIdeal
{ ....
return BunqResponseTokenQrRequestIdeal::castFromBunqResponse(
static::fromJson($responseRaw, self::OBJECT_TYPE)
);}
/******************Payment::create ************************************
public static function create(ApiContext $apiContext, array $requestMap, int $userId, int $monetaryAccountId, array $customHeaders = []): BunqResponseInt
{...
return BunqResponseInt::castFromBunqResponse(
static::processForId($responseRaw)
);}
/******************IdealMerchantTransactionList::create ************************************
public static function create(ApiContext $apiContext, array $requestMap, int $userId, int $monetaryAccountId, array $customHeaders = []): BunqResponseInt
{
return BunqResponseInt::castFromBunqResponse(
static::processForId($responseRaw)
);}
/******************RequestInquiry::create ************************************
public static function create(ApiContext $apiContext, array $requestMap, int $userId, int $monetaryAccountId, array $customHeaders = []): BunqResponseInt
{...
return BunqResponseInt::castFromBunqResponse(
static::processForId($responseRaw)
);}
Could anyone help check the code?
vendor/bin/bunq-install
PHP Fatal error: Uncaught TypeError: Argument 1 passed to bunq\Util\InstallationUtil::formatIps() must be of the type string, null given, called in /Users/mbernson/bunqtest/vendor/bunq/sdk_php/src/Util/InstallationUtil.php on line 88 and defined in /Users/mbernson/bunqtest/vendor/bunq/sdk_php/src/Util/InstallationUtil.php:193
Stack trace:
#0 /Users/mbernson/bunqtest/vendor/bunq/sdk_php/src/Util/InstallationUtil.php(88): bunq\Util\InstallationUtil::formatIps(NULL)
#1 /Users/mbernson/bunqtest/vendor/bunq/sdk_php/bin/bunq-install(16): bunq\Util\InstallationUtil::interactiveInstall()
#2 {main}
thrown in /Users/mbernson/bunqtest/vendor/bunq/sdk_php/src/Util/InstallationUtil.php on line 193
Fatal error: Uncaught TypeError: Argument 1 passed to bunq\Util\InstallationUtil::formatIps() must be of the type string, null given, called in /Users/mbernson/bunqtest/vendor/bunq/sdk_php/src/Util/InstallationUtil.php on line 88 and defined in /Users/mbernson/bunqtest/vendor/bunq/sdk_php/src/Util/InstallationUtil.php on line 193
TypeError: Argument 1 passed to bunq\Util\InstallationUtil::formatIps() must be of the type string, null given, called in /Users/mbernson/bunqtest/vendor/bunq/sdk_php/src/Util/InstallationUtil.php on line 88 in /Users/mbernson/bunqtest/vendor/bunq/sdk_php/src/Util/InstallationUtil.php on line 193
Call Stack:
0.0003 354664 1. {main}() /Users/mbernson/bunqtest/vendor/bunq/sdk_php/bin/bunq-install:0
0.0044 677128 2. bunq\Util\InstallationUtil::interactiveInstall() /Users/mbernson/bunqtest/vendor/bunq/sdk_php/bin/bunq-install:16
9.4677 1736472 3. bunq\Util\InstallationUtil::formatIps() /Users/mbernson/bunqtest/vendor/bunq/sdk_php/src/Util/InstallationUtil.php:88
See PR #78
no logs
When running the initialisation, I am getting an exception.
Choose an environment (SANDBOX/PRODUCTION): PRODUCTION
Please provide your api key: API_KEY_HERE
An unexpected error occurred: cURL error 60: SSL certificate problem: Invalid certificate chain (see http://curl.haxx.se/libcurl/c/libcurl-errors.html)
Am I missing something?
PHP version:
PHP 7.1.5 (cli) (built: May 13 2017 13:30:32) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
Curl version:
curl 7.54.0 (x86_64-apple-darwin16.0) libcurl/7.54.0 SecureTransport zlib/1.2.8
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz UnixSockets
id
, created
or updated
from CardGeneratedCvc2
no logs
Call to a member function getSessionTimeout() on null
.Call to a member function getSessionTimeout() on null
bunq/sdk_php/lib/Context/SessionContext.php:60
bunq/sdk_php/lib/Context/SessionContext.php:48
bunq/sdk_php/lib/Context/ApiContext.php:149
bunq/sdk_php/lib/Context/ApiContext.php:106
bunq/sdk_php/lib/Context/ApiContext.php:93
bunq/sdk_php/test/ApiContextHandler.php:36
bunq/sdk_php/test/Model/Generated/MonetaryAccountBankTest.php:69
There is a typo in the bunq\Model\Generated\Endpoint\SchedulePayment
class. It has the constant const OBJECT_TYPE = 'SchedulePayment';
while the API returns an object of type ScheduledPayment
. When I change the constant to this, it works fine.
PHP Notice: Undefined index: SchedulePayment in /Users/mathijs/Code/bunqapibouwsel/vendor/bunq/sdk_php/src/Model/Core/BunqModel.php on line 115
no logs
At the time of writing, to be able to ascertain the user ID requires making a specific call to the method of the type of the user you're expecting. If you would run the following piece of code:
$userId = User::listing($apiContext)[0]->getUserPerson()->getId();
You would run into an exception if the API HTTP response yields a business user (or vice versa when you use getUserCompany()
:
Fatal error: Uncaught Error: Call to a member function getId() on null in /Users/rukn01/Projects/rknol/sdk_php/example/monetary_account_example.php:20
So logically speaking if your integration is not aware of the kind of user that will come out of the API, the only way you can express this would be something like this pre-PHP 7.1:
$userWrapper = User::listing($apiContext)[0];
$userObj = !is_null($userWrapper->getUserPerson())
? $userWrapper->getUserPerson()
: $userWrapper->getUserCompany();
$userId = $userObj->getId();
Or PHP 7.1+ with the null-coalesce operator:
$userWrapper = User::listing($apiContext)[0];
$userId = ($userWrapper->getUserPerson() ?? $userWrapper->getUserCompany())->getId();
Either scenarios are unfavorable, and unnecessary if the design of the SDK is more developer friendly. I understand that the reasoning behind this is because resource-wise they're 3 different resources, however it makes for very ugly implementation.
One way to solve this would to abstract away the logic inside the User
resource:
class User
{
// ...
public function getUserObject()
{
return !is_null($this->userPerson)
? $this->userPerson
: $this->userCompany;
}
}
Or something in the likes so that the DX will be:
$userId = User::listing($apiContext)[0]->getUserObject()->getId();
when trying to run vendor/bin/bunq-install
i encountered the following error:
An unexpected error occurred: cURL error 51: SSL: no alternative certificate subject name
matches target host name 'public.api.bunq.com'
(see http://curl.haxx.se/libcurl/c/libcurl-errors.html)
so i checked the url public.api.bunq.com
using my browser.
The server is returning the certificate for the sandbox environment:
Ps. when in need of a new certificate checkout https://letsencrypt.org/
In the current implementation of the SDK static methods on classes are used to make requests.
For example:
// ...
$users = User::listing($apiContext)->getValue();
This is not good for developer experience as it makes it non-trivial to implement the SDK in a project with high unit test coverage.
Imagine for example that in my project I have a FinanceService
that has a method to accumulate total spend on saturday + sunday. If I expressed the service like this:
<?php
namespace Bla;
use bunq\Context\ApiContext;
use bunq\Model\Generated\Payment;
class FinanceService
{
private $apiContext;
public function __construct(ApiContext $apiContext)
{
$this->apiContext = $apiContext;
}
public function getWeekendSpending($userId, $monetaryAccountId)
{
$amount = 0;
$paymentList = Payment::listing($this->apiContext, $userId, $monetaryAccountId);
foreach ($paymentList as $payment) {
$date = new \DateTime($payment->getCreated());
if (in_array($date->format('D'), ['Sat', 'Sun'])) {
$amount += $payment->getAmount();
}
}
return $amount;
}
}
Writing the unit test would not allow me to mock the API HTTP request very easily as there is no simple way to ensure that Payment::listing
does not make an actual HTTP request, or to influence what the return value would be.
There is a workaround I could do on my end, but it involves wrapping each individual resource that makes HTTP requests in a wrapper object like this:
class PaymentResource implements PaymentResourceInterface
{
private $apiContext;
public function __construct(ApiContext $apiContext)
{
$this->apiContext = $apiContext;
}
public function listing($userId, $monetaryAccountId)
{
return Payment::listing($this->apiContext, $userId, $monetaryAccountId);
}
}
To be able to mock (the result of) API HTTP requests in a unit test designed to test only the logic of the service method, and not invoke an actual API HTTP request (or even obtain/instantiate an ApiContext/session with the API):
class FinanceServiceTest extends \PHPUnit\Framework\TestCase
{
public function testGetWeekendSpending()
{
// Friday
$payment0 = new Payment();
$payment0->setAmount(new Amount('199', 'EUR'));
$payment0->setCreated('2017-08-18 10:33:17.097996');
// Sunday
$payment1 = new Payment();
$payment1->setAmount(new Amount('199', 'EUR'));
$payment1->setCreated('2017-08-20 16:21:57.295726');
$paymentResource = $this
->createMock(PaymentResource::class)
->method('listing')
->willReturn([
$payment0,
$payment1
]);
$s = new FinanceService($paymentResource);
$this->assertEquals(199, $s->getWeekendSpending(1, 2));
}
}
This would mean that I would end up maintaining code that is domain specific to bunq but somehow physically located in the codebase of my own projects.
Ideally, the SDK is designed in a way that there is no need to invoke static methods to make API HTTP requests - this will make the DX much smoother in modern PHP projects.
PHP: 7.1
SDK Version: 0.12.1
Error: bunq\Exception\BunqException: Found model "BunqModel" which is not defined. in /vendor/bunq/sdk_php/src/Util/ModelUtil.php:60
As soon as you try to get a DraftPayment that has the status 'ACCEPTED', this DraftPayment will have the 'object' attribute set.
In the Endpoint/DraftPayment.php file is stated:
/**
* The Payment or PaymentBatch. This will only be present after the
* DraftPayment has been accepted.
*
* @var BunqModel
*/
protected $object;
The current code in ModelUtil.php doesn't allow for this since BunqModel is not a subclass of itself.
/**
* @param string $className
*
* @return bool
*/
private static function isClassSubClassOfBunqModel(string $className): bool
{
return class_exists($className) && is_subclass_of($className, BunqModel::class);
}
Quickly searching the code I think the Schedule / ScheduleInstance has the same error?
src/Model/Generated/Endpoint/MasterCardAction.php
no logs
no logs
For all listing
methods, there is no pagination.
This means
x
items from the resource.This could be resolved by a complete implementation of the pagination as documented here.
Though the SDK I can create regular payments, but there is no way to create a DraftPayment
- it seems the model is missing.
Is this correct, or is there a (hidden) option on Payment
to make it a draft instead of a regular one?
./gitattributes
file.I think using array
s for mapping is a bad practice. It makes more sense to create an object for this:
final class PaymentMap
{
const FIELD_AMOUNT = 'amount';
const FIELD_COUNTERPARTY_ALIAS = 'counterparty_alias';
const FIELD_DESCRIPTION = 'description';
const FIELD_ATTACHMENT = 'attachment';
private $amount;
private $pointer;
private $description;
private $attachments;
public function __construct(Amount $amount, Pointer $pointer, $description)
{
//... the usual stuff ...//
}
public function addAttachment(Attachment $attachment): void
{
$this->attachments[$attachment->getId()] = $attachment;
}
public function getAmount(): Amount
{
return $this->amount;
}
public function getAsArray(): array
{
return [
self::FIELD_AMOUNT => $this->amount,
self::FIELD_COUNTERPARTY_ALIAS => $this->pointer,
//... Well you get the idea ...//
];
}
}
$paymentMap = new PaymentMap(
new Amount('10.00', 'EUR'),
new Pointer('EMAIL', '[email protected]'),
This is a generated payment',
];
Payment::create($bunqApiContext, $paymentMap, $userId, $monetaryAccountId);
public static function create(ApiContext $apiContext, PaymentMap $paymentMap, $userId, $monetaryAccountId, array $customHeaders = [])
{
$apiClient = new ApiClient($apiContext);
$response = $apiClient->post(
vsprintf(
self::ENDPOINT_URL_CREATE,
[$userId, $monetaryAccountId]
),
$paymentMap->getAsArray(),
$customHeaders
);
return static::processForId($response);
}
Using this approach you restrict the programmer to fill in all the required fields, and prevent making invalid API calls.
It adds more structure to the project and gives the ability to run better tests :)
you can even unit test the whole thing now:
/**
* @small
*/
final class PaymentMapTest extends PHPUnit_Framework_TestCase
{
/** @test */
public function canCreateValidPaymentMap()
{
$amount = new Amount(10.00, 'EUR');
$pointer= new Pointer('EMAIL', '[email protected]');
$description = 'payment description';
$paymentMap = new PaymentMap($amount, $pointer, $description);
self::assertSame($amount, $paymentMap->getAmount());
self::assertSame($pointer, $paymentMap->getPointer());
self::assertSame($description, $paymentMap->getDescription());
}
/** @test */
public function canCreateValidPaymentMapParsesValidArray()
{
// duplicate code maybe extract it into a method?
$amount = new Amount(10.00, 'EUR');
$pointer= new Pointer('EMAIL', '[email protected]');
$description = 'payment description';
$paymentMap = new PaymentMap($amount, $pointer, $description);
self::assertEquals(
[
PaymentMap::FIELD_AMOUNT => $amount,
PaymentMap::FIELD_COUNTERPARTY_ALIAS => $pointer,
//... Well you get the idea ...//
],
$paymentMap->getAsArray()
);
}
/** @test */
public function canAddAttachments()
{
// duplicate code maybe extract it into a method?
$amount = new Amount(10.00, 'EUR');
$pointer= new Pointer('EMAIL', '[email protected]');
$description = 'payment description';
$paymentMap = new PaymentMap($amount, $pointer, $description);
$attachment = new Attachment();
$paymentMap->addAttachment($attachment);
// ... Well you get the idea ...//
}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.