Double-entry is not the hard part of a virtual-account ledger.
What breaks things is money that lands with no clear owner.
We have built per-user virtual accounts on account-to-account flows, including products where one user holds two positions at the same time, one for borrowing and one for investing. The accounting model is the easy half. The incoming transfer is the difficult half, because attribution usually rests on payment requisites and a free-text reference, and people mistype it, pay from a relative's account, or send one transfer meant to cover two different things.
What worked for us was refusing to guess.
Arriving funds get booked straight into an unattributed position that belongs to nobody. Attribution after that is a separate decision, and we record it: either an automatic rule match on requisites, or an operator action in moderation tooling. It is reversible, and you can see it in reconciliation against the bank side instead of in someone's spreadsheet.
The tempting shortcut is fuzzy matching on sender name similarity. In our experience that belongs on the operator's screen as a suggestion, and not in the credit path. In a lending product a wrong credit does not stay a bookkeeping error for long: it becomes somebody's funded deal, and unwinding it touches the counterparty too.
If you run virtual accounts, someone has to own the unattributed bucket, whether that is engineering or finance. We have seen it go badly both ways when nobody owns it at all.