
Banco Digital Público
Introduction
Banco Digital Público (BDP) was a project for the Colombian government that aimed to deliver state subsidies in a more efficient way. Instead of money, the beneficiary received tokens in a digital wallet and could redeem them in the participating shops, with discounts.
We built the mobile app, for Android and iOS. The tokenization core and the banking services were provided by other teams, and we integrated with them through an API.

What the app did
- Sign-up for people and companies. People signed up with their phone number and verified their identity with their national ID card, through VU's KYC SDK. Companies signed up with their tax ID (NIT) and registered their shareholders.
- Subsidies. Each subsidy had its token balance and its transactions, and the tokens could be converted into pesos and moved to the bank account.
- QR payments. The shop generated a QR code with the amount to charge and the user scanned it to pay. The app only showed the accounts that shop accepted, whether a subsidy or the bank account. The shop could refund a payment.
- Transfers to other users by phone number and to accounts at any bank, with recent contacts and receipts to share.
- Bank account. From the app you could open a low-value account at the Coopcentral bank. To upgrade to a regular account, without the limits of the low-value one, the user filled in the risk assessment form the bank required: personal, employment and financial data, tax information, assets, international operations, references, anti-money laundering statements and documents. Companies opened their account with an equivalent form, with the company's data. At the end, the app generated the application as a PDF.
- Account management. Transaction limits, blocking the account or part of the balance, and closing it.
- Shops. Shops registered with their photo and their business activity, and had their own QR code to charge. Users could search for them by department and city.
- Cards. A virtual card issued with Pomelo, which could be viewed, blocked and replaced, and a request for the physical card.
Technical approach
I led the project technically: I defined the architecture, led the team and coded almost all of the app.
The app was built in React Native with Expo and TypeScript, on top of
my open source libraries (react-presenter, cqbus-lite, money, validator, among others).
Architecture
I used the same architecture as in the Culti frontend: a lighter version of Clean Architecture. An app like this has little business logic of its own, but a lot of view logic (long forms, validations, rules about which account can be used in each operation), and mobile technology changes fast. Separating the layers lets you change one without touching the others.


- React only rendered. Screens had no logic: they showed what the ViewModel said and told the Presenter when the user did something. I barely use hooks or state management libraries.
- Presenters. View logic lived in Presenters: pure TypeScript classes with no dependency on React. The Presenter ran the use cases, validated, formatted the data and exposed a ViewModel ready to display. For example, the payment Presenter fetched the shop, checked which accounts it accepted, built the list with the balances already formatted and decided which screen to go to if something failed.
- Use cases. Each operation was a command or a query that went through a bus (
cqbus-lite) to its handler. Presenters didn't know whether the operation was local or went to the API. - App Services. Session, authentication, navigation, biometrics, KYC and analytics were behind interfaces. Their implementations (Expo, Firebase, the VU SDK) lived in the infrastructure layer.
This way, changing the navigation library or the analytics provider meant changing one implementation, and the logic of each screen could be read and tested without starting the UI.
Other decisions
- The API was being developed by another team in parallel. To not depend on their timing we worked against the contract: we published the Swagger specification and a mock server, so the whole app could be tested before each endpoint existed.
- VU's KYC SDK is native. To integrate it without leaving Expo I wrote a config plugin that adds its dependencies to the Android build.
- For security, the app was unlocked with fingerprint or face, didn't run on rooted devices, allowed only one active session per user and forced an update when a new version came out.
- A GitHub Actions pipeline ran the tests and generated the builds for the QA, pre-production and production environments.