Skip to content

feat(kilo-pass): record Google Play purchase amount, currency and tax (part 1/1) - #6282

Merged
iscekic merged 1 commit into
mainfrom
kwf/req-20260917-192220-8435-l1
Sep 18, 2026
Merged

iscekic merged 1 commit into
mainfrom
kwf/req-20260917-192220-8435-l1

Conversation

@iscekic

@iscekic iscekic commented Sep 17, 2026 •

Copy link
Copy Markdown
Collaborator

Changelog for users

  • Google Play purchases now record the amount actually charged, the currency, and the tax withheld in kilo_pass_store_purchases.
  • Warehouse revenue can be calculated per Google Play purchase from the recorded amount and currency.
  • New purchases take the three values from the Google Play order data the platform already receives.
  • A one-off run fills the three values for existing Google Play purchases from their stored order ids.
  • A purchase whose Play order carries no money, or money that cannot be read, still completes and records nothing rather than a wrong amount.

Changelog for maintainers

  • kilo_pass_store_purchases gains nullable amount_charged_minor_units (integer), currency (text), and tax_minor_units (integer); migration 0250 is additive and adds check constraints for non-negative amounts, ^[A-Z]{3}$ currency, and a currency whenever an amount is present.
  • The Play order request now asks for order- and line-item-level total and tax; the same call feeds verification, notifications, and the backfill.
  • New store-purchase-money.ts converts Play Money { currencyCode, units, nanos } to the currency's smallest unit using its ISO exponent; it prefers the matched line item, then the only line item, then order totals, and maps anything unusable to nulls.
  • The Google Play verifier attaches the money to the mapped purchase, and the single insert in completeStoreKiloPassPurchase persists it; App Store rows stay NULL.
  • New backfillGooglePlayPurchaseAmounts re-reads each stored order id, updates only rows with both money columns NULL, and is idempotent and resumable via limit; failures carry row id, order id, and message.
  • The backfill script supports --limit, --dry-run, and --help, and validates the Play service account before the first request so a bad credential exits non-zero instead of reporting only a failure count.
  • Review first: the currency-exponent fallback for well-formed but unknown codes (treated as two decimals), and the rule that an item carrying unreadable money yields nulls instead of borrowing order totals.

E2E proof

[e10] ux-check: Kilo Pass purchase/checkout and account surfaces regression — e10-surfaces.log records '/subscriptions' rendering 'Kilo Pass / KiloClaw / Coding Plans' and '/profile' rendering the Kilo Pass plans ('Starter', 'Pro', 'Expert', 'Buy now'), and e10-checkout.log records 'after buy url=https://checkout.stripe.com/c/pay/cs_test_' with 'Subscribe to Kilo Pass (Starter)' (screenshots e10-subscriptions.png, e10-profile.png, e10-checkout.png); the only error state seen, a 500 on /api/auto-routing/settings (e10-autorouting.log), is tagged [pre-existing] — no auto-routing file appears in e10-diffstat.log — and presents a 'Try again' action.

[e10] ux-check: Kilo Pass purchase/checkout and account surfaces regression — e2e-web/e10-profile.png (capture not retained)

[e10] ux-check: Kilo Pass purchase/checkout and account surfaces regression

[e10] ux-check: Kilo Pass purchase/checkout and account surfaces regression — e2e-web/e10-checkout.png (capture not retained)

[e10] ux-check: Kilo Pass purchase/checkout and account surfaces regression

[e10] ux-check: Kilo Pass purchase/checkout and account surfaces regression — prior/e10-profile.png

[e10] ux-check: Kilo Pass purchase/checkout and account surfaces regression

[e10] ux-check: Kilo Pass purchase/checkout and account surfaces regression — prior/e10-checkout.png

E2E proof — log excerpts

