Skip to main content

Multibanco (Portugal)

tip

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​

  1. Your server registers a payment with the Multibanco channel in EUR.
  2. dpay.pl returns the reference details (the multibanco object) and the gateway URL in the msg field.
  3. You redirect the payer to the gateway, where they see the payment details, the expiry time and instructions.
  4. The payer pays with the reference at an ATM or in online banking.
  5. dpay.pl confirms the payment with an IPN and the payment.succeeded webhook. If the reference expires unpaid, it sends the payment.failed webhook with the expired code.
POST/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
Rate limits

The payment registration endpoint supports up to 120 requests per minute.

Request parameters​

FieldTypeRequiredDescription
transactionTypestringYes"transfers"
servicestringYesService name from the dashboard
valuestringYesAmount in EUR (with a decimal point, for example "10.00")
currency_codestringYes"EUR"
channelstringYes"390" - the Multibanco channel
emailstringYes (with channel)Customer's email address
client_namestringYes (with channel)Customer's first name
client_surnamestringYes (with channel)Customer's last name
accept_tosbooleanYes (with channel)Confirmation that the customer accepts the terms and conditions
url_successstringYesURL the gateway redirects the payer to after the payment
url_failstringYesURL the gateway redirects the payer to after the reference expires
url_ipnstringNoURL for IPN notifications (sent only after a successful payment)
checksumstringYesSHA-256 checksum
Fields accompanying channel

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

EUR currency

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:

FieldTypeDescription
entitystringEntity number (Entidade) - 5 digits
referencestringReference number (Referência) - 9 digits without spaces. Show it to the payer in groups of three digits, for example 123 456 789
amountstringAmount to pay, with a decimal point
currencystringAlways EUR
expires_atstringReference 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​

EventTransaction statusIPNWebhook
Registration - the reference is waiting for paymentcreatedNo-
The payer paid the referencepaidYespayment.succeeded
The reference expired unpaidexpiredNopayment.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).

IPN only on success

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.

The payment may arrive later

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.

FieldTypeRequiredDescription
actionstringYes"pay" - simulates paying the reference, "expire" - simulates its expiry
ActionEffect
payThe transaction is paid: an IPN to url_ipn and the payment.succeeded webhook
expireThe 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
}
FieldDescription
successtrue - the action was accepted
changedtrue when the call changed the reference state; false when the reference was already paid or expired
statusReference state: pending, paid or expired
transactionStatusTransaction 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​

HTTPWhen
200The action was accepted - also when it changed nothing (see changed)
404The transaction is not a Multibanco payment or the service is not in test mode
422Unknown 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​

  1. Registration returns the multibanco object, and your application redirects the payer to the URL from msg.
  2. After the pay action, the order becomes paid based on the IPN or the payment.succeeded webhook - not based on the return to url_success.
  3. After the expire action, the order is cancelled based on the payment.failed webhook with the expired code (no IPN arrives).
  4. A repeated notification about the same payment does not change the order state a second time.

Common errors​

MessageWhere it appearsCauseSolution
Multibanco is not enabled for this service.message (HTTP 400)The Multibanco channel is not active for the serviceAsk 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 EURSend currency_code: "EUR"
Multibanco requires transactionType "transfers".message (HTTP 400)The Multibanco channel was sent with another transaction typeUse transactionType: "transfers"
Currency code "EUR" does not match the service currency code "PLN".message (HTTP 400)EUR is not enabled for the serviceAsk your dpay.pl account manager to enable EUR for the service
Field validation errorHTTP 422email, client_name, client_surname or accept_tos is missing while channel is providedAdd the missing fields
404 responseSimulation endpointThe payment is not a Multibanco payment or the service is not in test modeCheck the transactionId and the service's test mode
info

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.