"How hard can it be? The bank gives us an API. We call it. Done."
If you have ever said this in a planning meeting, this post is for you.
Integrating directly with a bank looks like the cheapest, most hands-on option: no middleman, no extra fees, full control. But the invoice never shows up as a line item. It shows up as missed deadlines, burned-out engineers, and a product roadmap that quietly stops moving.
Let's break down where the real costs hide.
1. The Time Cost: It's Never Just One Integration
Onboarding takes longer than coding
Before you write a single line of code, you typically need to:
Sign a commercial agreement and pass the bank's KYC/KYB process
Get your legal entity, business license, and use case approved
Request sandbox credentials (often via email, sometimes via a PDF form)
Complete security reviews, IP whitelisting, and certificate exchange
This can take weeks or months, and most of it is waiting, not building. Your engineers can't speed it up.
Every bank is a different snowflake
Once you're in, you discover there is no such thing as "the bank API". There are dozens of them, and they disagree on almost everything:
If your customers use more than one bank, you are not building one integration. You are building and maintaining N of them, each with its own quirks, its own docs (often outdated), and its own release cycle.
Testing is painful
Sandboxes are frequently incomplete. They don't simulate timeouts, duplicate callbacks, partial failures, or settlement delays. So you find out how the bank really behaves in production, with real money.
2. The Money Cost: Beyond the Bank's Fee Schedule
The bank may charge you little or nothing for API access. Your actual spend is elsewhere:
Engineering salaries. Multiple engineers for months is a real budget line, even if it never appears as "bank integration" in finance's spreadsheet.
Security and compliance. Certificate management, key rotation, audit logs, penetration testing, and possibly PCI or local regulatory requirements.
Infrastructure. Static IPs, VPN or dedicated connections, HSMs or secure key storage, monitoring and alerting.
Reconciliation tooling. Matching bank statements to your internal orders is a product in itself.
Failed and stuck transactions. Money in limbo means support tickets, manual refunds, and angry customers.
Opportunity cost. Every sprint spent on payment plumbing is a sprint not spent on features customers pay for.
A simple mental model:
Real cost = (engineer-months × loaded salary)
+ (compliance + infrastructure)
+ (ongoing maintenance × years)
+ (revenue lost from a delayed launch)
That last term is the one people forget, and it's often the biggest.
3. The People Cost: Your Best Engineers Stop Building Your Product
This is the most underestimated cost of all.
Payments are a specialty. To do them safely, someone on your team has to become an expert in:
Idempotency and exactly-once semantics
Webhook signature verification and replay protection
Settlement cycles, cut-off times, and holiday calendars
Reversals, chargebacks, and dispute flows
Regulatory changes that can land with only a few weeks' notice
Those people are now on call for payment incidents at 2 a.m. They are reading bank PDF specs instead of talking to users. If your company sells logistics, SaaS, e-commerce, or education, payments are not your core business, but they will start consuming a core-business amount of attention.
Here is a small example of code that is boring but has to be exactly right, and that you will have to write and operate yourself:
python
def handle_bank_callback(request):
# 1. Verify the signature (each bank does this differently)
if not verify_signature(request.headers, request.body):
return Response(status=401)
payload = parse(request.body) # JSON? XML? encrypted?
# 2. Idempotency: banks WILL send duplicates
if already_processed(payload.transaction_id):
return Response(status=200)
# 3. Map bank-specific status to your internal state machine
status = map_status(payload.bank_code, payload.status)
# 4. Update order, ledger, and notify user, atomically
with db.transaction():
update_order(payload.order_ref, status)
write_ledger_entry(payload)
return Response(status=200)
Now multiply this by every bank, every payment method, and every edge case, and then maintain it forever.
4. The Ongoing Cost: Integration Is Not a Project, It's a Product
Shipping the integration is only day one. After that:
Banks deprecate endpoints, rotate certificates, and change fields
New regulations require new flows
Volumes grow and rate limits start to bite
Adding a new bank partner means starting from scratch
Documentation drifts from reality and nobody tells you
You've effectively created an internal payments team, whether you planned to or not.
So, When Does Going Direct Make Sense?
To be fair, direct integration is sometimes the right call:
Payments are your core business, or you're building a payment product yourself
You have very high volume, where the fee savings outweigh the engineering cost
You only need one or two banks, with stable, well-documented APIs
You have (or want to build) a dedicated payments engineering team
If that isn't you, consider the alternative.
The Alternative: Use an Abstraction Layer Like PionPay
Instead of wiring up every bank yourself, you can use a unified payment gateway to carry the heavy lifting. PionPay is one option worth considering in this category. The core value of this approach is:
One integration instead of N: you work against a single, consistent API instead of learning each bank
Normalized data models for transactions, statuses, and errors, so your callback handling code becomes much simpler
Managed security and compliance on the provider's side, reducing the burden of certificates, keys, and audits for your team
Faster go-live, often days or weeks instead of many months
A focused team that keeps building the product that sets you apart, instead of acting as payment infrastructure engineers
With that in place, the callback handler above can shrink to a thin layer: verify the signature once against a single standard, map one unified set of statuses, then update the order. The differences between banks are hidden behind the gateway.
You do pay a fee, but compare it honestly against the full cost model above, not just the bank's price list. Before choosing any provider, check the list of supported banks and payment methods, sandbox quality, SLAs, fee structure, and developer documentation.
A Quick Checklist Before You Decide
Ask your team:
How many banks or payment methods do we need in year one? In year three?
Who will maintain this integration, and who is on call for it?
How long can we afford to wait before launch?
What would our engineers build instead if they weren't doing this?
Do we know our real cost, including maintenance and opportunity cost?
If the answers make you uneasy, that unease is the hidden cost showing itself early.
Final Thoughts
Direct bank integration isn't wrong. It's just expensive in ways that don't appear on the quote. The hidden costs are time, money, and focus, and focus is the hardest to get back.
Before you commit, calculate the total cost of ownership, not just the sticker price. Then decide whether payments are something you want to build or something you'd rather use. If you lean toward the latter, take a look at PionPay.vn and run a small proof of concept to measure the real integration time for yourself.
Have you integrated directly with a bank? What surprised you most? Share your war stories in the comments. 👇

Top comments (0)