PHP code example of chuckbe / ponto-connect-laravel-sdk
1. Go to this page and download the library: Download chuckbe/ponto-connect-laravel-sdk library . Choose the download type require .
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
chuckbe / ponto-connect-laravel-sdk example snippets
use PontoConnect\Facades\PontoConnect;
PontoConnect::api(); // no token: public endpoints and the OAuth token endpoints
PontoConnect::withAccessToken($accessToken); // customer (or client) access token
use PontoConnect\Facades\PontoConnect;
use PontoConnect\Support\Pkce;
$pkce = Pkce::generate(); // ['verifier' => ..., 'challenge' => ...]
$state = Str::random(40);
session(['ponto.state' => $state, 'ponto.verifier' => $pkce['verifier']]);
return redirect()->away(PontoConnect::authorizationUrl(
redirectUri: route('integrations.ponto.callback'),
state: $state,
codeChallenge: $pkce['challenge'],
language: 'nl', // optional: en, fr or nl
// scopes: ['ai', 'name', 'offline_access'], // optional override of the configured scopes
// onboardingDetailsId: $onboardingDetails->id, // optional, see below
));
$clientToken = PontoConnect::api()->oauth->clientCredentials();
$onboardingDetails = PontoConnect::withAccessToken($clientToken->accessToken)
->onboardingDetails
->create([
'email' => $user->email,
'firstName' => $user->first_name,
'lastName' => $user->last_name,
'organizationName' => $company->name,
'vatNumber' => $company->vat_number,
'addressCountry' => 'BE',
]);
abort_unless(hash_equals(session('ponto.state'), $request->query('state')), 403);
$token = PontoConnect::api()->oauth->exchangeAuthorizationCode(
code: $request->query('code'),
redirectUri: route('integrations.ponto.callback'),
codeVerifier: session()->pull('ponto.verifier'),
);
$userInfo = PontoConnect::withAccessToken($token->accessToken)->userInfo->get();
$connection->update([
'organization_id' => $userInfo->sub, // store it: needed for usage and integration deletion
'refresh_token' => $token->refreshToken, // encrypted cast
'access_token' => $token->accessToken,
'access_token_expires_at' => $token->expiresAt,
]);
use Illuminate\Support\Facades\Cache;
use PontoConnect\Exceptions\InvalidGrantException;
$accessToken = Cache::lock("ponto-refresh:{$connection->id}", 30)->block(10, function () use ($connection) {
$connection->refresh();
if ($connection->access_token_expires_at->isFuture()) {
return $connection->access_token; // another process refreshed in the meantime
}
try {
$token = PontoConnect::api()->oauth->refresh($connection->refresh_token);
} catch (InvalidGrantException $exception) {
$connection->markRevoked(); // used, expired (90 days) or revoked by the customer
throw $exception;
}
$connection->update([ // persist immediately, outside any DB transaction
'refresh_token' => $token->refreshToken,
'access_token' => $token->accessToken,
'access_token_expires_at' => $token->expiresAt,
]);
return $token->accessToken;
});
$ponto = PontoConnect::withAccessToken($accessToken);
$page = $ponto->account->list(limit: 50);
foreach ($page as $account) {
echo $account->reference; // IBAN
echo $account->currentBalance; // "1234.50" (string)
echo $account->latestSynchronization?->status;
}
$nextPage = $ponto->account->list(limit: 50, after: $page->nextCursor);
// Or walk every page lazily (one request per page, 100 items per page)
foreach ($ponto->transaction->all($accountId) as $transaction) {
$transaction->amount; // "-85.00"
$transaction->remittanceInformation; // "+++090/9337/55493+++"
$transaction->remittanceInformationType; // "structured"
$transaction->valueDate; // DateTimeImmutable (UTC)
}
$ponto->pendingTransaction->list($accountId); // replace your stored pending transactions each time
$ponto->account->revoke($accountId); // remove an account from the integration
use PontoConnect\Enums\SynchronizationSubtype;
$synchronization = $ponto->synchronization->create(
resourceId: $accountId,
subtype: SynchronizationSubtype::ACCOUNT_TRANSACTIONS,
customerIpAddress: $request->ip(),
idempotencyKey: (string) Str::uuid(), // optional, any POST/PATCH accepts one
);
$synchronization = $ponto->synchronization->get($synchronization->id);
if ($synchronization->isSuccessful()) {
foreach ($ponto->transaction->allUpdatedForSynchronization($synchronization->id) as $transaction) {
// new or changed transactions
}
}
if ($synchronization->hasFailed()) {
$synchronization->errorCodes(); // e.g. ['authorizationExpired']
}
$reauthorization = $ponto->reauthorizationRequest->create(
accountId: $accountId,
redirectUri: route('integrations.ponto.reauthorized'),
);
return redirect()->away($reauthorization->redirectUrl);
use Illuminate\Support\Facades\Cache;
use PontoConnect\Enums\WebhookEventType;
use PontoConnect\Exceptions\UnknownWebhookKeyException;
use PontoConnect\Exceptions\WebhookVerificationException;
public function __invoke(Request $request)
{
$verifier = PontoConnect::webhookVerifier();
$signature = (string) $request->header('Signature');
$rawBody = $request->getContent(); // the raw body, not the parsed JSON
try {
try {
$keys = Cache::remember('ponto-connect.webhook-keys', 3600, fn () => PontoConnect::api()->webhook->keys());
$event = $verifier->verify($signature, $rawBody, $keys);
} catch (UnknownWebhookKeyException) {
$keys = PontoConnect::api()->webhook->keys(); // Ponto rotated its signing keys
Cache::put('ponto-connect.webhook-keys', $keys, 3600);
$event = $verifier->verify($signature, $rawBody, $keys);
}
} catch (WebhookVerificationException) {
abort(400);
}
if (! Cache::add("ponto-connect.webhook-jti.{$event->jti}", true, now()->addDay())) {
return response()->noContent(); // replayed
}
if ($event->is(WebhookEventType::ACCOUNT_TRANSACTIONS_CREATED)) {
SyncPontoTransactions::dispatch($event->organizationId, $event->accountId, $event->synchronizationId);
}
return response()->noContent(); // answer within 5 seconds, do the work in a job
}
$fakeAccounts = $ponto->sandbox->listFinancialInstitutionAccounts($financialInstitutionId);
$ponto->sandbox->createFinancialInstitutionTransaction(
financialInstitutionId: $financialInstitutionId,
financialInstitutionAccountId: $fakeAccounts->items[0]->id,
attributes: [
'valueDate' => now(),
'executionDate' => now(),
'amount' => '121.00',
'currency' => 'EUR',
'description' => 'Payment invoice 2026-0042',
'remittanceInformationType' => 'structured',
'remittanceInformation' => '+++090/9337/55493+++',
'counterpartName' => 'Acme BV',
'counterpartReference' => 'BE68539007547034',
],
);
$client = PontoConnect::withAccessToken(PontoConnect::api()->oauth->clientCredentials()->accessToken);
$client->usage->get($organizationId, '2026-09'); // accountCount is a decimal string (prorated)
$client->integrationAccount->all(); // every account linked to your application
$client->integration->delete($organizationId); // revoke, even without a valid refresh token
try {
$ponto->account->get($accountId);
} catch (PontoConnectException $exception) {
$exception->status; // 404
$exception->errorCodes(); // the `code` of each error in the response
$exception->errors; // [['code' => ..., 'detail' => ..., 'meta' => [...]]]
$exception->requestId; // ibanity-request-id header, quote it to Ponto support
}
bash
php artisan vendor:publish --tag=ponto-connect-config
config/ponto-connect.php # Configuration file
src/
Api/
PontoConnectApiClient.php # Saloon connector: mTLS, auth, error mapping, resources
Requests/ # One Saloon request per endpoint, grouped per resource
Resources/ # Fluent resources returning DTOs and pages
Data/ # Readonly DTOs with fromArray()/toArray(), Page
Enums/ # Environment, Scope, Language, SynchronizationSubtype, ...
Exceptions/ # PontoConnectException, InvalidGrantException, webhook exceptions
Facades/PontoConnect.php
Support/ # JsonApiResource, Decimal, DateParser, Pkce
Webhooks/ # WebhookVerifier, WebhookEvent
Configuration.php # Immutable application settings
PontoConnectManager.php
PontoConnectServiceProvider.php