Commit Graph

11 Commits

Author SHA1 Message Date
Huang Xin 9e163fe746 fix(payment): reflect highest active plan across overlapping Stripe subscriptions (#4694)
* fix(payment): reflect highest active plan across overlapping Stripe subscriptions

When a user upgrades Plus to Pro, both subscriptions stay active until the old
one is cancelled. Each subscription webhook overwrote plans.plan with only that
event's plan, so whichever webhook arrived last won and could downgrade the
account back to plus.

Derive plans.plan from the highest active (or trialing) subscription via a new
getHighestActivePlan helper, used by createOrUpdateSubscription and by the
cancellation handler so a still-active higher plan is preserved instead of
dropping to free.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(payment): add opt-in live Stripe test for getHighestActivePlan

Skipped by default (CI included); runs only when STRIPE_SECRET_KEY and
STRIPE_TEST_CUSTOMER_ID are set, so it can be exercised locally against a real
customer with overlapping subscriptions. The module under test is imported
dynamically so the file stays import-safe while skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 07:29:39 +02:00
Huang Xin 495783d045 fix(security): harden OPDS proxy SSRF, storage key validation, Stripe check (#4638)
Server-side hardening for three reported web advisories:

- OPDS proxy (/api/opds/proxy): add http(s) scheme allowlist, internal/loopback/
  link-local host blocklist, and manual per-hop redirect re-validation so a
  public URL can't redirect into an internal address. Move isBlockedHost into
  the shared src/utils/network.ts as the canonical blocklist and reimplement
  isLanAddress to delegate to it (also tightens the /api/kosync LAN check);
  fetch-url.ts re-exports it. (GHSA-c7mm-g2j2-98cx, GHSA-5g3f-mq2c-j65v)

- Storage upload (/api/storage/upload): validate the client-supplied fileName
  with a new isSafeObjectKeyName helper before building the object key, so a
  name can't escape the caller's own prefix. (GHSA-mfmj-2frf-vhgw)

- Stripe (/api/stripe/check): bind the entitlement to the session owner —
  reject a Checkout Session whose metadata.userId differs from the authenticated
  caller. (GHSA-pv88-3727-j7v8)

Unit tests added for each path; full suite + lint green. The Tauri-native
advisory (GHSA-55vr-pvq5-6fmg) is handled in a separate change.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 07:45:29 +02:00
fabrisnick b251e537d3 chore(deps): update stripe (#3941)
* chore(deps): update stripe

* fix: filter and sort not affecting purchases

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* refactor: rename restoredPurchases to restoredSubscriptions

The filter excludes one-time purchases, so the array contains only
subscriptions. Naming reflects that.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Huang Xin <chrox.huang@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 10:24:00 +02:00
Huang Xin cd614e3845 feat(cjk): add an option to replace quotation marks in vertical layout for CJK languages (#2511) 2025-11-22 14:09:20 +01:00
Huang Xin c86af457e7 chore: bump Next.js to 16.0.3 (#2496) 2025-11-21 12:49:36 +01:00
Huang Xin fdf6908fc6 feat: expand cloud storage with one-time payment (#2325) 2025-10-25 11:46:51 +02:00
Huang Xin 8974a87168 feat: add IAP server api (#1676) 2025-07-25 14:44:56 +02:00
Huang Xin bf0bb80009 api: fix for supabase RLS (#1501) 2025-06-30 15:08:53 +02:00
Huang Xin 5d61a9808f feat: add subscription management for tauri platforms (#1499) 2025-06-30 10:04:57 +02:00
Huang Xin 280cb75ca4 api: cors in middleware (#1493) 2025-06-29 17:00:52 +02:00
Huang Xin d5d0a9abd3 api: add subscription API (#1491) 2025-06-29 14:38:04 +02:00