September 26, 2026
When PKR 27,999 Turns Into PKR 2.8 Million: A Small Billing Display Bug With a Big UX Impact
Recently, I noticed something unusual in my ChatGPT billing history.

By Abdulrehman
4 min read
One transaction was displayed as:
PKR 2,799,900
At first glance, that looks like a charge of nearly 2.8 million Pakistani rupees.
But the expected amount was much closer to:
PKR 27,999.00
That immediately suggested something was wrong.
The interesting part: the number is exactly 100ร larger
When I compared the displayed amount with the expected amount, the pattern became obvious:
2,799,900 รท 100 = 27,999.00
The same pattern appeared with another transaction:
PKR 570,000
which likely corresponds to:
PKR 5,700.00
Again:
570,000 รท 100 = 5,700.00
So this does not look like a normal currency-conversion problem.
It looks much more like a minor-unit formatting issue.
What probably happened technically
Payment systems often do not store money as decimal numbers.
Instead of storing:
27999.0027999.00they may store:
27999002799900The reason is simple: money is often represented in the smallest currency unit.
For example:
$10.00 = 1000 cents
PKR 27,999.00 = 2,799,900 paisa$10.00 = 1000 cents
PKR 27,999.00 = 2,799,900 paisaThis is a very common and generally good programming practice.
Using integers helps avoid floating-point precision problems.
The problem happens when the backend stores the amount correctly, but the frontend forgets to convert it back before displaying it.
Instead of:
displayAmount = amount / 100;displayAmount = amount / 100;the UI might effectively be doing:
displayAmount = amount;displayAmount = amount;So:
27999002799900gets displayed as:
PKR 2,799,900PKR 2,799,900instead of:
PKR 27,999.00PKR 27,999.00Why developers store money as integers
At first, someone might ask:
Why not simply store 27999.00?
Because floating-point arithmetic can cause unexpected precision problems.
For example, in many programming languages:
0.1 + 0.20.1 + 0.2may not equal exactly:
0.30.3Internally, it can become something like:
0.300000000000000040.30000000000000004That may be acceptable for many calculations.
It is not ideal for financial systems.
So developers often store monetary values like this:
PKR 100.00 โ 10000
PKR 5,700.00 โ 570000
PKR 27,999.00 โ 2799900PKR 100.00 โ 10000
PKR 5,700.00 โ 570000
PKR 27,999.00 โ 2799900Then the presentation layer converts the value back into a human-readable format.
This approach is perfectly normal.
The bug appears when that final formatting step is missed.
This is not really a currency exchange-rate bug
At first, a user may naturally think:
Is the USD-to-PKR conversion wrong?
But the numbers suggest something different.
Currency exchange errors usually produce irregular differences.
For example, you might see:
27,999 โ 31,50027,999 โ 31,500or:
27,999 โ 35,00027,999 โ 35,000depending on the exchange rate or fees.
But an exact multiplication of 100ร strongly suggests a unit-conversion issue.
That is a classic sign of:
minor currency units being shown as major currency units.
The backend may still be completely correct
This distinction is important.
A billing history page can show the wrong amount while the actual payment processing remains correct.
For example:
Database value:
2799900
Payment processor interpretation:
PKR 27,999.00
Bank charge:
PKR 27,999.00
UI display:
PKR 2,799,900Database value:
2799900
Payment processor interpretation:
PKR 27,999.00
Bank charge:
PKR 27,999.00
UI display:
PKR 2,799,900In that situation, the transaction itself may be fine.
Only the presentation layer is incorrect.
This is why users should compare the displayed amount with:
- the actual bank charge
- card statement
- invoice
- payment receipt
before assuming they were really charged the larger amount.
A small coding mistake can create a huge trust problem
Technically, this may be a tiny bug.
Potentially just one missing operation:
amount / 100amount / 100But from a user-experience perspective, it is not small at all.
Imagine opening your billing page and seeing:
PKR 2,799,900
instead of:
PKR 27,999
Even if no incorrect charge actually happened, the first reaction can easily be panic.
Financial interfaces are different from ordinary interfaces.
A typo in a profile page may be annoying.
A formatting problem in a billing page can immediately affect user trust.
Currency formatting is more complicated than it looks
Developers also need to be careful because not every currency uses two decimal places.
Many currencies commonly use two decimal minor units.
For example:
USD โ cents
EUR โ cents
GBP โ pence
PKR โ paisaUSD โ cents
EUR โ cents
GBP โ pence
PKR โ paisaBut some currencies behave differently.
For example, certain currencies use zero decimal places, while others may use three.
So hardcoding this everywhere:
amount / 100amount / 100is not always a good global solution.
A more robust implementation should understand the currency metadata.
Conceptually:
formatMoney(amount, currency)formatMoney(amount, currency)should determine:
- the currency
- its number of minor units
- the user's locale
- the correct symbol
- thousand separators
- decimal formatting
Then produce the final display.
For example:
2799900 + PKR
โ
PKR 27,999.002799900 + PKR
โ
PKR 27,999.00A better implementation
Instead of manually formatting values across different screens, applications should ideally use one centralized money-formatting function.
For example:
function formatMoney(amountInMinorUnits, currency) {
return new Intl.NumberFormat("en-PK", {
style: "currency",
currency: currency
}).format(amountInMinorUnits / 100);
}function formatMoney(amountInMinorUnits, currency) {
return new Intl.NumberFormat("en-PK", {
style: "currency",
currency: currency
}).format(amountInMinorUnits / 100);
}Then:
formatMoney(2799900, "PKR");formatMoney(2799900, "PKR");would produce a properly formatted value.
In larger systems, the divisor should come from the currency configuration rather than being permanently fixed at 100.
Tests that could catch this
This kind of issue is also a good reminder that billing UIs need very simple but very important tests.
For example:
Input:
2799900 minor units
Currency:
PKR
Expected display:
PKR 27,999.00Input:
2799900 minor units
Currency:
PKR
Expected display:
PKR 27,999.00Another test:
Input:
570000
Expected display:
PKR 5,700.00Input:
570000
Expected display:
PKR 5,700.00These tests may look almost trivial.
But trivial tests often prevent expensive mistakes.
For payment systems, I would test at least:
- small amounts
- large amounts
- zero-value transactions
- refunds
- different currencies
- currencies with different decimal rules
- thousands separators
- localized formatting
- negative values
- invoice and transaction-history consistency
The broader engineering lesson
This incident is a good example of something I find interesting about software development:
The hardest-looking bugs are not always caused by complicated systems.
Sometimes the backend is correct.
The database is correct.
The payment is correct.
The exchange rate is correct.
And the entire problem is simply:
2,799,9002,799,900being interpreted as rupees instead of paisa.
One missing conversion step can make a perfectly normal transaction look financially catastrophic.
Final thought
Good financial software is not only about calculating money correctly.
It is also about displaying money correctly.
Users do not see database integers, payment processor APIs, or internal units.
They see the number on the screen.
And when that number says PKR 2.8 million instead of PKR 27,999, even a small formatting issue becomes a major UX problem.
Sometimes the difference between a trustworthy billing system and a frightening one is simply:
รท 100รท 100