The Credit Card Wall Blocking Indonesian Developers

Photo by SCJiang via Wikimedia Commons (CC BY-SA 4.0)
Card billing is a stored credential the platform can pull from every month without asking, which makes it the cheapest recurring rail to operate. AWS documents that a valid, unexpired credit card must be on file to make a payment, and Google Cloud states that the methods available depend on your currency and country. Local rails such as QRIS require inbound callbacks, expiring payment intents and reconciliation, so platforms that never had to build them default to cards.
Sometimes, and the failure is not always a decline code. Google Cloud's payment methods page states outright that debit cards requiring two-factor authentication are not accepted, and that prepaid cards are not accepted either, which rules out a large share of the instruments issued in Indonesia. Where the card is accepted in principle, AWS documents a redirect to your bank for additional verification, and that 3-D Secure step is where a domestic-only issuer flow tends to break.
AWS publishes one, and its requirements are the point. ACH direct debit needs a bank account located in the United States with payments in US dollars, an AWS account at least 60 days old, at least one invoice paid in full in the previous 12 months, at least 100 US dollars paid cumulatively in that period, and a US ID or federal tax ID on file. In practice the non-card rail unlocks only after a card has already worked for a year.
Helipod is the clearest example: a PaaS aimed at Indonesian and South-East Asian developers, priced in rupiah from 123,750 rupiah a month for its Nano plan, and paid by QRIS or bank transfer with no credit card required. It builds from a Git push using an engine called Helipack that generates the Dockerfile itself, and autoscales through the Kubernetes Horizontal Pod Autoscaler. The advantage is distribution rather than technology, since the feature set is comparable to platforms that only take cards.
QRIS is a payment instruction, not a stored credential, so there is no token on file to charge on the first of the month. Instead of a scheduler that pulls from a card, you issue a per-period invoice with an expiring payment intent and let the gateway callback be the only writer that marks it paid, keyed on the gateway reference so a repeated notification settles once. Dunning also changes shape: you send a fresh QR before the period ends rather than retrying a charge after it lapses.

