Announcing mollie-api-php v3 Beta

Sander van Hooft's profile picture

Sander van Hooft

Founder

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.

What's New in v3?

The v3 release focuses on modernizing the codebase and addressing the most requested features from our community. Here are the highlights:

Fully Typed Requests

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.

Enhanced Testing Capabilities

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.

Test Mode Configuration

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.

Powerful API Mocking

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'));

Simulating Error Responses

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'
    )
]);

Working with Collections and Embedded Resources

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.

Improved Debugging

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.

Dedicated Exception Classes

Instead of handling a generic ApiException, v3 introduces dedicated exception classes based on response status codes:

  • UnauthorizedException for 401 Unauthorized responses
  • ForbiddenException for 403 Forbidden responses
  • NotFoundException for 404 Not Found responses
  • MethodNotAllowedException for 405 Method Not Allowed responses
  • RequestTimeoutException for 408 Request Timeout responses
  • ValidationException for 422 Unprocessable Entity responses
  • TooManyRequestsException for 429 Too Many Requests responses
  • ServiceUnavailableException for 503 Service Unavailable responses
  • ApiException as a fallback for other error status codes

This 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.

PSR-18 HTTP Client Compatibility

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:

  • Framework Agnostic: Use the HTTP client that best fits your application or framework
  • Dependency Reduction: Avoid adding Guzzle as a dependency if you're already using another HTTP client
  • Future Proof: As new PSR-18 compatible clients emerge, you can easily switch without changing your Mollie integration
  • Consistent Error Handling: The adapter properly translates PSR-18 exceptions into Mollie's exception hierarchy

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.

Advanced Response Features

The v3 release offers enhanced response handling capabilities that give you more control and flexibility:

Resource Hydration

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;

Access to Raw Response

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.

Custom Resource Wrappers

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.

Try It Today

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!

Sandorian Consultancy B.V.

  • Veemarktstraat 34
    5038CV Tilburg
    The Netherlands
  • KVK 84842822

Subscribe to our newsletter

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