17 thg 11, 2025·8 phút đọc

Làm tròn tiền trong ứng dụng tài chính: lưu trữ tiền an toàn

Làm tròn tiền tệ trong ứng dụng tài chính có thể gây sai lệch 1 xu. Tìm hiểu lưu trữ theo đơn vị nhỏ dạng số nguyên, quy tắc làm tròn thuế và cách hiển thị nhất quán trên web và mobile.

Làm tròn tiền trong ứng dụng tài chính: lưu trữ tiền an toàn

Tại sao lỗi 1 xu xảy ra

Lỗi 1 xu là loại sai sót người dùng nhận ra ngay. Tổng giỏ hàng hiện $19.99 trên danh sách sản phẩm nhưng thành $20.00 khi thanh toán. Hoàn tiền $14.38 về $14.37. Một dòng hóa đơn ghi “Tax: $1.45”, nhưng tổng cuối dường như được cộng theo một cách làm tròn khác.

Những vấn đề này thường đến từ các khác biệt nhỏ về làm tròn tích tụ. Tiền không chỉ là “một con số”. Nó có quy tắc: tiền tệ có bao nhiêu chữ số thập phân, khi nào làm tròn, và có làm tròn theo từng dòng hay trên tổng cuối cùng hay không. Nếu ứng dụng của bạn chọn cách khác ở một chỗ, một xu có thể xuất hiện hoặc biến mất.

Chúng cũng thường chỉ xuất hiện đôi khi, khiến việc gỡ lỗi rất khó chịu. Cùng dữ liệu đầu vào có thể cho ra các xu khác nhau tùy vào thiết bị hoặc cài đặt vùng, thứ tự phép tính, hoặc cách giá trị được chuyển đổi giữa các kiểu.

Những tác nhân phổ biến bao gồm tính toán với float và làm tròn “ở cuối” (nhưng “cuối” không giống nhau ở mọi nơi), áp thuế theo từng món trên một màn hình và theo tổng trên màn khác, trộn tiền tệ hoặc tỉ giá và làm tròn ở các bước không nhất quán, hoặc định dạng để hiển thị rồi vô tình parse lại chuỗi đó thành số.

Tổn hại nặng nhất rơi vào nơi niềm tin mong manh và số tiền bị kiểm toán: tổng thanh toán, hoàn tiền, hóa đơn, đăng ký, tip, thanh toán trả cho người dùng và báo cáo chi tiêu. Sai lệch 1 xu có thể gây thất bại thanh toán, đau đầu khi đối soát, và ticket support kiểu “ứng dụng của bạn đang ăn cắp tôi”.

Mục tiêu đơn giản: cùng dữ liệu đầu vào phải cho cùng số xu ở mọi nơi. Cùng món hàng, cùng thuế, cùng chiết khấu, cùng quy tắc làm tròn — bất kể màn hình, thiết bị, ngôn ngữ hay xuất dữ liệu.

Ví dụ: nếu hai mặt hàng $9.99 chịu thuế 7.25%, hãy quyết định làm tròn thuế theo từng mặt hàng hay trên tổng phụ, rồi làm như vậy ở backend, UI web và app mobile. Tính nhất quán ngăn khoảnh khắc “tại sao nó khác ở đây?”.

Tại sao dùng float rủi ro cho tiền

Hầu hết ngôn ngữ lưu floatdouble ở dạng nhị phân. Nhiều giá thập phân không thể biểu diễn chính xác bằng nhị phân, nên số bạn nghĩ đã lưu thường hơi cao hoặc thấp một chút.

Ví dụ kinh điển là 0.1 + 0.2. Trong nhiều hệ thống nó thành 0.30000000000000004. Điều đó có vẻ vô hại, nhưng logic tiền thường là một chuỗi: giá món, chiết khấu, thuế, phí rồi làm tròn cuối cùng. Lỗi nhỏ có thể đảo quyết định làm tròn và tạo khác biệt 1 xu.

Những triệu chứng dễ nhận thấy khi làm tròn tiền sai:

  • Giá trị như 9.989999 hoặc 19.9000001 xuất hiện trong log hoặc phản hồi API.
  • Tổng trôi sau khi thêm nhiều món, dù từng món trông ổn.
  • Tổng hoàn tiền không khớp với khoản charge gốc chênh 0.01.
  • Cùng một giỏ hàng khác nhau giữa web, mobile và backend.