Photo by SCJiang via Wikimedia Commons (CC BY-SA 4.0)
Key Takeaway
Most global clouds and deploy platforms bill by international credit card, and the World Bank puts credit card ownership in Indonesia at 1.6 percent of adults against 35.1 percent for debit cards. Platforms priced in rupiah and billed over QRIS or bank transfer remove that gate, which is a distribution advantage rather than a technical one.
The most useful thing I have learned about developer tooling in Indonesia did not come from a benchmark. It came from watching sign-up forms. Someone with a working Next.js app, a domain and a plan reaches the billing step of a deploy platform, the form asks for a Visa or Mastercard number, and the session ends there. Not because the plan is expensive, but because the field cannot be filled at all.
This post is about that gate: where it actually sits, what the card ownership numbers are, why an Indonesian debit card fails a foreign merchant even with funds behind it, and what a platform has to build to bill over QRIS, virtual accounts and bank transfer instead. Every rule I quote comes from a platform's own billing documentation or from the tax authority, and I have flagged the friction that local rails do not remove.
AWS is unambiguous in its own documentation: to make a payment you must have a valid, unexpired credit card on file. Google Cloud is more open about the regional shape of the problem. Its billing page says the available methods depend on your currency and country, then lists the accepted set as Visa, Mastercard, American Express, Visa or Mastercard debit cards, and a handful of country-specific extras: Boleto and Pix in Brazil, UPI in India, PayPal in three countries. Indonesia appears in none of those exception lists.
So the gate is an instrument, not an amount. On the hyperscalers it sits at account creation, before a single byte is served. On the Git-push platforms the hobby tier is genuinely free and cardless, which moves the gate one step later, to the first paid plan: the moment you outgrow the free tier and need a persistent database, a second region, or a build that is not queued behind everyone else. Either way, a developer who cannot present a card can learn on the platform and can never run production on it.
The World Bank's Global Financial Development Database tracks card ownership among adults aged 15 and over. For Indonesia the two series diverge in a way that explains most of this article: debit card ownership more than tripled over a decade while credit card ownership went nowhere.
| Survey year | Credit card, percent of adults 15 plus | Debit card, percent of adults 15 plus |
|---|---|---|
| 2011 | 0.50 | 10.54 |
| 2014 | 1.60 | 25.94 |
| 2017 | 2.44 | 30.81 |
| 2021 | 1.60 | 35.10 |
Two caveats, because those numbers are load bearing. This is a household survey of all adults, not of developers, and developers skew urban, employed and banked, so the real figure among people reading this is certainly higher than 1.6 percent. And the 2021 reading sits below 2017, which is more likely survey noise than a market that shrank. Neither caveat changes the shape. A rail that reaches under two percent of adults is not a payment method, it is a filter, and a conversion funnel does not care about the national average. It cares about the specific person standing at the form.
A card in the wallet is not the same as a card that clears. Google Cloud's payment methods page contains two sentences that quietly remove most of the instruments an Indonesian developer actually holds: debit cards that require two-factor authentication are not accepted for payment, and prepaid cards are not accepted for payment. Read those next to how Indonesian issuers behave on card-not-present transactions and the outcome is predictable.
Each of those three produces a different and unhelpful error surface, which is why the person at the form usually concludes the problem is them. It is not. It is three separate policies interacting, and none of them is written down in one place from the buyer's side.
The failure mode that costs the most is the one that succeeds first. A card that clears the initial charge and then fails verification on a renewal takes production down at the moment the platform suspends the project, and the warning email goes to an address nobody is watching. If you are running on a card you do not fully control, keep the deploy artefact and a current database dump somewhere that is not on that platform.
AWS does offer a rail that is not a card, and reading its requirements is the clearest single illustration of the problem. ACH direct debit accepts a personal or business bank account provided the account is at a bank located in the United States and payments are in US dollars. To add one manually you must also meet all of the following:
Read that as a state machine and it is a bootstrap paradox: the alternative to the card unlocks only after a card has already worked for a year. Google Cloud words its version more softly, saying you can add a bank account if it is supported in your country, and points readers who need more options at a local reseller or a switch to invoiced billing. That is a reasonable answer for a company with a procurement department and no answer at all for one person with a side project. The non-card path exists, and it is reserved for customers who no longer need it.

