Pay.nl Client Library¶
Overview¶
Standalone PHP client for the Pay.nl payment gateway API.
Package: atraxion/paynl-client
Installation¶
composer require atraxion/paynl-client
Configuration¶
use Atraxion\PayNl\Config;
use Atraxion\PayNl\PayNlClient;
$config = new Config(
token: 'AT-1234-5678',
serviceId: 'SL-1234-5678',
sandbox: false, // true for testing
timeout: 30
);
$client = new PayNlClient($config);
Core Features¶
Create Payment¶
use Atraxion\PayNl\Request\CreatePaymentRequest;
use Atraxion\PayNl\Model\Customer;
use Atraxion\PayNl\Model\Address;
$request = new CreatePaymentRequest(
amount: 12500, // €125.00 in cents
currency: 'EUR',
returnUrl: 'https://shop.example.com/payment/return',
exchangeUrl: 'https://shop.example.com/payment/webhook',
description: 'Order #12345',
reference: '12345',
customer: new Customer(
firstName: 'Jan',
lastName: 'Janssen',
email: 'jan@example.com',
phone: '+31612345678',
language: 'nl'
),
billingAddress: new Address(
street: 'Hoofdstraat',
houseNumber: '1',
zipCode: '1234AB',
city: 'Amsterdam',
country: 'NL'
),
paymentMethodId: 10, // iDEAL
issuerId: 'ABNANL2A' // ABN AMRO
);
$payment = $client->createPayment($request);
// Redirect customer
header('Location: ' . $payment->getPaymentUrl());
Get Payment Status¶
use Atraxion\PayNl\Enum\PaymentStatus;
$status = $client->getPaymentStatus('1234567890X12345');
if ($status->getState() === PaymentStatus::PAID) {
// Payment successful
$paidAmount = $status->getAmountPaid();
$paymentMethod = $status->getPaymentMethod();
}
Refund Payment¶
use Atraxion\PayNl\Request\RefundRequest;
$refund = $client->refund(new RefundRequest(
transactionId: '1234567890X12345',
amount: 5000, // €50.00 partial refund
description: 'Partial refund for returned item'
));
// Full refund
$fullRefund = $client->refund(new RefundRequest(
transactionId: '1234567890X12345'
// amount omitted = full refund
));
Get Payment Methods¶
$methods = $client->getPaymentMethods();
foreach ($methods as $method) {
echo $method->getId(); // 10
echo $method->getName(); // 'iDEAL'
echo $method->getMinAmount(); // 100 (€1.00)
echo $method->getMaxAmount(); // 5000000 (€50,000)
// Get issuers for iDEAL
if ($method->hasIssuers()) {
foreach ($method->getIssuers() as $issuer) {
echo $issuer->getId(); // 'ABNANL2A'
echo $issuer->getName(); // 'ABN AMRO'
}
}
}
Models¶
Payment¶
readonly class Payment
{
public function __construct(
public string $transactionId,
public string $paymentUrl,
public ?string $popupUrl = null,
public ?string $qrUrl = null
) {}
}
PaymentStatus¶
readonly class PaymentStatus
{
public function __construct(
public string $transactionId,
public PaymentStatus $state,
public int $amount,
public int $amountPaid,
public string $currency,
public ?string $paymentMethod,
public ?string $paymentMethodName,
public ?\DateTimeImmutable $paidAt,
public array $refunds = []
) {}
public function isPaid(): bool
{
return $this->state === PaymentStatus::PAID;
}
public function isPending(): bool
{
return $this->state === PaymentStatus::PENDING;
}
public function isCancelled(): bool
{
return in_array($this->state, [
PaymentStatus::CANCELLED,
PaymentStatus::EXPIRED,
PaymentStatus::DENIED
]);
}
}
PaymentStatus Enum¶
enum PaymentStatus: string
{
case PENDING = 'pending';
case PAID = 'paid';
case CANCELLED = 'cancelled';
case EXPIRED = 'expired';
case DENIED = 'denied';
case REFUND = 'refund';
case PARTIAL_REFUND = 'partial_refund';
case AUTHORIZE = 'authorize';
case CHARGEBACK = 'chargeback';
}
Webhook Handling¶
use Atraxion\PayNl\Webhook\WebhookParser;
use Atraxion\PayNl\Webhook\WebhookValidator;
// Parse incoming webhook
$parser = new WebhookParser();
$webhook = $parser->parse($_POST);
// Validate webhook signature (recommended)
$validator = new WebhookValidator($config);
if (!$validator->isValid($_POST, $_SERVER['HTTP_PAYNL_SIGNATURE'] ?? '')) {
throw new \RuntimeException('Invalid webhook signature');
}
// Handle webhook
switch ($webhook->getAction()) {
case 'pay':
// Payment received
$transactionId = $webhook->getTransactionId();
$orderId = $webhook->getOrderId();
break;
case 'refund':
// Refund processed
break;
case 'chargeback':
// Chargeback received
break;
}
// Always respond with TRUE
echo 'TRUE';
Error Handling¶
use Atraxion\PayNl\Exception\ApiException;
use Atraxion\PayNl\Exception\AuthenticationException;
use Atraxion\PayNl\Exception\ValidationException;
try {
$payment = $client->createPayment($request);
} catch (AuthenticationException $e) {
// Invalid API token
$this->logger->error('Pay.nl auth failed', ['error' => $e->getMessage()]);
} catch (ValidationException $e) {
// Invalid request parameters
$errors = $e->getValidationErrors();
} catch (ApiException $e) {
// Other API error
$this->logger->error('Pay.nl API error', [
'code' => $e->getCode(),
'message' => $e->getMessage(),
'response' => $e->getResponse()?->getBody(),
]);
}
Testing¶
Mock Responses¶
use Atraxion\PayNl\Testing\MockPayNlClient;
$mockClient = new MockPayNlClient();
$mockClient->addResponse('createPayment', new Payment(
transactionId: 'TEST123',
paymentUrl: 'https://pay.nl/test'
));
// Use in tests
$payment = $mockClient->createPayment($request);
assert($payment->transactionId === 'TEST123');
Sandbox Mode¶
$config = new Config(
token: 'AT-1234-5678',
serviceId: 'SL-1234-5678',
sandbox: true // Uses sandbox API
);
Gherkin Scenarios¶
Feature: Pay.nl Client
As a developer
I want to integrate Pay.nl payments
Using a clean client library
Scenario: Create iDEAL payment
Given valid Pay.nl credentials
When I create a payment for €125.00 with iDEAL
Then I should receive a transaction ID
And I should receive a payment URL
Scenario: Get payment status after success
Given a successful payment "TX123"
When I check the payment status
Then status should be "paid"
And paid amount should match
Scenario: Handle webhook for payment
Given a payment webhook arrives
When I parse the webhook
Then I should receive transaction details
And I should validate the signature
Scenario: Partial refund
Given a paid transaction for €100
When I refund €30
Then refund should be created
And transaction status should be "partial_refund"
Scenario: Invalid credentials
Given invalid Pay.nl credentials
When I try to create a payment
Then AuthenticationException should be thrown
Configuration Reference¶
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
token |
string | Yes | - | API token (AT-xxxx-xxxx) |
serviceId |
string | Yes | - | Service ID (SL-xxxx-xxxx) |
sandbox |
bool | No | false | Use sandbox API |
timeout |
int | No | 30 | Request timeout in seconds |
baseUrl |
string | No | auto | Override API base URL |
API Endpoints Used¶
| Endpoint | Method | Purpose |
|---|---|---|
/v2/Transaction/start |
POST | Create payment |
/v2/Transaction/status |
GET | Get status |
/v2/Transaction/refund |
POST | Create refund |
/v2/Transaction/getPaymentMethods |
GET | List methods |
/v2/Transaction/cancel |
POST | Cancel payment |