March 3, 2025
I'm excited to announce that the beta version of mollie-api-php v3 is now available! This new major version brings significant improvements to developer experience, testing capabilities, and overall robustness. Let's dive into what makes this release special.
The v3 release focuses on modernizing the codebase and addressing the most requested features from our community. Here are the highlights:
One of the most significant improvements is the introduction of fully typed request objects. While you can still use the array-based payloads you're familiar with, v3 now offers a more structured approach:
// Using typed request objects with full IDE autocompletion
$payment = $mollie->send(new CreatePaymentRequest(
amount: new Mollie\Api\Http\Data\Money(
currency: 'EUR',
value: '10.00'
),
description: 'Order #12345',
redirectUrl: 'https://example.org/order/12345/'
));
This provides you with full IDE autocompletion and enables robust static analysis, making your code more reliable and easier to maintain.
Testing your integration with Mollie has never been easier. The v3 release introduces powerful testing features that make your development workflow more efficient and reliable.
When using api keys, test mode is automatically determined by the prefix (e.g. test_
). For Oauth2 and organization tokens, test mode needs to be explicitly set however on each request.
In v3, we've made this as easy as possible by allowing you to set the test mode at the global client level or per individual request:
// Global test mode configuration
$mollie = new MollieApiClient();
$mollie->setAccessToken("access_xyz");
$mollie->test(true); // Applies to all subsequent requests
// Or per-request configuration
$createPaymentRequest = new CreatePaymentRequest(/* ... */);
$mollie->send($createPaymentRequest->test(true));
This is especially useful for OAuth2 authenticated requests, where test mode isn't automatically determined by the API key prefix.
One of the most exciting additions is the comprehensive API mocking system. You can now simulate API responses without making actual network calls, making testing your integration a breeze:
use Mollie\Api\MollieApiClient;
use Mollie\Api\Fake\MockResponse;
use Mollie\Api\Http\Requests\GetPaymentRequest;
$client = MollieApiClient::fake([
GetPaymentRequest::class => new MockResponse(
body: [
'resource' => 'payment',
'id' => 'tr_xxxxxxxxxxxx',
'mode' => 'test',
'amount' => [
'value' => '20.00',
'currency' => 'EUR'
],
'description' => 'Test',
'status' => 'open',
],
status: 200
)
]);
$payment = $client->send(new GetPaymentRequest('tr_xxxxxxxxxxxx'));
You can easily simulate API error responses to test your error handling:
// 404 Not Found
$client = MollieApiClient::fake([
GetPaymentRequest::class => MockResponse::notFound('No payment exists with token tr_xxxxxxxxxxx')
]);
// 422 Validation Error
$client = MollieApiClient::fake([
CreatePaymentRequest::class => MockResponse::unprocessableEntity(
detail: 'Amount must be at least €1.00',
field: 'amount'
)
]);
The mocking system also supports paginated collections and embedded resources:
// Create paginated list responses
$client = MollieApiClient::fake([
GetPaginatedPaymentsRequest::class => MockResponse::list(PaymentCollection::class)
->add([
'resource' => 'payment',
'id' => 'tr_xxxxxxxxxxxx',
'amount' => [
'value' => '20.00',
'currency' => 'EUR'
],
'status' => 'open',
])
->create()
]);
// Simulate embedded resources
$client = MollieApiClient::fake([
GetPaymentRequest::class => MockResponse::resource(Payment::class)
->with([
'resource' => 'payment',
'id' => 'tr_xxxxxxxxxxxx',
'amount' => [
'value' => '20.00',
'currency' => 'EUR'
]
])
->embed(RefundCollection::class)
->add([
'resource' => 'refund',
'id' => 're_12345',
'amount' => [
'value' => '10.00',
'currency' => 'EUR'
]
])
->create()
]);
Check out the complete testing documentation to learn more about these features.
Debugging API interactions is now more straightforward with enhanced debugging tools:
$mollie = new MollieApiClient;
$mollie->setApiKey('test_123');
// Will output detailed debugging information for all api calls
$mollie->debug();
// Or stop execution after debugging the first api call's output
$mollie->debug(die: true);
$mollie->payments->create();
The debugging output automatically strips sensitive information while providing comprehensive details about the request and response. See the debugging documentation for more information.
Instead of handling a generic ApiException
, v3 introduces dedicated exception classes based on response status codes:
UnauthorizedException
for 401 Unauthorized responsesForbiddenException
for 403 Forbidden responsesNotFoundException
for 404 Not Found responsesMethodNotAllowedException
for 405 Method Not Allowed responsesRequestTimeoutException
for 408 Request Timeout responsesValidationException
for 422 Unprocessable Entity responsesTooManyRequestsException
for 429 Too Many Requests responsesServiceUnavailableException
for 503 Service Unavailable responsesApiException
as a fallback for other error status codesThis allows for more precise exception handling in your application, making your error handling more robust and specific. Nevertheless, you can still rely on only catching the ApiException as all other exceptions are extending it.
In addition to supporting Guzzle and Curl, v3 now ships with a dedicated PSR-18 compatible HTTP adapter. This highly requested feature gives you more flexibility in choosing your HTTP client implementation.
The PSR-18 adapter allows you to use any HTTP client that implements the PHP-FIG PSR-18 standard, including popular libraries like Symfony HttpClient, Guzzle, and others. This means you can:
// Example using Symfony HTTP Client with PSR-18 adapter
$httpClient = Symfony\Component\HttpClient\Psr18Client();
$requestFactory = new Nyholm\Psr7\Factory\Psr17Factory();
$responseFactory = new Nyholm\Psr7\Factory\Psr17Factory();
$streamFactory = new Nyholm\Psr7\Factory\Psr17Factory();
$uriFactory = new Nyholm\Psr7\Factory\Psr17Factory();
$adapter = new Mollie\Api\Http\Adapter\PSR18MollieHttpAdapter(
$httpClient,
$requestFactory,
$responseFactory,
$streamFactory,
$uriFactory
);
$mollie = new Mollie\Api\MollieApiClient($adapter);
Benefits of using the PSR-18 adapter include:
This adapter is particularly useful for projects that already have a PSR-18 compatible HTTP client in place, allowing you to standardize on a single HTTP client implementation across your entire application.
The v3 release offers enhanced response handling capabilities that give you more control and flexibility:
By default, all API responses are automatically hydrated into the corresponding Resource
or ResourceCollection
objects, providing you with a clean, object-oriented interface to work with:
// Get a payment using either approach
$payment = $mollie->payments->get('tr_12345');
// or
$payment = $mollie->send(new GetPaymentRequest('tr_12345'));
// Access properties in an object-oriented way
echo $payment->status;
echo $payment->amount->value;
echo $payment->description;
While working with hydrated objects, you can still access the underlying raw response when needed:
$payment = $mollie->payments->get('tr_12345');
// Access the raw Response object
$response = $payment->getResponse();
// Get the raw JSON
$jsonBody = $response->body();
// Check HTTP status code
$statusCode = $response->status();
// Examine headers
$contentType = $response->header('Content-Type');
This gives you the best of both worlds - the convenience of object-oriented access plus the ability to inspect the raw HTTP details when needed.
One of the most powerful new features is the ability to create custom resource wrappers. These allow you to transform API responses into your own domain-specific objects:
use Mollie\Api\Utils\Utility;
use Mollie\Api\Resources\Payment;
use Mollie\Api\Resources\ResourceWrapper;
class PaymentWrapper extends ResourceWrapper
{
public function __construct(
public Money $amount,
public Timestamp $createdAt,
) {}
public static function fromResource($resource): self
{
/** @var Payment $resource */
return (new self(
amount: Utility::transform($resource->amount, fn ($amount) => Money::fromMollieObject($amount)),
createdAt: Utility::transform($resource->createdAt, fn ($timestamp) => Timestamp::fromIsoString($timestamp))
))->setWrapped($resource);
}
}
You can then use your custom wrapper when making API requests:
use Mollie\Api\Resources\WrapperResource;
$request = new GetPaymentRequest('tr_12345');
$request->setHydratableResource(new WrapperResource(PaymentWrapper::class));
/** @var PaymentWrapper $paymentWrapper */
$paymentWrapper = $mollie->send($request);
// Use your custom properties
echo $paymentWrapper->amount->formatForDisplay();
echo $paymentWrapper->createdAt->humanReadable();
// Still access original resource properties
echo $paymentWrapper->status;
This feature is particularly useful when integrating Mollie with your domain model, allowing you to transform API responses directly into your application's data structures without additional mapping code.
Learn more about these features in the responses documentation.
Ready to experience these improvements? Install the beta version with Composer:
composer require "mollie/mollie-api-php:^3.0.0-beta"
We're eager to hear your feedback on this beta release. Please report any issues or suggestions on our GitHub repository.
Stay tuned for more updates as we move toward the stable release of v3!
The latest news, articles and resources sent to your inbox.
Sandorian is a trademark of Sandorian Holding B.V.
© 2025 Sandorian.com • All rights reserved