Skip to content

Worker: POST /updates and Play subscriptions - #236

Merged
HereLiesAz merged 1 commit into
mainfrom
claude/amazing-fermi-3o92qn-updates
Sep 27, 2026
Merged

HereLiesAz merged 1 commit into
mainfrom
claude/amazing-fermi-3o92qn-updates

Conversation

@HereLiesAz

@HereLiesAz HereLiesAz commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

This closes two of the store Worker's remaining gaps against spec/repository-api.md. The third, registry-sync, is #235.

POST /updates (§ 6)

  • Input: the installed [{ id, version }], capped at 5000 entries and 1 MB.
  • Output: { updates: [{ id, latest }] }, listing only ids whose served, non-yanked version is strictly newer. Current, ahead and unknown ids are omitted.
  • Errors: a malformed body gets 400 with the repository error envelope.
  • Semver: precedence comes from src/semver.ts, a mirror of packages/registry's compareSemver. The Worker can't import that package because it needs node:crypto; the header says to keep the two in step.

Play subscriptions (§ 7)

A listing with an interval used to answer 501. It is now verified through purchases.subscriptionsv2.

  • When it entitles: only while the subscription is ACTIVE or IN_GRACE_PERIOD, it includes that product, and the period hasn't ended.
  • What it issues: a store-signed kind: "subscription" entitlement with expiresAt set to the product's expiryTime, so the store app exchanges again after a renewal.
  • Subject: the base order id, with the ..N renewal suffix stripped so renewals keep one subject. An obfuscatedExternalAccountId is used instead when the app set one.
  • Acknowledgement: a pending subscription is acknowledged, on a best-effort basis, the same as one-time purchases.

The old test asserted the 501 for subscriptions. That behaviour was removed on purpose, and real subscription cases replace the assertion.

Docs: Worker README (the route table, "Play purchases", and a new "Update checks" section). Changeset included.

Verification

  • Worker tests: 38/38 pass. New tests cover:
    • a subscription that is active, in grace, on hold, lapsed, or for the wrong product;
    • base-order subject and acknowledgement;
    • /updates with newer, current, ahead, unknown and prerelease ids;
    • yank exclusion and malformed bodies;
    • semver precedence, including the SemVer 2.0 example order.
  • tsc --noEmit is clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QwYWVPse8spRrfMMcuTJPv


Generated by Claude Code

Summary by Sourcery

Implement repository update checks and Play subscription entitlements in the storefront Worker.

New Features:

  • Add batch update checks through POST /updates, returning strictly newer served, non-yanked versions for installed packages.
  • Support Google Play subscription verification and issue expiring subscription entitlements for active and grace-period subscriptions.

Enhancements:

  • Add shared SemVer precedence handling for update comparisons, including prerelease and build metadata rules.
  • Acknowledge eligible Play subscriptions and preserve entitlement subjects across renewals or configured external account identifiers.

Documentation:

  • Document Play subscription entitlement behavior and the POST /updates update-check API in the Worker README.

Tests:

  • Add coverage for update selection, yanked and malformed inputs, SemVer precedence, and Play subscription lifecycle cases.

POST /updates answers the batch update check from the served,
non-yanked catalog. The Play exchange verifies subscription listings
through subscriptionsv2 and issues an entitlement that expires with the
current period, keyed to the base order id across renewals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QwYWVPse8spRrfMMcuTJPv
@vercel

vercel Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
azphalt Error Error Sep 27, 2026 9:36pm UTC

@sourcery-ai

sourcery-ai Bot commented Sep 27, 2026

Copy link
Copy Markdown

Reviewer's Guide

The Worker now supports spec-compliant POST /updates checks using served non-yanked versions and SemVer precedence, and verifies Play subscription listings through subscriptionsv2 to issue renewable, expiring entitlements with stable subjects and best-effort acknowledgement.

Sequence diagram for the POST /updates batch check

sequenceDiagram
    participant Host
    participant Worker
    participant Catalog
    participant Listings
    participant Revocations
    participant Semver

    Host->>Worker: POST /updates
    Worker->>Catalog: getCatalog(req, env)
    Worker->>Listings: getListings(req, env)
    Worker->>Revocations: revocations(env)
    Worker->>Semver: compareSemver(latest, version)
    Semver-->>Worker: precedence result
    Worker-->>Host: { updates: [{ id, latest }] }
Loading

Sequence diagram for Play subscription entitlement verification

sequenceDiagram
    participant App
    participant Worker
    participant Play as Google Play
    participant Entitlement

    App->>Worker: POST /entitlements/play
    Worker->>Play: verifyPlaySubscription(...)
    Play-->>Worker: subscriptionState, lineItems, expiryTime
    alt ACTIVE or IN_GRACE_PERIOD and product matches
        opt acknowledgement pending
            Worker->>Play: POST subscriptions/{productId}/tokens/{token}:acknowledge
        end
        Worker->>Entitlement: issueEntitlement(kind subscription, expiresAt)
        Entitlement-->>Worker: signed entitlement
        Worker-->>App: encoded expiring entitlement
    else Not entitled
        Worker-->>App: 402 payment_required
    end
Loading

File-Level Changes

Change Details Files
Added the repository batch update-check endpoint.
  • Validates JSON array input, request size, and entry count/type limits.
  • Builds a served, non-yanked catalog and returns only strictly newer versions.
  • Uses a Worker-local SemVer precedence comparator and registers POST /updates.
apps/storefront-worker/src/index.ts
apps/storefront-worker/src/semver.ts
apps/storefront-worker/test/semver.test.ts
apps/storefront-worker/test/gaps.test.ts
Implemented Google Play subscription entitlement verification.
  • Routes interval listings through purchases.subscriptionsv2 and entitles only active or grace-period subscriptions containing the requested product.
  • Acknowledges pending subscriptions best-effort and emits expiring subscription entitlements.
  • Normalizes renewal order IDs to a base subject, preferring obfuscated account IDs when available, while preserving Play error handling.
apps/storefront-worker/src/index.ts
apps/storefront-worker/src/play.ts
apps/storefront-worker/test/gaps.test.ts
Documented and released the Worker API additions.
  • Updated purchase behavior, subscription semantics, and update-check limits and response rules in the Worker README.
  • Added a minor changeset describing both features.
apps/storefront-worker/README.md
.changeset/worker-updates-play-subs.md

Possibly linked issues

  • #unknown: PR directly implements the issue's POST /updates requirement, though the latest field remains unaddressed.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@cloudflare-workers-and-pages

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Failed ❌

View logs ↗
f66b4ae 2026-09-27T21:36:24.646Z View logs ↗

@HereLiesAz

Copy link
Copy Markdown
Owner Author

OpenCode security review

⚠️ Review unavailable. Neither model answered; see the shared workflow run. This is not a finding about the pull request.

@HereLiesAz
HereLiesAz marked this pull request as ready for review September 27, 2026 22:19
@HereLiesAz
HereLiesAz merged commit 16cc464 into main Sep 27, 2026
25 of 28 checks passed

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @HereLiesAz, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 23 hours and 16 minutes by commenting @sourcery-ai review. Upgrade to get a review now.

@HereLiesAz
HereLiesAz deleted the claude/amazing-fermi-3o92qn-updates branch September 27, 2026 22:19

This branch had an error being deployed

1 failed deployment
Preview — f66b4aef Deployed Sep 27, 2026 by vercel[bot]
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.

2 participants