Định dạng hay che khuất vấn đề. Nếu in 9.989999 với hai chữ số thập phân, nó hiện 9.99, nên mọi thứ có vẻ đúng. Lỗi xuất hiện sau khi bạn cộng nhiều giá trị, so sánh tổng, hoặc làm tròn sau thuế. Đó là lý do các đội đôi khi phát hành và chỉ phát hiện khi đối soát với nhà cung cấp thanh toán hoặc xuất dữ liệu kế toán.

Quy tắc đơn giản: đừng lưu hoặc cộng tiền bằng số dấu phẩy động. Xử lý tiền như số nguyên đếm đơn vị nhỏ của tiền tệ (như cents), hoặc dùng kiểu decimal đảm bảo toán thập phân chính xác.

Nếu bạn xây backend, web app hoặc mobile (kể cả nền tảng no-code như AppMaster), giữ nguyên tắc: lưu giá trị chính xác, tính toán trên giá trị chính xác, và chỉ định dạng để hiển thị ở cuối.

Chọn mô hình tiền phù hợp với tiền tệ thực tế

Hầu hết lỗi tiền bắt đầu trước khi toán học diễn ra: mô hình dữ liệu không khớp cách tiền tệ thực tế hoạt động. Lấy mô hình đúng từ đầu và việc làm tròn trở thành vấn đề quy tắc, không phải đoán mò.

Mặc định an toàn nhất là lưu tiền dưới dạng số nguyên ở đơn vị nhỏ của tiền tệ. Với USD, đó là cents; với EUR, euro cents. Cơ sở dữ liệu và code xử lý số nguyên chính xác, và bạn chỉ “thêm dấu thập phân” khi định dạng cho con người.

Không phải tiền tệ nào cũng có 2 chữ số thập phân, nên mô hình của bạn phải nhận biết tiền tệ. JPY có 0 chữ số nhỏ (1 yên là đơn vị nhỏ nhất). BHD thường dùng 3 chữ số (1 dinar = 1000 fils). Nếu bạn gán cứng “hai chữ số ở mọi nơi”, bạn sẽ vô tình tính thừa hoặc thiếu tiền.

Một bản ghi tiền thực tế thường cần:

  • amount_minor (số nguyên, như 1999 cho $19.99)
  • currency_code (chuỗi như USD, EUR, JPY)
  • tùy chọn minor_unit hoặc scale (0, 2, 3) nếu hệ thống bạn không thể tra cứu reliably

Lưu mã tiền tệ kèm mỗi giá, kể cả trong cùng một bảng. Nó ngăn lỗi khi bạn thêm giá đa tiền tệ, hoàn tiền hoặc báo cáo sau này.

Cũng hãy quyết định nơi được phép làm tròn và nơi không. Một chính sách bền là: không làm tròn trong các tổng nội bộ, phân bổ, sổ cái hoặc chuyển đổi đang tiến hành; chỉ làm tròn tại các ranh giới định nghĩa (như bước thuế, bước chiết khấu hoặc dòng hóa đơn cuối); và luôn ghi lại chế độ làm tròn dùng (half up, half even, round down) để kết quả có thể tái tạo.

Bước một: triển khai tiền là số nguyên đơn vị nhỏ

Nếu bạn muốn ít bất ngờ, chọn một dạng nội bộ cho tiền và đừng phá vỡ nó: lưu các khoản dưới dạng số nguyên ở đơn vị nhỏ của tiền tệ (thường là cents).

Điều này có nghĩa $10.99 trở thành 1099 với currency USD. Với tiền không có đơn vị nhỏ như JPY, 1.500 yên vẫn là 1500.

Một lộ trình triển khai đơn giản mà có thể mở rộng:

  1. Database: lưu amount_minor như số nguyên 64-bit cộng mã tiền tệ (như USD, EUR, JPY). Đặt tên cột rõ ràng để không ai nhầm với decimal.
  2. Hợp đồng API: gửi và nhận { amount_minor: 1099, currency: "USD" }. Tránh chuỗi đã định dạng như "$10.99" và tránh float trong JSON.
  3. Nhập liệu UI: coi input người dùng là văn bản, không phải số. Chuẩn hóa nó (cắt khoảng trắng, chấp nhận một dấu thập phân), rồi chuyển theo số chữ số nhỏ của tiền tệ.
  4. Tất cả phép toán bằng số nguyên: tổng, cộng dòng, chiết khấu, phí và thuế đều thao tác trên số nguyên. Định nghĩa quy tắc như “chiết khấu phần trăm được tính rồi làm tròn về đơn vị nhỏ” và áp dụng cùng cách mọi lúc.
  5. Chỉ định dạng ở cuối: khi hiển thị tiền, chuyển amount_minor thành chuỗi theo locale và quy tắc tiền tệ. Không bao giờ parse lại chuỗi đã định dạng để làm toán.

Một ví dụ phân tích: với USD, lấy "12.3" và coi như "12.30" trước khi chuyển thành 1230. Với JPY, từ chối có dấu thập phân ngay từ đầu.

Quy tắc làm tròn cho thuế, chiết khấu và phí

Làm toán thuế có thể dự đoán
Triển khai thuế theo dòng hoặc theo hóa đơn một lần, rồi tái sử dụng ở mọi nơi.
Start Building

Phần lớn tranh cãi 1 xu không phải nhầm lẫn toán học. Đó là nhầm lẫn chính sách. Hai hệ thống đều có thể “đúng” nhưng vẫn khác nhau nếu làm tròn vào các thời điểm khác nhau.

Ghi rõ chính sách làm tròn và dùng nó mọi nơi: tính toán, biên nhận, xuất dữ liệu và hoàn tiền. Các chế độ phổ biến gồm round half-up (0.5 làm lên) và round half-even (0.5 về số chẵn gần nhất). Một số loại phí yêu cầu luôn làm tròn lên (ceiling) để không thu thiếu.

Tổng thường thay đổi dựa trên vài quyết định: làm tròn theo từng dòng hay theo hóa đơn, có pha trộn quy tắc (ví dụ thuế theo dòng nhưng phí trên hóa đơn), và giá có bao gồm thuế hay chưa (bao gồm thuế thì bạn phải suy ngược phần thuế, chưa bao gồm thì tính thuế từ net).

Chiết khấu tạo thêm ngã rẽ. “Giảm 10%” trước thuế làm giảm cơ sở chịu thuế, trong khi chiết khấu sau thuế giảm số khách thanh toán nhưng có thể không thay đổi thuế báo cáo, tùy luật địa phương và hợp đồng.

Một ví dụ nhỏ cho thấy vì sao quy tắc rõ ràng quan trọng. Hai món $9.99, thuế 7.5%. Nếu bạn làm tròn thuế theo từng dòng, thuế mỗi dòng là $0.75 (9.99 x 0.075 = 0.74925) — làm tròn thành $0.75 cho mỗi dòng, tổng thuế $1.50. Nếu bạn tính thuế trên tổng hóa đơn thì cũng được $1.50 ở ví dụ này, nhưng thay đổi giá chút xíu sẽ thấy khác nhau 1 xu.

Viết quy tắc bằng ngôn ngữ thường để support và tài chính có thể giải thích. Sau đó tái sử dụng helper đó cho thuế, phí, chiết khấu và hoàn tiền.

Chuyển đổi tiền tệ mà không làm trôi tổng

Toán đa tiền tệ là nơi các lựa chọn làm tròn nhỏ có thể dần thay đổi tổng. Mục tiêu rõ ràng: chuyển đổi một lần, làm tròn có chủ ý và giữ các dữ liệu gốc để giải thích sau này.

Lưu tỉ giá với độ chính xác rõ ràng. Mẫu phổ biến là số nguyên có scale, ví dụ “rate_micro” nơi 1.234567 lưu là 1234567 với scale 1,000,000. Một lựa chọn khác là decimal cố định, nhưng vẫn phải ghi scale vào trường để không phải đoán.

Chọn một đồng tiền cơ sở cho báo cáo và kế toán (thường là tiền của công ty). Chuyển các khoản nhập về tiền cơ sở để ghi sổ và phân tích, nhưng giữ giá gốc và tiền tệ kèm theo. Như vậy bạn có thể giải thích mọi con số sau này.

Quy tắc ngăn trôi:

  • Chuyển đổi một chiều cho kế toán (ngoại tệ -> cơ sở), tránh chuyển ngược lại.
  • Quyết định thời điểm làm tròn: làm tròn theo dòng khi phải hiện tổng dòng, hoặc làm tròn ở cuối khi chỉ hiện tổng chung.
  • Dùng một chế độ làm tròn duy nhất và ghi lại.
  • Giữ lại số gốc, tiền tệ và tỉ giá chính xác dùng cho giao dịch.

Ví dụ: khách trả 19.99 EUR, bạn lưu là 1999 minor units với currency=EUR. Bạn cũng lưu tỉ giá dùng lúc thanh toán (ví dụ EUR sang USD ở micro unit). Sổ cái lưu khoản đã chuyển sang USD (làm tròn theo quy tắc bạn chọn), nhưng khi hoàn tiền dùng số EUR gốc và currency đã lưu, không chuyển đổi lại từ USD. Điều này ngăn các ticket “tại sao tôi nhận lại 19.98 EUR?”.

Định dạng và hiển thị trên các thiết bị

Khắc phục trôi xu khi hoàn tiền
Thiết kế hoàn tiền và hoàn tiền từng phần khớp chính xác với giao dịch gốc.
Create App

Bước cuối là trên màn hình. Một giá trị có thể đúng trong lưu trữ nhưng trông sai nếu định dạng khác giữa web và mobile.

Các locale khác nhau chờ các dấu câu và vị trí ký hiệu khác nhau. Ví dụ người dùng ở Mỹ đọc $1,234.50, trong khi nhiều nước châu Âu mong 1.234,50 € (giá trị giống nhau, nhưng dấu phân cách và vị trí ký hiệu khác). Nếu bạn hardcode định dạng, bạn sẽ làm người dùng bối rối và tạo gánh nặng support.

Giữ một quy tắc ở mọi nơi: định dạng ở rìa (edge), không trong lõi. Nguồn dữ liệu chính là (mã tiền tệ, số nguyên đơn vị nhỏ). Chỉ chuyển sang chuỗi để hiển thị. Không bao giờ parse chuỗi đã định dạng thành tiền — chính nơi đó các bất ngờ về làm tròn, cắt bớt và locale xuất hiện.

Với số âm như hoàn tiền, chọn một kiểu thống nhất và dùng ở mọi nơi. Một số hệ thống hiển thị -$12.34, số khác hiển thị ($12.34). Cả hai đều ổn. Chuyển đổi giữa chúng trên các màn hình sẽ trông như lỗi.

Hợp đồng cross-device đơn giản hiệu quả:

  • Mang theo mã tiền tệ ISO (như USD, EUR), không chỉ ký hiệu.
  • Định dạng theo locale thiết bị theo mặc định, nhưng cho phép override trong app.
  • Hiện mã tiền cạnh số trên màn hình đa tiền (ví dụ 12.34 USD).
  • Xử lý định dạng input khác với định dạng hiển thị.
  • Làm tròn một lần, theo quy tắc tiền của bạn, trước khi định dạng.

Ví dụ: khách thấy hoàn tiền 10,00 EUR trên mobile, rồi mở trên desktop thấy -€10. Nếu bạn cũng hiển thị mã (10,00 EUR) và giữ style số âm nhất quán, họ sẽ không thắc mắc có thay đổi hay không.

Ví dụ: thanh toán, thuế và hoàn tiền không gây bất ngờ

Mặc định nhận biết tiền tệ
Xử lý JPY, BHD và hơn thế nữa bằng cách giữ rõ tỉ lệ đơn vị nhỏ trong dữ liệu.
Try AppMaster

Một giỏ hàng đơn giản:

  • Mặt hàng A: $4.99 (499 cents)
  • Mặt hàng B: $2.50 (250 cents)
  • Mặt hàng C: $1.20 (120 cents)

Subtotal = 869 cents ($8.69). Áp chiết khấu 10% trước: 869 x 10% = 86.9 cents, làm tròn thành 87 cents. Subtotal sau chiết khấu = 782 cents ($7.82). Bây giờ tính thuế 8.875%.

Đây là nơi quy tắc làm tròn có thể thay đổi xu cuối cùng.

Nếu bạn tính thuế trên tổng hóa đơn: 782 x 8.875% = 69.4025 cents, làm tròn thành 69 cents.

Nếu bạn tính thuế theo từng dòng (sau chiết khấu) và làm tròn mỗi dòng:

  • Mặt hàng A: thuế $4.49 = 39.84875 cents, làm tròn thành 40
  • Mặt hàng B: thuế $2.25 = 19.96875 cents, làm tròn thành 20
  • Mặt hàng C: thuế $1.08 = 9.585 cents, làm tròn thành 10

Tổng thuế theo dòng = 70 cents. Cùng giỏ, cùng tỉ lệ, quy tắc khác hợp lệ, chênh 1 xu.

Thêm phí vận chuyển sau thuế, ví dụ 399 cents ($3.99). Tổng thành $12.50 (thuế theo hóa đơn) hoặc $12.51 (thuế theo dòng). Chọn một quy tắc, ghi chép và giữ nhất quán.

Bây giờ hoàn tiền chỉ Mặt hàng B. Hoàn theo giá sau chiết khấu (225 cents) cộng thuế thuộc về nó. Với thuế theo dòng, đó là 225 + 20 = 245 cents ($2.45). Các tổng còn lại vẫn đối soát chính xác.

Để giải thích bất kỳ sai khác sau này, ghi log các giá trị này cho mỗi charge và refund:

  • số xu thuần theo dòng, số xu thuế theo dòng, và chế độ làm tròn
  • xu chiết khấu hóa đơn và cách phân bổ
  • tỉ lệ thuế và cơ sở chịu thuế tính bằng xu
  • phí/vận chuyển tính bao nhiêu xu và có chịu thuế không
  • tổng cuối cùng bằng xu và xu hoàn tiền

Cách test các phép tính tiền

Hầu hết lỗi tiền không phải lỗi “toán học”. Là lỗi làm tròn, thứ tự và định dạng, chỉ xuất hiện cho một số giỏ hàng hoặc ngày. Test tốt biến những trường hợp đó thành chuyện nhàm.

Bắt đầu với test golden: input cố định với output mong đợi chính xác ở đơn vị nhỏ (như cents). Giữ assert chặt chẽ. Nếu một món là 199 cents và thuế 15 cents, test nên kiểm tra giá trị nguyên, không phải chuỗi đã định dạng.

Một bộ golden nhỏ có thể che được nhiều:

  • Một dòng với thuế, rồi chiết khấu, rồi phí (kiểm tra mọi làm tròn trung gian)
  • Nhiều dòng nơi thuế làm tròn theo dòng vs trên subtotal (xác minh quy tắc đã chọn)
  • Hoàn tiền và hoàn tiền từng phần (xác minh dấu và hướng làm tròn)
  • Chuyển đổi vòng (A -> B -> A) với chính sách xác định nơi làm tròn
  • Giá biên (món 1 cent, số lượng lớn, tổng rất lớn)

Rồi thêm kiểm tra property-based (hoặc test ngẫu nhiên đơn giản) để bắt bất ngờ. Thay vì một số mong đợi, kiểm tra invariants: tổng bằng tổng các dòng, không có đơn vị nhỏ phân số nào xuất hiện, và “tổng = subtotal + thuế + phí - chiết khấu” luôn đúng.

Test cross-platform quan trọng vì kết quả có thể trôi giữa backend và client. Nếu bạn có backend Go với web Vue và mobile Kotlin/SwiftUI, chạy cùng bộ vectơ test ở mỗi lớp và so sánh output nguyên, không phải chuỗi UI.

Cuối cùng, test các trường hợp theo thời gian. Lưu tỉ lệ thuế dùng trên hóa đơn và xác minh hóa đơn cũ tái tính vẫn cho kết quả như cũ ngay cả khi tỉ lệ thay đổi. Đây là nơi phát sinh lỗi “trước khớp, bây giờ không khớp”.

Bẫy hay gặp cần tránh

Giữ hóa đơn thân thiện kiểm toán
Tạo hóa đơn, biên nhận và xuất dữ liệu bằng cùng phép tính dựa trên xu.
Build an App

Hầu hết lỗi 1 xu không phải lỗi toán học. Là lỗi chính sách: code làm đúng những gì bạn bảo, nhưng không phải điều tài chính mong đợi.

Các bẫy nên phòng:

  • Làm tròn quá sớm: Nếu làm tròn mỗi dòng, rồi làm tròn subtotal, rồi làm tròn thuế, tổng có thể trôi. Quyết định một quy tắc (ví dụ: thuế theo dòng hoặc trên tổng hóa đơn) và chỉ làm tròn nơi chính sách cho phép.
  • Trộn tiền tệ trong một tổng: Cộng USD và EUR trong cùng một trường “tổng” trông vô hại cho đến khi hoàn tiền, báo cáo hoặc đối soát xảy ra. Giữ amounts có tag tiền tệ, và chuyển đổi theo tỉ giá thống nhất trước khi cộng xuyên tiền tệ.
  • Parse sai input người dùng: Người dùng gõ “1,000.50”, “1 000,50”, hoặc “10.0”. Nếu parser của bạn giả sử một định dạng, bạn có thể vô tình tính 100050 thay vì 1000.50, hoặc mất các số 0 cuối. Chuẩn hóa input, validate và lưu dưới dạng đơn vị nhỏ.
  • Dùng chuỗi đã định dạng trong API hoặc database: “$1,234.56” chỉ để hiển thị. Nếu API chấp nhận, hệ thống khác có thể parse khác. Truyền số nguyên (đơn vị nhỏ) cùng mã tiền, và để mỗi client định dạng cục bộ.
  • Không version hóa quy tắc thuế hoặc bảng tỉ giá: Thuế thay đổi, miễn giảm thay đổi, và quy tắc làm tròn thay đổi. Nếu bạn ghi đè tỉ giá cũ, hóa đơn trước đó không thể tái hiện. Lưu phiên bản hoặc ngày hiệu lực với mỗi phép tính.

Kiểm tra thực tế: một checkout tạo vào thứ Hai dùng tỉ lệ thuế tháng trước; người dùng hoàn tiền vào thứ Sáu sau khi tỉ lệ thay đổi. Nếu bạn không lưu phiên bản quy tắc thuế và chính sách làm tròn ban đầu, hoàn tiền sẽ không khớp biên lai gốc.

Checklist nhanh và bước tiếp theo

Nếu bạn muốn ít bất ngờ hơn, xem tiền như một hệ nhỏ với input, quy tắc và output rõ ràng. Hầu hết lỗi 1 xu tồn tại vì chưa ai viết ra nơi được phép làm tròn.

Checklist trước khi phát hành:

  • Lưu amounts ở đơn vị nhỏ (như cent) dưới dạng số nguyên ở mọi nơi: database, business logic và API.
  • Làm mọi toán bằng số nguyên, và chỉ chuyển sang định dạng hiển thị ở cuối.
  • Chọn một điểm làm tròn cho mỗi kiểu phép tính (thuế, chiết khấu, phí, FX) và thực thi ở một chỗ.
  • Định dạng theo quy tắc tiền tệ chính xác (số chữ số, dấu phân cách, số âm) nhất quán trên web và mobile.
  • Thêm test cho các trường hợp biên: 0.01, thập phân lặp trong chuyển đổi, hoàn tiền, capture từng phần và giỏ lớn.

Viết xuống một chính sách làm tròn cho mỗi loại phép tính. Ví dụ: “Chiết khấu làm tròn theo từng dòng về cent gần nhất; thuế làm tròn trên tổng hóa đơn; hoàn tiền lặp lại đường làm tròn gốc.” Đặt những chính sách này cạnh code và vào tài liệu nhóm để chúng không trôi.

Thêm log nhẹ cho mọi bước tiền quan trọng. Ghi input, tên chính sách đã chọn và output ở đơn vị nhỏ. Khi khách báo “tôi bị tính thừa 1 xu”, bạn muốn một dòng duy nhất giải thích vì sao.

Lên kế hoạch một audit nhỏ trước khi đổi logic trên production. Tính lại tổng trên một mẫu đơn hàng lịch sử, so sánh kết quả cũ vs mới và đếm khác biệt. Xem qua vài khác biệt thủ công để xác nhận chúng phù hợp chính sách mới.

Nếu bạn muốn xây luồng end-to-end kiểu này mà không viết lại cùng quy tắc ba lần, AppMaster (appmaster.io) được thiết kế cho ứng dụng hoàn chỉnh với logic backend dùng chung. Bạn có thể mô hình amounts là số nguyên đơn vị nhỏ trong PostgreSQL qua Data Designer, triển khai các bước làm tròn và thuế một lần trong Business Process, rồi tái dùng cùng logic trên web và native mobile UI.

Câu hỏi thường gặp

Tại sao tổng tiền của tôi thay đổi 0.01$ giữa giỏ hàng và thanh toán?

Chúng thường xảy ra khi các phần khác nhau của ứng dụng làm tròn ở thời điểm hoặc theo cách khác nhau. Nếu danh sách sản phẩm làm tròn một bước còn trang thanh toán làm tròn bước khác, cùng một giỏ hàng có thể thực sự cho ra các số xu khác nhau.

Dùng float hoặc double cho giá có vấn đề gì không?

Hầu hết các kiểu float không thể biểu diễn chính xác nhiều giá tiền thập phân, nên các lỗi nhỏ tích tụ. Những khác biệt tí hon đó có thể làm thay đổi quyết định làm tròn sau này và gây sai lệch 1 xu.

Cách an toàn nhất để lưu tiền trong cơ sở dữ liệu và API là gì?

Lưu tiền dưới dạng số nguyên của đơn vị nhỏ của tiền tệ, ví dụ cent cho USD (1999 nghĩa là $19.99), kèm theo mã tiền tệ. Tính toán bằng số nguyên và chỉ chuyển sang chuỗi thập phân khi hiển thị cho người dùng.

Làm sao xử lý các tiền tệ không có 2 chữ số thập phân?

Cố định hai chữ số thập phân sẽ sai với các tiền tệ như JPY (0 chữ số) hoặc BHD (3 chữ số). Luôn lưu mã tiền tệ cùng giá trị và áp dụng thang đơn vị nhỏ phù hợp khi phân tích input và định dạng output.

Nên làm tròn thuế theo từng mặt hàng hay trên tổng hóa đơn?

Chọn một quy tắc rõ ràng và áp dụng ở mọi nơi, ví dụ làm tròn thuế theo từng dòng hoặc làm tròn trên tổng hóa đơn. Chìa khóa là nhất quán giữa backend, web, mobile, xuất dữ liệu và hoàn tiền, và dùng cùng chế độ làm tròn mỗi lần.

Thứ tự áp dụng chiết khấu, thuế và phí nên là gì?

Quyết định thứ tự trước và xem đó là chính sách, không phải chi tiết triển khai. Một mặc định phổ biến là áp dụng chiết khấu trước (để giảm cơ sở chịu thuế) rồi mới tính thuế, nhưng hãy theo quy định doanh nghiệp và pháp luật, và giữ giống nhau trên mọi màn hình và dịch vụ.

Làm thế nào để chuyển đổi tiền tệ mà không làm trôi tổng theo thời gian?

Chuyển đổi một lần bằng tỉ giá đã lưu với độ chính xác rõ ràng, làm tròn có chủ ý ở bước xác định, và giữ lại số tiền gốc cùng tiền tệ để dùng khi hoàn tiền. Tránh chuyển đổi qua lại vì làm tròn lặp lại sẽ gây trôi số.

Tại sao số tiền trông khác nhau trên web và mobile dù toán học đúng?

Đừng phân tích lại chuỗi đã format thành số, vì dấu phân cách theo locale và làm tròn có thể đổi giá trị. Chuyển dữ liệu cấu trúc như (amount_minor, currency_code) và chỉ định dạng ở cạnh UI theo quy tắc locale.

Những test nào bắt được lỗi 1 xu trước khi người dùng gặp?

Dùng các test "golden" cố định với input và output mong đợi ở đơn vị nhỏ (xu). Sau đó thêm kiểm tra invariants như tổng = tổng các dòng, không có đơn vị nhỏ phân số, và tổng = subtotal + thuế + phí - chiết khấu luôn đúng.

Làm sao giữ quy tắc làm tròn nhất quán giữa backend, web và mobile?

Tập trung toán học tiền trong một chỗ và tái sử dụng nó để cùng input cho cùng kết quả ở mọi nơi. Trong AppMaster, cách thực tế là mô hình amount_minor như số nguyên trong PostgreSQL và đặt logic thuế, chiết khấu vào một Business Process dùng chung cho web và mobile.

Dễ dàng bắt đầu
Tạo thứ gì đó tuyệt vời

Thử nghiệm với AppMaster với gói miễn phí.
Khi bạn sẵn sàng, bạn có thể chọn đăng ký phù hợp.

Bắt đầu
Làm tròn tiền trong ứng dụng tài chính: lưu trữ tiền an toàn | AppMaster