Multibanco (Portugal)
Accepting this payment channel requires an additional agreement with the acquiring agent designated by dpay.pl.
Multibanco is a popular reference-based payment method in Portugal. After you register a payment, dpay.pl issues the payment details: the entity number (Entidade), the reference number (Referência) and the amount. The payer uses them to pay at a Multibanco ATM or in their bank's online or mobile banking (the services and purchases payment option - Pagamentos de Serviços/Compras).
The payment result is asynchronous. The payer can pay right away, but also hours or days later - until the reference expires. You learn that the reference was paid or expired from notifications, not from the registration response.
How it works
- Your server registers a payment with the Multibanco channel in EUR.
- dpay.pl returns the reference details (the
multibancoobject) and the gateway URL in themsgfield. - You redirect the payer to the gateway, where they see the payment details, the expiry time and instructions.
- The payer pays with the reference at an ATM or in online banking.
- dpay.pl confirms the payment with an IPN and the
payment.succeededwebhook. If the reference expires unpaid, it sends thepayment.failedwebhook with theexpiredcode.
/api/v1_0/payments/registerComplete payment registration contract: parameters, checksum, and error codes.Full contract in the API Reference
Endpoint
POST https://api-payments.dpay.pl/api/v1_0/payments/register
Content-Type: application/json
The payment registration endpoint supports up to 120 requests per minute.
Request parameters
| Field | Type | Required | Description |
|---|---|---|---|
transactionType | string | Yes | "transfers" |
service | string | Yes | Service name from the dashboard |
value | string | Yes | Amount in EUR (with a decimal point, for example "10.00") |
currency_code | string | Yes | "EUR" |
channel | string | Yes | "390" - the Multibanco channel |
email | string | Yes (with channel) | Customer's email address |
client_name | string | Yes (with channel) | Customer's first name |
client_surname | string | Yes (with channel) | Customer's last name |
accept_tos | boolean | Yes (with channel) | Confirmation that the customer accepts the terms and conditions |
url_success | string | Yes | URL the gateway redirects the payer to after the payment |
url_fail | string | Yes | URL the gateway redirects the payer to after the reference expires |
url_ipn | string | No | URL for IPN notifications (sent only after a successful payment) |
checksum | string | Yes | SHA-256 checksum |
Providing the channel field also requires email, client_name, client_surname, and accept_tos. If any of them is missing, the request fails validation (HTTP 422).
Multibanco accepts EUR only - any other currency fails registration. The service must have EUR enabled. If it does not, registration returns Currency code "EUR" does not match the service currency code "PLN". - in that case, contact your dpay.pl account manager.
The checksum is calculated in the same way as for any payment registration (details: Checksum):
sha256({service}|{SecretHash}|{value}|{url_success}|{url_fail}|{url_ipn})
Request example
cURL
curl -X POST https://api-payments.dpay.pl/api/v1_0/payments/register \
-H "Content-Type: application/json" \
-d '{
"transactionType": "transfers",
"service": "abc123",
"value": "10.00",
"currency_code": "EUR",
"channel": "390",
"email": "customer@example.com",
"client_name": "Joao",
"client_surname": "Silva",
"accept_tos": true,
"url_success": "https://myshop.example/success",
"url_fail": "https://myshop.example/failure",
"url_ipn": "https://myshop.example/api/ipn",
"checksum": "e3b0c44298fc1c149afb..."
}'
PHP
<?php
$service = getenv('DPAY_SERVICE');
$secretHash = getenv('DPAY_SECRET_HASH');
$value = '10.00';
$urlSuccess = 'https://myshop.example/success';
$urlFail = 'https://myshop.example/failure';
$urlIpn = 'https://myshop.example/api/ipn';
$checksum = hash('sha256',
$service . '|' . $secretHash . '|' . $value . '|' .
$urlSuccess . '|' . $urlFail . '|' . $urlIpn
);
$payload = json_encode([
'transactionType' => 'transfers',
'service' => $service,
'value' => $value,
'currency_code' => 'EUR',
'channel' => '390', // Multibanco
'email' => $_POST['email'],
'client_name' => $_POST['client_name'],
'client_surname' => $_POST['client_surname'],
'accept_tos' => true,
'url_success' => $urlSuccess,
'url_fail' => $urlFail,
'url_ipn' => $urlIpn,
'checksum' => $checksum,
]);
$ch = curl_init('https://api-payments.dpay.pl/api/v1_0/payments/register');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
if ($result['status'] === true && !$result['error']) {
// Store the reference with the order - useful, for example, in the email to the payer
saveMultibancoReference(
$result['transactionId'],
$result['multibanco']['entity'],
$result['multibanco']['reference'],
$result['multibanco']['expires_at'],
);
header('Location: ' . $result['msg']);
exit;
}
API response
Success
{
"error": false,
"msg": "https://gateway.dpay.pl/pt/pay/42191111-A7AE-392E-8C09-7965C1DC6B0B",
"status": true,
"transactionId": "42191111-A7AE-392E-8C09-7965C1DC6B0B",
"multibanco": {
"entity": "99999",
"reference": "123456789",
"amount": "10.00",
"currency": "EUR",
"expires_at": "2026-10-18T12:00:00+00:00"
}
}
The status field is a boolean (true/false), and msg contains the gateway URL you redirect the payer to. Treat it as a ready-made URL - do not build it yourself. The transactionId field identifies the payment in notifications, in status queries and in the simulation.
The multibanco object:
| Field | Type | Description |
|---|---|---|
entity | string | Entity number (Entidade) - 5 digits |
reference | string | Reference number (Referência) - 9 digits without spaces. Show it to the payer in groups of three digits, for example 123 456 789 |
amount | string | Amount to pay, with a decimal point |
currency | string | Always EUR |
expires_at | string | Reference expiry time (ISO 8601). After it, the reference can no longer be paid |
The payer pays outside your website, often later. You can also show the reference details yourself, for example on the thank-you page or in the order confirmation email.
Registration error (HTTP 400)
Errors detected during registration use the following response format:
{
"status": "failed",
"message": "Multibanco supports only currency_code \"EUR\".",
"errors": []
}
Redirecting the payer to the gateway
On the gateway, the payer sees:
- the entity number (Entidade) and the reference number (Referência), with copy buttons,
- the amount (Montante) and the expiry time (Válido até),
- short instructions for paying at a Multibanco ATM or in online banking.
The gateway checks the payment state on its own. After the payment, it redirects the payer to url_success, and after the reference expires - to url_fail. The payer does not have to wait on the gateway: they can close it and pay later, so a return to your website is not a payment confirmation.
Statuses and notifications
| Event | Transaction status | IPN | Webhook |
|---|---|---|---|
| Registration - the reference is waiting for payment | created | No | - |
| The payer paid the reference | paid | Yes | payment.succeeded |
| The reference expired unpaid | expired | No | payment.failed with the expired code |
A Multibanco transaction does not move through the processing state - it stays in created until the reference is paid or expires (state descriptions: Transaction statuses).
The IPN carries no status and is sent only after a successful payment. You learn about an expired reference from the payment.failed webhook (code expired) or by querying the transaction status. For Multibanco, we therefore recommend webhooks.
In webhooks, the payment method contains the reference details:
"payment_method": {
"type": "multibanco",
"multibanco": { "entity": "99999", "reference": "123456789" }
}
Reference expiry
A reference is valid for 72 hours from registration. The exact expiry time is returned in multibanco.expires_at - rely on it rather than on a fixed value. No more than a few minutes after that time, dpay.pl invalidates the reference, cancels the transaction and sends the payment.failed webhook with the expired code.
Do not cancel an order before expires_at just because the payer did not pay right away. If you reserve stock, keep the reservation until the reference expires.
Testing
In test mode, the reference is simulated. You trigger the payment result yourself - with the simulation endpoint or the buttons on the gateway. Until then, the transaction waits for payment, and once the reference expires it is cancelled just as in production.
Simulation endpoint
POST https://api-payments.dpay.pl/api/v1_0/payments/transaction/{transactionId}/multibanco/simulate
Content-Type: application/json
Accept: application/json
Replace {transactionId} with the transactionId from the registration response. The endpoint does not require a checksum and works only for services in test mode.
| Field | Type | Required | Description |
|---|---|---|---|
action | string | Yes | "pay" - simulates paying the reference, "expire" - simulates its expiry |
| Action | Effect |
|---|---|
pay | The transaction is paid: an IPN to url_ipn and the payment.succeeded webhook |
expire | The transaction is cancelled: the payment.failed webhook with the expired code, no IPN |
Example: paying the reference
curl -X POST https://api-payments.dpay.pl/api/v1_0/payments/transaction/42191111-A7AE-392E-8C09-7965C1DC6B0B/multibanco/simulate \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"action": "pay"}'
Example: expiring the reference
curl -X POST https://api-payments.dpay.pl/api/v1_0/payments/transaction/42191111-A7AE-392E-8C09-7965C1DC6B0B/multibanco/simulate \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"action": "expire"}'
Response (HTTP 200)
{
"success": true,
"changed": true,
"status": "paid",
"transactionStatus": 1
}
| Field | Description |
|---|---|
success | true - the action was accepted |
changed | true when the call changed the reference state; false when the reference was already paid or expired |
status | Reference state: pending, paid or expired |
transactionStatus | Transaction status as a number: 0 - waiting for payment, 1 - paid, -1 - cancelled |
The endpoint is idempotent. Calling it again for a reference that is already paid or expired changes nothing and returns changed: false with the current state - for example, expire after pay returns "status": "paid".
Response codes
| HTTP | When |
|---|---|
200 | The action was accepted - also when it changed nothing (see changed) |
404 | The transaction is not a Multibanco payment or the service is not in test mode |
422 | Unknown action value - only pay and expire are allowed |
Simulation buttons on the gateway
In test mode, the gateway shows a test mode banner and two buttons: one to pay and one to expire the reference. They work in the same way as the simulation endpoint, and the gateway refreshes the state right away and redirects the payer to url_success or url_fail. This is a convenient way to walk through the whole payer journey by hand.
What to check before going live
- Registration returns the
multibancoobject, and your application redirects the payer to the URL frommsg. - After the
payaction, the order becomes paid based on the IPN or thepayment.succeededwebhook - not based on the return tourl_success. - After the
expireaction, the order is cancelled based on thepayment.failedwebhook with theexpiredcode (no IPN arrives). - A repeated notification about the same payment does not change the order state a second time.
Common errors
| Message | Where it appears | Cause | Solution |
|---|---|---|---|
Multibanco is not enabled for this service. | message (HTTP 400) | The Multibanco channel is not active for the service | Ask your dpay.pl account manager to activate the Multibanco channel for the service |
Multibanco supports only currency_code "EUR". | message (HTTP 400) | currency_code is missing or is not EUR | Send currency_code: "EUR" |
Multibanco requires transactionType "transfers". | message (HTTP 400) | The Multibanco channel was sent with another transaction type | Use transactionType: "transfers" |
Currency code "EUR" does not match the service currency code "PLN". | message (HTTP 400) | EUR is not enabled for the service | Ask your dpay.pl account manager to enable EUR for the service |
| Field validation error | HTTP 422 | email, client_name, client_surname or accept_tos is missing while channel is provided | Add the missing fields |
404 response | Simulation endpoint | The payment is not a Multibanco payment or the service is not in test mode | Check the transactionId and the service's test mode |
A Multibanco reference can be paid by a payer with an account at a bank operating in Portugal - at a Multibanco ATM or in online or mobile banking.