How to Verify App Store Receipts with JWT & PHP: A Step-by-Step Guide
Here’s a guide on how to verify App Store receipts using JWT and PHP:
Now you have Receipt Data from the App Store in base64-encoded format. Which you get from Apple Server. See our previous article to see how you can call API to fetch this data. So you need to decode it with the JWT firebase library or you can also use our solution which is given in this article.
Solution 1:
Decoding JWT and Extracting Receipt Data
To decode a “signedPayload” object, which is a JSON Web Signature (JWS) format will look like this:
Decode the JWS Header
- The signedPayload consists of three parts separated by periods (‘.’). The first part is the JWS header, which contains metadata about the JWS, such as the signature algorithm used.
- Use base64url decoding to extract the header part from the signedPayload.
Decode the JWS Payload
- The second part of the signedPayload is the JWS payload, which contains the actual data, including information about the App Store notification.
- Use base64url decoding to extract the payload part from the signedPayload.
Extract the JWS Signature
- The third part of the signedPayload is the JWS signature, which is used to verify the authenticity and integrity of the JWS.
- Use base64url decoding to extract the signature part from the signedPayload.
Verify the JWS Signature
- You need the public key associated with the private key used by Apple to sign the payload. This public key is typically provided by Apple and can be found in your developer account.
- Verify the JWS signature using the public key and the JWS header and payload data.
if you can’t find the public key then download the Apple root certificate and make .PEM file from it.
Download the certificate below URL:
https://www.apple.com/certificateauthority/AppleRootCA-G3.cer
Then using OpenSSL command convert this certificate into .PEM file:
openssl x509 -in AppleRootCA-G3.cer -out apple_root.pem
The next step is to run the script below and decode the JWS payload.
//Include JWT Library
// Load Composer's autoloader
require 'vendor/autoload.php';
use Firebase\JWT\JWT;
//Get the PEM file From YOUR Directory Where you converted
$pem = file_get_contents("YOUR_DIRECTORY/apple_root.pem");
//Pass or Call JWS data "$jwsData" to below line
$header_payload_secret = explode('.', $jwsData);
//------------------------------------------
// Header
//------------------------------------------
$header = json_decode(base64_decode($header_payload_secret[0]));
$algorithm = $header->alg;
$x5c = $header->x5c; // array
$certificate = $x5c[0];
$intermediate_certificate = $x5c[1];
$root_certificate = $x5c[2];
//------------------------------------------
// Payload this You can return this payload as its already decoded
//------------------------------------------
$payload = json_decode(base64_decode($header_payload_secret[1]));
$certificate =
"-----BEGIN CERTIFICATE-----\n"
. $certificate
. "\n-----END CERTIFICATE-----";
$intermediate_certificate =
"-----BEGIN CERTIFICATE-----\n"
. $intermediate_certificate
. "\n-----END CERTIFICATE-----";
$root_certificate =
"-----BEGIN CERTIFICATE-----\n"
. $root_certificate
. "\n-----END CERTIFICATE-----";
// Verify again with Apple root certificate
if (openssl_x509_verify($root_certificate, $pem) == 1) {
$cert_object = openssl_x509_read($certificate);
$pkey_object = openssl_pkey_get_public($cert_object);
$pkey_array = openssl_pkey_get_details($pkey_object);
$publicKey = $pkey_array['key'];
// Decode the JWT using the public key
try {
$decodedData = JWT::decode($jws, new Key($publicKey, $algorithm));
var_dump($decodedData);
} catch (Exception $e) {
echo 'Header is not valid'; exit;
}
}
Solution 2:
If somehow you are unable to decode the JWS payload from above script or any error occurred then follow the other solution below:
//YOUR JWS DATA From AppStore -> $jwsData //Pass or Call JWS data "$jwsData" to below line // Decode the JWS Data //YOUR Receipt Header $jwsHeader = base64_decode(explode('.', $jwsData)[0]); var_dump($jwsHeader); //YOUR Payload Receipt Data $jwsPayload = base64_decode(explode('.', $jwsData)[1]); var_dump($jwsPayload);
This is your decoded AppStore $signedPayload JWS Data. Sample Result shown below:
This is the second part on how to Verify receipt AppStore with JWT & PHP | 2023.
Read also Part-1