BLIK payin
The payer gives a BLIK code from their banking app, then confirms the payment in the app. PLN only.
Use the key of a payin shop; a payout shop key gets 403 method_not_enabled. Rules shared by every
method — Authentication, Idempotency,
Errors, Webhooks — apply here and are
not repeated.
Flow
- Ask the payer for the BLIK code. Generate
merchant_referenceand save it with your order. - Create the payin:
POST /v2/payins/blik. Saveidfrom the response. -
Read
next_action: tell the payer to confirm the payment in the banking app (await_confirmation), or send them tonext_action.redirect.url(redirect). Which one comes is set for the shop. -
Wait for the webhook
payin.completedorpayin.failed. Verify the signature and act ondata.object— the full payin. -
Fallback: if no webhook has come for a minute, you can start polling the status with
GET /v2/transactions/{transaction_id}until it is final. Polling instead of webhooks loads the API and gets429 rate_limited(R20).
Endpoints
| Step | Endpoint | Operation |
|---|---|---|
| Create | POST /v2/payins/blik |
createBlikPayin |
| Read one | GET /v2/transactions/{transaction_id} — common for all transactions, see Read one transaction |
retrieveTransaction |
Refunds of BLIK payins return 501 not_supported_yet — see Refunds.
The create has its own section below. Reading returns the Payin object.
POST /v2/payins/blik
Creates a payin and waits up to 10 seconds for the payer step, so the response usually carries
next_action.
Request
Headers: Authorization: Bearer <payin shop key>, Content-Type: application/json.
| Field | Type | Required | Format and limits |
|---|---|---|---|
merchant_reference |
string | yes | 1–100 letters, digits, _, -. Unique per merchant across payins, refunds, payouts and pay sessions. The idempotency key of this call |
amount |
object | yes | |
amount.value |
string | yes | Major units, at most two decimals, at least 0.01: "120.00". A string, never a number |
amount.currency |
string | yes | PLN, the shop currency |
payer |
object | yes | The person who pays |
payer.first_name |
string | yes | 2–255 Latin letters, spaces, ., ', - |
payer.last_name |
string | yes | 2–255 Latin letters, spaces, ., ', - |
payer.email |
string | yes | Email address |
payer.ip_address |
string | yes | IPv4 or IPv6 of the payer |
payer.phone |
string | no | +48500100200 |
blik |
object | yes | |
blik.code |
string | yes | The BLIK code from the payer's banking app |
curl -X POST https://payments-api.mrcr.io/v2/payins/blik \
-H "Authorization: Bearer $PAYIN_SHOP_KEY" \
-H "Content-Type: application/json" \
-d '{
"merchant_reference": "order-40001",
"amount": { "value": "120.00", "currency": "PLN" },
"payer": { "first_name": "Jan", "last_name": "Kowalski", "email": "jan@example.com", "ip_address": "203.0.113.20" },
"blik": { "code": "777123" }
}'
Response 201 Created
The Payin object in data. Usually requires_action with next_action; if the
payer step is not ready in 10 seconds — processing, and the step comes by the
payin.requires_action webhook. A repeat of an earlier call returns the payin in
its current status.
| Header | Meaning |
|---|---|
Idempotent-Replayed: true |
The same merchant_reference and body were sent before. This is the existing payin in its current status; nothing was created again |
{
"data": {
"id": "5a1e9d7f-2b6c-4d3a-8f8e-0c7b3d6a9fc5",
"object": "payin",
"merchant_reference": "order-40001",
"method_type": "payin_blik",
"status": "requires_action",
"status_details": null,
"amount": { "value": "120.00", "currency": "PLN" },
"refunded_amount": { "value": "0.00", "currency": "PLN" },
"next_action": { "type": "await_confirmation" },
"created_at": "2026-09-29T09:30:00Z",
"updated_at": "2026-09-29T09:30:02Z"
}
}
Errors
No payin is created on any of them.
| HTTP | code |
When | Retry |
|---|---|---|---|
| 400 | validation_failed |
A field is missing or malformed — details[] names it. Also: the amount has more than two decimals or is not in PLN |
No. Fix the request |
| 401 | authentication_failed |
The key is missing, malformed, revoked, or sent to the wrong host | No |
| 403 | method_not_enabled |
BLIK is not enabled for the shop, or the key is of a payout shop | No. Use the payin shop key, or ask Mercuryo to enable the method |
| 409 | merchant_reference_conflict |
The reference was used with a different body | No. Resend the original body, or use a new reference for a new payin |
| 429 | rate_limited |
Too many requests | Yes, after Retry-After seconds |
| 500 | internal_error |
Failure on our side | Yes, the identical request |
After a timeout or a dropped connection the payin may or may not exist. Resend the identical request (R4), never with a new reference.
Payer step: confirm the payment
The payin waits in requires_action until the payer confirms or rejects the payment in the banking
app. next_action.type is set for the shop:
next_action.type |
What you do |
|---|---|
await_confirmation |
Tell the payer to confirm the payment in the banking app. Keep the page open until the final status |
redirect |
Send the payer to next_action.redirect.url — the payment page — as a full page or a new window |
How to show it in a browser or an app — Payer actions.
The Payin object
Returned by the create and by GET /v2/transactions/{transaction_id}, and sent in webhooks as
data.object. It has the common fields of
every transaction, with these values and fields for a BLIK payin:
| Field | Type | Always set | Meaning |
|---|---|---|---|
id |
string | yes | UUID. Use it in GET /v2/transactions/{transaction_id} |
object |
string | yes | payin |
method_type |
string | yes | payin_blik |
status |
string | yes | created, processing, requires_action, completed, failed — see Statuses |
status_details.code |
string | when failed |
See Decline reasons |
refunded_amount |
object | yes | "0.00" in PLN |
next_action |
object or null |
yes | Set in requires_action, null in every other status |
next_action.type |
string | in requires_action |
await_confirmation or redirect |
next_action.redirect.url |
string | when type is redirect |
The payment page |
Statuses
| Status | What it means | The money | Webhook | What you do | Next |
|---|---|---|---|---|---|
created |
Accepted and recorded | — | — | Wait | processing, failed |
processing |
Checks run, the payin goes to the bank | — | — | Wait | requires_action, failed |
requires_action |
Waiting for the payer to confirm in the banking app | — | payin.requires_action |
Show the payer step from next_action |
completed, failed |
completed |
Final. Charged | Charged | payin.completed |
Fulfil the order | — |
failed |
Final. Not charged, including a payer who rejected. status_details.code says why |
— | payin.failed |
Act on the decline reason | — |
- Only
completedandfailedare outcomes. A final status never changes. -
requires_actionusually comes in the create response; thepayin.requires_actionwebhook comes as well. - An unknown status is not final — keep waiting (R13).
Decline reasons
status_details.code on a failed BLIK payin. Every decline is final: a new attempt is a new payin
with a new merchant_reference and a new BLIK code.
| Code | When | What you do |
|---|---|---|
method_unavailable |
BLIK is not available right now | Try again later with a new payin |
risk_rejected |
Rejected by antifraud | Do not retry automatically |
provider_declined |
The bank declined, or the code is wrong or used | Ask the payer for a new code |
canceled_by_payer |
The payer rejected the payment in the app, or the code ran out | Ask the payer whether to try again |
internal_error |
Failure on our side; nothing was charged | Try again with a new payin. Quote id to support if it repeats |
An unknown code: handle it like provider_declined.
Webhooks
type |
When | data.object |
|---|---|---|
payin.requires_action |
The payin waits for the payer to confirm | The Payin object, with next_action |
payin.completed |
The payin reaches completed |
The Payin object |
payin.failed |
The payin reaches failed |
The Payin object, with status_details |
Headers, signature check and redelivery — Webhooks.
In the sandbox
blik.code: any six digits. The cents of amount.value select the outcome:
| Cents | Example | Outcome |
|---|---|---|
| any other | 120.00 |
requires_action, then completed after 5 seconds |
.51 |
120.51 |
failed — provider_declined |
.52 |
120.52 |
failed — risk_rejected |
.55 |
120.55 |
failed — method_unavailable |
.56 |
120.56 |
failed — internal_error |
.58 |
120.58 |
requires_action, then failed — canceled_by_payer |
Sandbox keys and fixtures — Sandbox.
Next: Hosted payment form