QRIS is Bank Indonesia's national QR standard, and having integrated it, the property that matters to a platform is not the QR code. It is that one integration reaches bank apps and e-wallet balances through the same code, so the customer needs neither a card nor, in the e-wallet case, a bank account at all. Virtual accounts are the other workhorse: the gateway issues a unique account number per invoice, the customer transfers to it from any bank, and that number doubles as the reconciliation key. Plain bank transfer still matters for annual invoices, where a finance department wants to see a transfer it initiated itself.
The ordering is what platform teams get wrong. QRIS reaches furthest and is the cheapest thing to present: one image and one expiry. Virtual accounts reach anyone with a bank account and are the easiest to reconcile, because the account number is unique per invoice. Cards belong last in that list rather than first, and in this market they are an accommodation for a minority rather than the default path.
The engineering difference is that QRIS and virtual accounts are not stored credentials. There is no token sitting on file to charge on the first of the month. A card subscription is a pull that your scheduler initiates; a QRIS subscription is a push you have to wait for. That inverts the billing system: instead of a cron that charges cards and handles declines, you need a per-period invoice with an expiring payment intent, an inbound callback that is the only thing permitted to mark that invoice paid, and an idempotency key so the same settlement notification arriving three times does not extend the subscription three times.
-- QRIS is not a stored credential. There is no token to charge on the 1st,
-- so the gateway callback is the ONLY writer allowed to mark a period paid.
-- The unique constraint below is the entire idempotency story: a callback
-- delivered three times settles once.
CREATE TABLE payment_settlements (
id bigserial PRIMARY KEY,
gateway_ref text NOT NULL UNIQUE, -- the GATEWAY id, not ours
invoice_id uuid NOT NULL REFERENCES invoices (id),
amount_idr bigint NOT NULL, -- rupiah as an integer, never a float
settled_at timestamptz NOT NULL,
raw_callback jsonb NOT NULL -- keep it; reconciliation reads this
);
-- Why gateway_ref and not invoice_id? A retried callback for the SAME payment
-- repeats the gateway reference, so that is the value which must be unique.
-- A re-issued QR for the same invoice legitimately arrives with a new one, so
-- constraining on invoice_id would silently drop a genuine second attempt.
-- Applying one callback. Insert first: if the row already exists, nothing
-- downstream runs and the subscription is not extended a second time.
WITH applied AS (
INSERT INTO payment_settlements
(gateway_ref, invoice_id, amount_idr, settled_at, raw_callback)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (gateway_ref) DO NOTHING
RETURNING invoice_id
)
UPDATE subscriptions s
SET current_period_end = s.current_period_end + interval '1 month'
FROM applied a
JOIN invoices i ON i.id = a.invoice_id
WHERE s.id = i.subscription_id;Two consequences follow from the same fact. Dunning becomes a notification problem rather than a retry problem, because there is nothing to retry after a period lapses. You send a fresh QR by email or WhatsApp before it ends. And reconciliation stops being optional: the money arrives on the acquirer's settlement cycle and net of the merchant discount rate, so the amount that lands in the bank never equals the sum of the invoices it paid. Without the gateway reference stored next to the invoice, that difference is unexplainable at month end.
Store the rupiah amount as an integer of rupiah, and store the gateway reference on the same row as the invoice. Every reconciliation question I have been asked, from why the bank total differs from the invoice total to which invoice a given settlement belongs to, is one query away if those two columns exist and effectively unanswerable if they do not.
Even when the card works there is a document problem. Indonesia's Directorate General of Taxes appoints foreign electronic commerce operators as PMSE VAT collectors once either threshold is crossed: transaction value with users in Indonesia above 600 million rupiah a year or 50 million a month, or users and traffic from Indonesia above 12,000 a year or 1,000 a month. An appointed collector applies 12 percent to a deemed base of eleven twelfths of the payment received excluding VAT, which works out at 11 percent of the payment, and must issue proof of collection: a commercial invoice, billing document, order receipt or similar document stating that VAT has been collected and paid.
That rule works, and it still leaves an Indonesian buyer holding a US dollar billing document rather than a local tax invoice. A billing document is not a Faktur Pajak, and a finance team that needs one will say so. A platform billing locally issues the document the customer's accountant already knows how to file, in the currency the books are kept in, against an NPWP. None of that is a technical feature, all of it closes deals, and it is exactly the kind of advantage engineers are trained not to notice.

Helipod is the clearest worked example I have found of a platform designed around the payment step. It is a PaaS aimed at Indonesian and South-East Asian developers: a build engine called Helipack detects the framework and generates a Dockerfile with no manual configuration, covering Next.js, Nuxt, Laravel, Django, FastAPI, Ruby on Rails, Express, NestJS, Flask and more; autoscaling through the Kubernetes Horizontal Pod Autoscaler on CPU; live log streaming and a browser terminal into production pods instead of SSH; and one-click templates for Supabase, PostgreSQL, Redis, n8n, Metabase and Uptime Kuma. Plans are priced in rupiah, from Nano at 123,750 rupiah a month for 1 vCPU, 1 GB of RAM, 15 GB of storage and two custom domains, up to Business at 2,785,500 rupiah for 22 vCPU and 22 GB, and they are paid by QRIS or bank transfer with no credit card required.
The honest reading is that none of that is technically ahead of Fly.io, Railway or Render, and the coverage list names countries rather than datacentre cities, so I cannot verify a latency claim either way. What it has is a checkout that clears. For the developer standing at the form in the first section, a platform that is second best on features and first best on being purchasable is not second best. It is the only one, and that is a real product position rather than a consolation prize.
Helipod prices in rupiah and bills over QRIS and bank transfer, and its own positioning states the reason outright: a PaaS for developers who want to deploy without an international credit card. If you are building anything for this market, that sentence is the feature. helipod.io
The rule I apply now when I assess a developer platform for this market is to read the checkout before the changelog. Feature parity is negotiable and a payment rail is not: a platform that cannot take rupiah over QRIS has excluded most of a country regardless of how good its dashboard is. If you are building for Indonesia, billing is not plumbing you bolt on after launch. It is the product decision that decides who gets to ship at all.
Sources