Skip to content

← All tools

Free tool for iOS and Mac developers

App Store JWS decoder and verifier

Paste a signedPayload from an App Store Server Notification, a signedTransactionInfo or signedRenewalInfo, a StoreKit 2 jwsRepresentation or a signedAppTransactionInfo. The page tells you whether Apple really signed it (x5c chain up to Apple Root CA - G3, Apple's marker extensions, certificate dates at the signing date, ES256 signature) and turns the payload into a readable table: dates in UTC and your time, prices in real currency amounts, and every notification type, subtype and status value explained. Transactions nested in a notification are decoded too. It all runs in your browser.

A notification body (signedPayload), signedTransactionInfo, signedRenewalInfo, a StoreKit 2 jwsRepresentation or signedAppTransactionInfo. A whole JSON body works too. Nothing is uploaded: decoding and checking happen in this page.

Paste signed data above to decode and check it.

Field names and values checked against Apple's documentation and App Store Server Library on 2026-10-30.

How it works

  • •A JWS is three base64url parts: header, payload and signature. The header names the algorithm (ES256 for the App Store) and carries the certificate chain in x5c: the leaf that signed the data, Apple's intermediate, and Apple's root.
  • •The chain check follows Apple's open-source App Store Server Library (jws_verification.ts): x5c must hold three certificates; the intermediate must be signed by Apple Root CA - G3 and the leaf by the intermediate; the intermediate must be a certificate authority; the leaf must carry extension 1.2.840.113635.100.6.11.1 and the intermediate 1.2.840.113635.100.6.2.1.
  • •Apple Root CA - G3 is built into the page (SHA-256 63:34:3A:BF:…:3E:91:79, taken from apple.com and matched against the copy in macOS). Like Apple's library, the page never trusts the root sent in x5c.
  • •Certificate dates are checked at the payload's signedDate with one minute of slack, the same rule as the library's offline mode, so old notifications still verify after a certificate expires.
  • •The ES256 signature (raw R and S, 64 bytes) is checked against the leaf's public key with Web Crypto. Certificate signatures (ECDSA with SHA-256 or SHA-384) are checked the same way.
  • •Dates are milliseconds since 1970 and are shown in UTC and in your time zone. Prices are in milliunits (1990 means 1.99 in the currency field). Enum values use the lists in Apple's documentation and server library.

What it does not do

  • •It makes no network requests: no OCSP revocation check and no call to Apple's App Store Server API. A certificate Apple revoked after signing would still pass here. Apple's library does the OCSP check when you turn online checks on.
  • •It does not check that bundleId, appAppleId or environment match your app. Your server must do that, as Apple's library does.
  • •It cannot tell whether a transaction was later refunded or is still current: the signed data is a snapshot. Ask the App Store Server API for the latest state.
  • •It does not decode the old receipt format (base64 PKCS #7 from verifyReceipt) or create signatures.

Questions

What is signedPayload?
When the App Store sends your server a notification (App Store Server Notifications V2), the POST body is JSON with one field, signedPayload. It is a JWS signed by Apple whose payload holds notificationType, subtype, notificationUUID, signedDate and a data object. Inside data, signedTransactionInfo and signedRenewalInfo are JWS of their own, signed the same way. This page decodes all three at once.
How should my server verify App Store signed data?
Use Apple's App Store Server Library (Swift, Java, Python and Node versions on GitHub). Give its SignedDataVerifier Apple's root certificates, your bundle ID, your app's Apple ID and the environment, and call verifyAndDecodeNotification, verifyAndDecodeTransaction or verifyAndDecodeRenewalInfo. It checks the chain, the dates, the signature and that the data is for your app, and can check revocation online. Use this page to look at a payload while debugging, not as your server's check.
Why does StoreKit testing data from Xcode not pass the chain?
StoreKit testing in Xcode signs transactions with a certificate generated on your Mac ("StoreKit Testing in Xcode"), and the payload says environment Xcode. There is no Apple chain to check, so the page labels it Xcode test data instead of a failure. To check it in your app, export the certificate from Xcode (Editor > Save Public Certificate) and trust it in your test setup. Sandbox and production data are signed by Apple and do pass.
What do the notification types mean?
notificationType says what happened and subtype narrows it down. The common ones: SUBSCRIBED (INITIAL_BUY or RESUBSCRIBE), DID_RENEW, DID_FAIL_TO_RENEW (GRACE_PERIOD keeps access), EXPIRED (VOLUNTARY, BILLING_RETRY, PRICE_INCREASE, PRODUCT_NOT_FOR_SALE), DID_CHANGE_RENEWAL_STATUS (renewal turned on or off), DID_CHANGE_RENEWAL_PREF (UPGRADE or DOWNGRADE), REFUND, REVOKE and CONSUMPTION_REQUEST. The page explains whichever one you paste.
What is the difference between Sandbox and Production?
Production data comes from real App Store purchases. Sandbox data comes from TestFlight and sandbox Apple Accounts; no money changes hands and subscriptions renew on a shortened timetable. Both are signed by Apple with the same chain, so both verify here. Check the environment field and keep the two apart on your server; Apple sends them to the production and sandbox notification URLs you set in App Store Connect.
Can I trust a transaction my app sends to my server?
Only after you verify it. A jwsRepresentation from StoreKit 2 is signed by Apple, so your server can check the signature and chain offline, then check bundleId, environment and, for subscriptions, expiresDate and revocationDate. A JWS proves what Apple signed at signedDate, not that the purchase is still valid now: for the current state, call the App Store Server API (Get Transaction Info or Get All Subscription Statuses) and listen to notifications.

Related