[e2] ux-check: Kilo Pass purchase/checkout and account surfaces regression -> pass :: Live playwright run against the dev stack at localhost:4800 signed in as e2e-verifier@kilo.test: /subscriptions renders 'Manage your subscriptions and billing in one place.' with the Kilo Pass, KiloClaw ('New KiloClaw instances are unavailable') and Coding Plans ('MiniMax Token Plan Plus' ... 'Sold out') tabs, /profile renders 'Unlock up to 50% free credits. Checkout securely with Stripe.' and the purchase entry is present ('buynow count=3' in e2-surfaces.log), the first Buy now reached the hosted checkout shown as 'Subscribe to Kilo Pass (Starter)' (e2-surfaces.log), no visible element is new or changed because the diff touches no UI file (e2-diffstat.log), and no layout shift was measured:
/home/igor_kilocode_ai/.local/share/kwf/sections/req-20260917-192220-8435/e2e-web/e2-surfaces.log
Maldives
Mali
Malta
Martinique
Mauritania
Mauritius
Mayotte
Mexico
Moldova
Monaco
Mongolia
Montenegro
Montserrat
Morocco
Mozambique
Myanmar (Burma)
Namibia
Nauru
Nepal
Netherlands
New
consoleErrors: ["Failed to load resource: the server responded with a status of 401 (Unauthorized)","Failed to load resource: the server responded with a status of 404 (Not Found)","Failed to load res
5xx/4xx responses: ["401 http://localhost:4800/api/user","404 http://localhost:4800/ingest/array/fake-token/config.js","404 http://localhost:4800/ingest/array/fake-token/config?ip=0&_=1789672743291&ve
DONE
/home/igor_kilocode_ai/.local/share/kwf/sections/req-20260917-192220-8435/e2e-web/e2-diffstat.log
apps/web/src/lib/kilo-pass/store-subscription-completion.ts
apps/web/src/scripts/d2026-09-17_backfill-store-purchase-amounts.ts
packages/db/src/migrations/0250_kilo_pass_store_purchase_amounts.sql
packages/db/src/migrations/meta/0250_snapshot.json
packages/db/src/migrations/meta/_journal.json
packages/db/src/schema.ts
$ git diff --stat 4238fe94e..HEAD
 .../kilo-pass/google-play-notifications.test.ts    |    70 +
 apps/web/src/lib/kilo-pass/google-play-sdk.test.ts |    29 +-
 apps/web/src/lib/kilo-pass/google-play-sdk.ts      |    36 +-
 .../src/lib/kilo-pass/google-play-verifier.test.ts |    39 +
 apps/web/src/lib/kilo-pass/google-play-verifier.ts |     2 +
 .../store-purchase-money-backfill.test.ts          |   355 +
 .../lib/kilo-pass/store-purchase-money-backfill.ts |   184 +
 .../src/lib/kilo-pass/store-purchase-money.test.ts |   185 +
 apps/web/src/lib/kilo-pass/store-purchase-money.ts |   108 +
 .../store-subscription-completion.test.ts          |   138 +
 .../lib/kilo-pass/store-subscription-completion.ts |     6 +
 .../d2026-09-17_backfill-store-purchase-amounts.ts |   123 +
 .../0250_kilo_pass_store_purchase_amounts.sql      |     7 +
 packages/db/src/migrations/meta/0250_snapshot.json | 41877 +++++++++++++++++++
 packages/db/src/migrations/meta/_journal.json      |     7 +
 packages/db/src/schema.ts                          |    19 +
 16 files changed, 43180 insertions(+), 5 deletions(-)
/home/igor_kilocode_ai/.local/share/kwf/sections/req-20260917-192220-8435/e2e-web/e2-layoutshift.log
/subscriptions cls=0.0009420437282986111 shiftEntries=2 firstShifts=[0.0001,0.0008]
/profile cls=0.00007263183593750001 shiftEntries=1 firstShifts=[0.0001]
/home/igor_kilocode_ai/.local/share/kwf/sections/req-20260917-192220-8435/e2e-web/e2-autorouting.log
status=500
body(head 1200)=
/home/igor_kilocode_ai/.local/share/kwf/sections/req-20260917-192220-8435/e2e-web/web-setup-smoke.log
  ✘  1 [chromium] › tests/setup-smoke/profile.spec.ts:10:7 › local setup smoke › signs in with fake auth and renders the profile page (30.2s)
  1) [chromium] › tests/setup-smoke/profile.spec.ts:10:7 › local setup smoke › signs in with fake auth and renders the profile page
    Test timeout of 30000ms exceeded.
    Error: expect(locator).toBeVisible() failed
    Locator: getByRole('region', { name: 'User profile' })
    Expected: visible
    Error: element(s) not found
    Call log:
      - Expect "toBeVisible" with timeout 30000ms
      - waiting for getByRole('region', { name: 'User profile' })
      58 |
      59 |     const profileCard = page.getByRole('region', { name: 'User profile' });
    > 60 |     await expect(profileCard).toBeVisible({ timeout: 30_000 });
         |                               ^
      61 |     await expect(page.getByRole('link', { name: 'Your Profile' })).toBeVisible();
      62 |     await expect(profileCard.getByRole('button', { name: 'Edit profile' })).toBeVisible();
      63 |     await expect(profileCard.getByText(testEmail, { exact: true })).toBeVisible();
        at /home/igor_kilocode_ai/.local/share/kwf/wt/req-20260917-192220-8435/apps/web/tests/setup-smoke/profile.spec.ts:60:31
    attachment #1: screenshot (image/png) ──────────────────────────────────────────────────────────
    test-results/setup-smoke/profile-local-setup-smoke--f6e7e-nd-renders-the-profile-page-chromium/test-failed-1.png
    ────────────────────────────────────────────────────────────────────────────────────────────────
    Error Context: test-results/setup-smoke/profile-local-setup-smoke--f6e7e-nd-renders-the-profile-page-chromium/error-context.md
  1 failed
Owner request for the stack

Add the actual amount charged, the currency, and the tax to our Google Play purchase records in kilo_pass_store_purchases. Today those records cannot tell the warehouse what a purchase really earned: we need the real amount charged, the currency it was charged in, and the tax withheld, recorded per Google Play purchase alongside what is already stored, so revenue can be calculated in the warehouse. Find the existing table and the Google Play purchase write path, extend the record with the three values, populate them for new purchases from the Google Play purchase data we already receive, and backfill what can be backfilled from the stored receipts. Prove it end to end with a real Google Play purchase record written and read back showing the amount, the currency and the tax.

PR stack (merge bottom to top)

Comment thread apps/web/src/lib/kilo-pass/store-purchase-money.ts Outdated
Comment thread apps/web/src/lib/kilo-pass/store-purchase-money-backfill.ts
@kilo-code-bot

kilo-code-bot Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review of 4cb7e99 → a0e82871: all PR source and SQL files are byte-identical to the previously reviewed revision, and the delta is only the rebase onto current main plus regenerated Drizzle snapshots/journal, which remain internally consistent (journal idx 251/252 map to 0251_snapshot.json/0252_snapshot.json, and the latest snapshot carries the new columns and check constraints). The earlier findings are fixed in current code. No new issue was found in the changed lines.

Files Reviewed (18 files)
  • apps/web/src/lib/kilo-pass/google-play-notifications.test.ts
  • apps/web/src/lib/kilo-pass/google-play-sdk.test.ts
  • apps/web/src/lib/kilo-pass/google-play-sdk.ts
  • apps/web/src/lib/kilo-pass/google-play-verifier.test.ts
  • apps/web/src/lib/kilo-pass/google-play-verifier.ts
  • apps/web/src/lib/kilo-pass/store-purchase-money-backfill.test.ts
  • apps/web/src/lib/kilo-pass/store-purchase-money-backfill.ts
  • apps/web/src/lib/kilo-pass/store-purchase-money.test.ts
  • apps/web/src/lib/kilo-pass/store-purchase-money.ts
  • apps/web/src/lib/kilo-pass/store-subscription-completion.test.ts
  • apps/web/src/lib/kilo-pass/store-subscription-completion.ts
  • apps/web/src/scripts/d2026-09-17_backfill-store-purchase-amounts.ts
  • packages/db/src/migrations/0250_kilo_pass_store_purchase_amounts.sql
  • packages/db/src/migrations/0251_kilo_pass_store_purchase_money_backfill_attempted.sql
  • packages/db/src/migrations/meta/0251_snapshot.json (generated)
  • packages/db/src/migrations/meta/0252_snapshot.json (generated)
  • packages/db/src/migrations/meta/_journal.json (generated)
  • packages/db/src/schema.ts
Previous Review Summaries (4 snapshots, latest commit 4cb7e99)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 4cb7e99)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental change bounds converted Play money to PostgreSQL integer range (safe integer, non-negative, <= 2_147_483_647) on both the live converter and the backfill sanitizer, resolving the previously reported overflow finding and introducing no new issue in the changed lines.

Files Reviewed (3 files)
  • apps/web/src/lib/kilo-pass/store-purchase-money.ts
  • apps/web/src/lib/kilo-pass/store-purchase-money-backfill.ts
  • apps/web/src/lib/kilo-pass/store-purchase-money.test.ts

Previous review (commit b34ca2a)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental change replaces the CLDR-derived currency exponent with an explicit ISO 4217 minor-unit table and restricts the order-total fallback to orders with a single line item, so both previously reported issues are resolved and no new issue was found in the changed lines.

Files Reviewed (2 files)
  • apps/web/src/lib/kilo-pass/store-purchase-money.ts
  • apps/web/src/lib/kilo-pass/store-purchase-money.test.ts

Previous review (commit 1f382e1)

Status: 2 Issues Found | Recommendation: Address before merge

Executive Summary

The new Google Play money mapping can attribute an entire multi-line-item order's total to a single Kilo Pass purchase, which would misreport warehouse revenue for upgrade/deferred-replacement orders.

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/lib/kilo-pass/store-purchase-money.ts 111 The order-total fallback also runs for multi-line-item orders, attributing the whole order's money to one product; restrict it to the single-line-item case

SUGGESTION

File Line Issue
apps/web/src/lib/kilo-pass/store-purchase-money.ts 25 The currency exponent comes from ICU/CLDR display data, which diverges from ISO 4217 minor units for some currencies (e.g. HUF, IDR, TWD) and can mis-scale stored amounts
Files Reviewed (18 files)
  • apps/web/src/lib/kilo-pass/store-purchase-money.ts - 2 issues
  • apps/web/src/lib/kilo-pass/store-purchase-money.test.ts
  • apps/web/src/lib/kilo-pass/store-purchase-money-backfill.ts
  • apps/web/src/lib/kilo-pass/store-purchase-money-backfill.test.ts
  • apps/web/src/lib/kilo-pass/google-play-sdk.ts
  • apps/web/src/lib/kilo-pass/google-play-sdk.test.ts
  • apps/web/src/lib/kilo-pass/google-play-verifier.ts
  • apps/web/src/lib/kilo-pass/google-play-verifier.test.ts
  • apps/web/src/lib/kilo-pass/google-play-notifications.test.ts
  • apps/web/src/lib/kilo-pass/store-subscription-completion.ts
  • apps/web/src/lib/kilo-pass/store-subscription-completion.test.ts
  • apps/web/src/scripts/d2026-09-17_backfill-store-purchase-amounts.ts
  • packages/db/src/migrations/0250_kilo_pass_store_purchase_amounts.sql
  • packages/db/src/migrations/0251_kilo_pass_store_purchase_money_backfill_attempted.sql
  • packages/db/src/schema.ts
  • packages/db/src/migrations/meta/_journal.json (generated)
  • packages/db/src/migrations/meta/0250_snapshot.json (generated)
  • packages/db/src/migrations/meta/0251_snapshot.json (generated)

Fix these issues in Kilo Cloud

Previous review (commit d0f2fdd)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/lib/kilo-pass/store-purchase-money.ts 52 Negative Play money is not mapped to null, so the new non-negative check constraints reject the insert and the whole purchase completion fails

SUGGESTION

File Line Issue
apps/web/src/lib/kilo-pass/store-purchase-money-backfill.ts 137 Rows whose order carries no money stay NULL and are re-selected every run, so --limit batches never converge or report completion
Files Reviewed (16 files)
  • apps/web/src/lib/kilo-pass/store-purchase-money.ts - 1 issue
  • apps/web/src/lib/kilo-pass/store-purchase-money-backfill.ts - 1 issue
  • apps/web/src/lib/kilo-pass/store-purchase-money.test.ts
  • apps/web/src/lib/kilo-pass/store-purchase-money-backfill.test.ts
  • apps/web/src/lib/kilo-pass/google-play-sdk.ts
  • apps/web/src/lib/kilo-pass/google-play-sdk.test.ts
  • apps/web/src/lib/kilo-pass/google-play-verifier.ts
  • apps/web/src/lib/kilo-pass/google-play-verifier.test.ts
  • apps/web/src/lib/kilo-pass/google-play-notifications.test.ts
  • apps/web/src/lib/kilo-pass/store-subscription-completion.ts
  • apps/web/src/lib/kilo-pass/store-subscription-completion.test.ts
  • apps/web/src/scripts/d2026-09-17_backfill-store-purchase-amounts.ts
  • packages/db/src/migrations/0250_kilo_pass_store_purchase_amounts.sql
  • packages/db/src/schema.ts
  • packages/db/src/migrations/meta/_journal.json (generated)
  • packages/db/src/migrations/meta/0250_snapshot.json (generated snapshot)

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch main

@iscekic
iscekic marked this pull request as draft September 17, 2026 19:49
iscekic added a commit that referenced this pull request Sep 17, 2026
@iscekic
iscekic force-pushed the kwf/req-20260917-192220-8435-l1 branch from 1173234 to 1f382e1 Compare September 17, 2026 20:49
@iscekic
iscekic marked this pull request as ready for review September 17, 2026 21:09
Comment thread apps/web/src/lib/kilo-pass/store-purchase-money.ts
Comment thread apps/web/src/lib/kilo-pass/store-purchase-money.ts Outdated
@iscekic
iscekic marked this pull request as draft September 17, 2026 21:29
iscekic added a commit that referenced this pull request Sep 17, 2026
@iscekic
iscekic force-pushed the kwf/req-20260917-192220-8435-l1 branch from 1f382e1 to b34ca2a Compare September 17, 2026 21:50
@iscekic
iscekic marked this pull request as ready for review September 17, 2026 22:10
@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 17, 2026
@iscekic iscekic self-assigned this Sep 17, 2026
Comment thread apps/web/src/lib/kilo-pass/store-purchase-money.ts Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human-ready The PR is ready for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants