Skip to content

fix: add missing $casts for numeric columns (PostgreSQL compatibility)#842

Open
onesyue wants to merge 1 commit intocedar2025:masterfrom
onesyue:fix/postgresql-numeric-casts
Open

fix: add missing $casts for numeric columns (PostgreSQL compatibility)#842
onesyue wants to merge 1 commit intocedar2025:masterfrom
onesyue:fix/postgresql-numeric-casts

Conversation

@onesyue
Copy link
Copy Markdown

@onesyue onesyue commented Mar 30, 2026

Problem

When using PostgreSQL as the database backend, PHP's PDO driver returns bigint, decimal, and double precision columns as strings (unlike MySQL which auto-converts to native PHP types). These string values pass through Laravel's JSON serialization unchanged, causing JavaScript's + operator to perform string concatenation instead of numeric addition in the frontend.

Confirmed Bug: Payment Page Total = ¥41,600,332.80

The handling_fee_fixed column (double precision) is returned as string "0" from PG. In the frontend checkout page:

// "0" is truthy in JavaScript, so ("0" || 0) returns "0" (string)
fee = subtotal * parseFloat("8") / 100 + ("0" || 0)
    = 3328 + "0"        // number + string = string concatenation
    = "33280"            // should be 3328

total = subtotal + fee
      = 41600 + "33280"  // string concatenation again  
      = "4160033280"

display = (4160033280 / 100).toFixed(2)  = "41600332.80"  // ¥41,600,332.80 💥

Expected: ¥449.28 — Displayed: ¥41,600,332.80

Other Affected Areas

The same class of bug affects any frontend code that uses + with values from:

  • User.u + User.d (traffic calculation)
  • Order.total_amount + Order.handling_amount (order totals)
  • Coupon.value (discount calculation)
  • Stat*.u + Stat*.d (traffic statistics)

Solution

Add $casts declarations for all numeric columns across 10 models. This ensures Laravel converts PG string values to proper PHP int/float types before JSON serialization.

Backward compatible: MySQL already returns correct native types, so these casts are effectively no-ops on MySQL deployments.

Changes

Model Added Casts
Payment handling_fee_percent (float), handling_fee_fixed (integer)
Order total_amount, discount_amount, balance_amount, refund_amount, surplus_amount, commission_balance, actual_commission_balance
User balance, commission_balance, discount, u, d, transfer_enable, t, speed_limit, device_limit, online_count
Coupon value, type, limit_use, limit_use_with_user
Plan transfer_enable, speed_limit, capacity_limit, device_limit
Server rate (float), server_port
CommissionLog order_amount, get_amount
Stat order_count, order_total, commission_count, commission_total, paid_count, paid_total, register_count, invite_count
StatUser u, d, server_rate (float)
StatServer u, d

Test Plan

  • Verified on production PostgreSQL deployment — all fields now return correct numeric types
  • Confirmed the ¥41,600,332.80 display bug is fixed after adding Payment casts
  • Idempotent — running the patch multiple times produces the same result
  • MySQL deployments should be unaffected (casts are no-ops when PDO already returns native types)

PostgreSQL's PDO driver returns bigint/decimal/double columns as PHP
strings, unlike MySQL which auto-converts to native types. When these
string values reach the frontend via JSON, JavaScript's + operator
performs string concatenation instead of numeric addition.

Confirmed bug: Payment.handling_fee_fixed stored as "0" (string) in
PG → JS evaluates ("0" || 0) as "0" (truthy) → 3328 + "0" = "33280"
(concat) → order total displayed as ¥41,600,332.80 instead of ¥449.28.

This patch adds integer/float $casts to all numeric columns across 10
models. The change is backward-compatible — MySQL already returns
correct types, so the casts are effectively no-ops on MySQL while
fixing all PG deployments.

Models patched: Payment, Order, User, Coupon, Plan, Server,
CommissionLog, Stat, StatUser, StatServer.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant