RORK LABJP
MAX — Rork Max handles code signing and provisioning so you can submit to the App Store in two clicks, with no local Mac requiredSIMULATOR — Try your app in an in-browser streaming simulator, then scan a QR code to install straight to your device without TestFlightNATIVE — Coverage reaches SwiftUI, ARKit, HealthKit, HomeKit, Core ML, and Metal, going where React Native cannotSEED — Rork raised a $15M seed led by Left Lane Capital in April 2026, joined by Peak XV and a16z SpeedrunGROWTH — Reported at 743,000 monthly visits with 85% growth, widening access to AI-built mobile appsNOCODE — Gartner expects 75% of new applications to be built with low-code or no-code tools by the end of 2026MAX — Rork Max handles code signing and provisioning so you can submit to the App Store in two clicks, with no local Mac requiredSIMULATOR — Try your app in an in-browser streaming simulator, then scan a QR code to install straight to your device without TestFlightNATIVE — Coverage reaches SwiftUI, ARKit, HealthKit, HomeKit, Core ML, and Metal, going where React Native cannotSEED — Rork raised a $15M seed led by Left Lane Capital in April 2026, joined by Peak XV and a16z SpeedrunGROWTH — Reported at 743,000 monthly visits with 85% growth, widening access to AI-built mobile appsNOCODE — Gartner expects 75% of new applications to be built with low-code or no-code tools by the end of 2026
Articles/Dev Tools
Dev Tools/2026-07-11Advanced

Verifying StoreKit 2 Signed Transactions in a Cloudflare Worker

If you trust whatever the device says about its purchase state, a tampered entitlement walks right through. This walks through verifying StoreKit 2 signed transactions (JWS) in a Cloudflare Worker so you grant entitlements without trusting the client, with working code.

StoreKit 215Cloudflare Workers23Purchase VerificationJWS3Rork500

Premium Article

The first time purchase logic gave me a cold feeling was in an app where I stored a "premium unlocked" flag in UserDefaults and gated features on it. In front of a jailbroken device or a proxied request, that flag guarantees nothing. The whole app could be unlocked for free, and I only noticed the shape of that hole some time after release.

Purchase decisions should ultimately rest on one question: who signed this? StoreKit 2 hands you transactions carrying Apple's signature. So you can draw a boundary where your own server verifies that signature and never trusts the device's raw claim. Here I'll build that verification on a Cloudflare Worker, piece by piece, with code you can actually run.

What "don't trust the device" really means

There are two stances for deciding purchase state. One takes the device at its word when it says "I'm a paying user." The other accepts only the "Apple-signed evidence" the device carries, and only after verifying it. The difference isn't implementation complexity — it's where you place trust.

AspectTrust the device's claimVerify the signature first
Tamper resistanceNone (rewrite the flag, get unlocked)Present (nothing passes without Apple's signature)
Source of truthThe device's local storageAn entitlement your server verified and issued
Offline behaviorAlways instant (but forgeable)Cache the verified result to stay instant
CostLowOne extra Worker

As an indie developer, you want everything to live on the device with no server at all. I did too. But the closer a feature sits to revenue, the more a single verification layer earns its keep. The nice part is that a Cloudflare Worker lets you run that signature check only when a request arrives, without keeping an always-on server around. That "a verification layer that spins up only when needed" shape fits solo operations well.

StoreKit 2's signed transactions (JWS)

In StoreKit 2, purchase results and transaction history come back as a VerificationResult. Inside sits a raw, Apple-signed string: jwsRepresentation. It's a JWS (JSON Web Signature) in compact form — header.payload.signature, three parts joined by dots.

The header carries the signing algorithm (ES256) and an x5c certificate chain. x5c is an array: the first entry is the certificate that actually signed (the leaf), followed by the intermediate, and finally Apple's root (Apple Root CA - G3). The payload holds bundleId, productId, transactionId, environment, purchase dates, and so on.

So verification decomposes into three checks. First, that the JWS signature is valid under the leaf certificate's public key. Second, that the certificate chain climbs correctly to Apple's root. Third, that the payload points at the right product in your own app. Only when all three hold can you conclude the device genuinely purchased.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
If you have been uneasy about trusting the device's claim of 'I'm a paying user,' you'll get a working server-side verification that grants entitlements only after checking Apple's signature
You'll understand how Apple's signed transactions (JWS) and their certificate chain work, and be able to write a Worker that verifies them with Web Crypto and jose
You'll be able to close three quiet holes at the code level: replay, environment mix-ups, and an unpinned root certificate
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-05-20
Rork × StoreKit 2 × App Store Server API — A Three-Layer Subscription Architecture for Indie Apps
How to combine StoreKit 2 and Apple's App Store Server API to protect subscription revenue in Rork iOS apps with three coordinated layers: client verification, server-side JWS validation, and notification reconciliation.
Dev Tools2026-04-25
Outgrowing RevenueCat: A Self-Hosted Guide to App Store Server Notifications V2 for Rork Apps
A complete implementation guide for receiving App Store Server Notifications V2 in Cloudflare Workers without RevenueCat — covering JWS verification, idempotent state updates, user binding, and production testing for Rork apps.
Dev Tools2026-06-30
Answering a Refund Request Within 12 Hours: Handling CONSUMPTION_REQUEST in Cloudflare Workers
How to receive the App Store CONSUMPTION_REQUEST notification for your Rork subscription app in Cloudflare Workers and respond to the Send Consumption Information API within 12 hours — with field-by-field mapping and the operational judgment behind it.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →