How PDP Mobile Pays Utilities with Cards and SBP
PDP Mobile now supports two utility payment flows: cards via redirect and SBP via QR/deep link. Separate TTLs handle expired links.

On the PDP Mobile payment screen, two buttons now sit side by side: card and SBP. For utility payments, this turned out to be a requirement rather than a nice-to-have: younger users tend to choose SBP, while older users prefer bank cards.
Before: the user would get all the way to payment and could run into a method that wasn’t convenient for them. For some, it’s more natural to open their banking app via a deep link or QR code. Others expect the standard card flow with a redirect to a payment page where Visa, MasterCard, and MIR work.
After: both routes are available on the same screen, and the backend separately tracks the lifetime of each payment link.
Two scenarios instead of one compromise
For cards, we implemented payment via redirect. The user selects a card, goes to the bank’s payment page, pays with Visa, MasterCard, or MIR, and returns to the app according to the bank’s flow.
For SBP, the logic is different: we show a QR code and provide a deep link to the banking app. The user does not enter card details and instead confirms the transfer in their bank.
Technically, these are two different flows with different UX expectations:
- card — the usual redirect, where the user expects to see a separate payment page;
- SBP — a quick transition to the bank or scanning a QR code;
- both methods need to live on the same utility payment screen, so the user doesn’t have to look for the right option in another section.
The most boring, but most important, part here is not the buttons. What mattered was not mixing up payment states: the card link and the SBP link are created and expire according to their own rules.
Each method has its own TTL
Each payment method has its own TTL. This affects how long a link or QR code remains valid and when it must be considered invalid.
If an expired link stays active on the screen, the user may follow it, get rejected by the bank, and come back asking why the app offered a broken option. So we added a background job that invalidates expired links.
There was a small implementation debate here: whether to build a single cleanup mechanism for all payments or explicitly account for each method’s TTL. We chose the second option. It’s less elegant as an abstraction, but clearer when debugging incidents: it’s obvious which payment method expired and why a specific link should no longer be used.
The result is a flow without unnecessary guesswork on the client side:
- the user selects a payment method;
- the app receives a current link, QR code, or deep link;
- a background job closes expired options;
- the payment screen does not keep old links around as if they were still valid.
Quick amounts for utility payments
Utility payments often repeat the same amounts. So we added quick selection: 1000, 5000, and 10000 rubles.
It’s a small UI detail, but it removes extra input in a place where the user usually just wants to complete the payment. Especially in a mobile app: fewer taps, fewer amount entry mistakes, faster transition to the bank or card redirect.
Before: the user entered the amount manually every time and then chose an available payment method.
After: they can tap 1000, 5000, or 10000 rubles, choose card or SBP, and go straight to payment. If the link has expired, the background job removes it instead of making the user discover that on the bank’s side.
For PDP Mobile, we ended up with utility payments that have two mandatory routes: Visa, MasterCard, and MIR cards via redirect, and SBP via QR code or deep link into the banking app. The next layer to watch in integrations like this is correct status synchronization after returning from the bank.