Category: software-development

Software development is the process of conceiving, specifying, designing, programming, documenting, testing, and bug fixing involved in creating and maintaining applications, frameworks, or other software components. Software development involves writing and maintaining the source code, but in a broader sense, it includes all processes from the conception of the desired software through to the final manifestation of the software, typically in a planned and structured process. Software development also includes research, new development, prototyping, modification, reuse, re-engineering, maintenance, or any other activities that result in software products.

  • App Store Server Notifications V2 in Laravel (Step-by-Step Guide + PHP JWT Example)

    App Store Server Notifications V2 in Laravel (Step-by-Step Guide + PHP JWT Example)

    App Store Server Notifications

    App Store Server Notifications V2 allows Apple to notify your backend in real time about subscription events such as renewals, cancellations, refunds, and billing issues. In this guide, you’ll learn how to decode, verify, and process App Store Server Notifications V2 using PHP and Laravel, with practical examples and best practices for production use.

    App offering subscription based products must use App Store Server Notification to verify the purchase, renew the subscription, cancel the subscription, and more.

    What are App Store Server Notifications?

    App Store Server Notifications are webhooks sent by Apple to your server whenever a significant event happens to an in-app purchase or auto-renewable subscription.

    Common events include:

    • Subscription renewal
    • Cancellation or expiration
    • Refunds and revocations
    • Billing retry failures
    • Price increases

    With Server Notifications V2, Apple sends a signed payload (JWT) that contains detailed transaction and renewal information, allowing your backend to stay in sync without relying solely on client-side validation.

    Why Use Version 2 (vs V1)?

    Apple introduced Server Notifications V2 to replace V1 with a more secure, structured, and extensible format.

    Key advantages of V2:

    • Uses JWT (JSON Web Tokens) for payload security
    • Provides richer transaction and renewal data
    • Supports App Store Server API integration
    • Better future compatibility with Apple’s subscription system

    If you’re building or maintaining a modern subscription-based app, V2 is the recommended and future-proof choice.

    How Apple Server Notifications Work
    The notification flow looks like this:

    1. A subscription event occurs on the App Store
    2. Apple sends a POST request to your webhook endpoint
    3. The request contains a signedPayload
    4. Your server:
      • Decodes the JWT
      • Verifies the signature
      • Extracts transaction and renewal data
    5. Your backend updates the user subscription status accordingly

    This process ensures your server remains the source of truth for subscription state.

    My Intentions

    My main goal is to retrieve the originalTransactionId from the notification data. Then, when a subscription expires or Is Refunded, it will downgrade the user’s subscription status.

    Related reading

    Step-by-Step Implementation (without library)

    If you prefer full control, you can implement Server Notifications V2 without any third-party libraries.

    High-level steps:

    1. Read the raw POST body from Apple
    2. Extract the signedPayload
    3. Split the JWT into header, payload, and signature
    4. Base64-decode the payload
    5. Parse and process the JSON data

    This approach is useful for:

    • Learning how V2 works internally
    • Minimal dependencies
    • Custom verification logic

    However, you must be careful with signature verification and edge cases.

    Implementation with JWT Library

    Using a JWT library simplifies decoding and validation while reducing security risks.

    Typical steps:

    1. Install a trusted JWT library
    2. Decode the signedPayload
    3. Validate the JWT signature using Apple’s public key
    4. Extract:
      • notificationType
      • subtype
      • transactionInfo
      • renewalInfo

    This method is recommended for most production systems because it is safer, cleaner, and easier to maintain.

    App Store Configuration

    User purchases we verify Apple’s receipt and we save originalTransactionId in our database. So each user has originalTransactionId and we can find the user with this originalTransactionId. You can read more about this here Auto-renewable subscriptions with SwiftUI

    Make sure you set your app store notification URL on the AppStoreConnect. Choose Version 2 Notifications.

    App Store Server Notifications  V2 with JWT, PHP and Laravel

    Each data will look like this, call it signedPayload.

    App Store Server Notifications  V2 with JWT, PHP and Laravel

    The signedPayload object is a JWS representation. To get the transaction and subscription renewal details from the notification payload, process the signedPayload as follows:

    1. Parse signedPayload to identify the JWS header, payload, and signature representations.
    2. Base64 URL-decode the payload to get the responseBodyV2DecodedPayload. The decoded payload contains the notificationType , subtype, other notification metadata, and a data object.
    3. The data object contains a signedTransactionInfo (JWSTransaction) and depending on the notification type, a signedRenewalInfo (JWSRenewalInfo). Parse and Base64 URL-decode these signed JWS representations to get transaction and subscription renewal details.

    Each of the signed JWS representations, signedPayloadsignedTransactionInfo, and signedRenewalInfo, have a JWS signature that you can validate on your server. Use the algorithm specified in the header’s alg parameter to validate the signature. For more information about validating signatures, see the JSON Web Signature (JWS) IETF RFC 7515 specification.

    Hopefully, you are already getting this data. Now let’s get originalTransactionId from this. We will do this without any 3rd party library first to understand the process.

    First thing, I will download the Apple root certificate and make it.PEM file from it.

    Download the certificate https://www.apple.com/certificateauthority/AppleRootCA-G3.cer

    Get .PEM file from it, on your Mac Terminal

    openssl x509 -in AppleRootCA-G3.cer -out apple_root.pem

    For testing purposes, I’m loading apple_root.pem and my signedPayload from a file called notification.json (replace it with file_get_contents(‘php://input’);) and then decoding the signedPayload. signedPayload has three parts, separated by .(dot), line 13.

    The first part is the header, the Second part is the body (payload), and the Third part is the signature. The header has an algorithm and x5c, x5c has three elements. Certificate, intermediate certificate, and root certificate. We can verify the certificate in two steps. Once the verification is completed, we know we have signedPayload from Apple.

    Finally, decode the payload again and get the originalTransactionId from lines 56 to 65.

    Server Side

    For this article, our production server URL looks like inafiz.com/jwt.php. You can get whatever Apple sends you and write a log in your server if you are interested.

    $appleData = file_get_contents('php://input');
    $file = fopen(
        "/var/www/html/appstore_prod.log", "a"
    );
    fwrite($file, $appleData);
    fclose($file);
    

    Without any 3rd party library(not recommended).

    <?php
    
    ini_set('display_errors', 1);
    error_reporting(E_ALL);
    
    // Download the certificate -> https://www.apple.com/certificateauthority/AppleRootCA-G3.cer
    // Convert it to .PEM file, run on macOS terminal ->  ```bash openssl x509 -in AppleRootCA-G3.cer -out apple_root.pem```
    
    $pem = file_get_contents('apple_root.pem');
    $data = file_get_contents('notification.json'); // replace with file_get_contents('php://input');
    $json = json_decode($data);
    
    $header_payload_secret = explode('.', $json->signedPayload);
    
    //------------------------------------------
    // 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];
    
    $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 the notification request   
    //------------------------------------------
    if (openssl_x509_verify($intermediate_certificate, $root_certificate) != 1){ 
        echo 'Intermediate and Root certificate do not match';
        exit;
    }
    
    // Verify again with Apple root certificate
    if (openssl_x509_verify($root_certificate, $pem) == 1){
        //------------------------------------------
        // Payload
        //------------------------------------------
        // https://developer.apple.com/documentation/appstoreservernotifications/notificationtype
        // https://developer.apple.com/documentation/appstoreservernotifications/subtype
        $payload = json_decode(base64_decode($header_payload_secret[1]));
        $notificationType = $payload->notificationType;
        $subtype = $payload->subtype;
    
        if ($notificationType == "EXPIRED" || $notificationType == "REFUND") {
            $transactionInfo = $payload->data->signedTransactionInfo;
            $ti = explode('.', $transactionInfo);
            
            $data = json_decode(base64_decode($ti[1]));
            var_dump($data); // this will contain our originalTransactionId
        }
    } else {
        echo 'Header is not valid';
        exit;
    }

    Using firebase/php-jwt in composer (recommended), big difference is to use the public key to decode the payload using JWT.

    composer require firebase/php-jwt
    <?php
    
    ini_set('display_errors', 1);
    error_reporting(E_ALL);
    
    // No need these 3 lines for Laravel
    require_once './vendor/firebase/php-jwt/src/JWT.php';
    require_once './vendor/firebase/php-jwt/src/JWK.php';
    require_once './vendor/firebase/php-jwt/src/Key.php';
    
    use Firebase\JWT\JWT;
    use Firebase\JWT\Key;
    
    // Download the certificate -> https://www.apple.com/certificateauthority/AppleRootCA-G3.cer
    // Convert it to .PEM file, run on macOS terminal ->  ```bash openssl x509 -in AppleRootCA-G3.cer -out apple_root.pem```
    
    $pem = file_get_contents('apple_root.pem');
    
    $data = file_get_contents('notification.json');  // replace with file_get_contents('php://input');
    $json = json_decode($data);
    
    $header_payload_secret = explode('.', $json->signedPayload);
    
    //------------------------------------------
    // 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];
    
    $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 the notification request   
    //------------------------------------------
    
    if (openssl_x509_verify($intermediate_certificate, $root_certificate) != 1){ 
        echo 'Intermediate and Root certificate do not match';
        exit;
    }
    
    // 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'];
    
        //------------------------------------------
        // Payload
        //------------------------------------------
        $payload = json_decode(base64_decode($header_payload_secret[1]));
        $notificationType = $payload->notificationType;
    
        //if ($notificationType == "EXPIRED" || $notificationType == "REFUND") {
            $transactionInfo = $payload->data->signedTransactionInfo;
            $signedRenewalInfo = $payload->data->signedRenewalInfo;
    
            $transactionDecodedData = JWT::decode($transactionInfo, new Key($publicKey, $algorithm));
            var_dump($transactionDecodedData->originalTransactionId);
            echo "========================================";
            $signedRenewalDecodedData = JWT::decode($signedRenewalInfo, new Key($publicKey, $algorithm));
            var_dump($signedRenewalDecodedData);
        //}
    
    } else {
        echo 'Header is not valid';
        exit;
    }

    Conclusion & Next Steps

    App Store Server Notifications V2 are essential for managing subscriptions reliably in modern iOS apps.

    By implementing them correctly in PHP and Laravel, you can:

    • Keep subscription states accurate
    • React to real-time billing events
    • Reduce reliance on client-side receipt validation

    Next steps:

    • Add App Store Server API integration
    • Store originalTransactionId as a primary key
    • Build retry-safe webhook processing
    • Automate subscription audits

    If you’d like a full Laravel package or production-ready webhook example, feel free to reach out or leave a comment

    Download the source code

    Spread the love
  • Protocol in Swift

    Protocol in Swift

    Protocol in Swift

    In Swift, a protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol.

    Protocols are a way to define a set of requirements for any type to conform to. This means that you can use protocols to define the expected behavior for a particular task or piece of functionality, without worrying about how that functionality will be implemented. This makes it easy to write flexible and reusable code that can be used in a variety of contexts.

    For example, if you were creating a game, you might define a protocol called “Level” that specifies the properties and methods that all levels in the game must have. Then, any class that adopts the Level protocol must implement those properties and methods, so that they can be used in the game.

    Here is an example of how a protocol might be defined in Swift:

    protocol Level {
        var name: String { get set }
        func complete()
        func start()
    }
    

    In this example, the Level protocol defines two properties (name and difficulty) and two methods (complete and start). Any type that adopts the Level protocol must implement these properties and methods, so that it can be used in the game.

    To adopt a protocol, you write the protocol’s name after the class name, separated by a colon. Here is an example of how a class might adopt the Level protocol from the previous example:

    class ForestLevel: Level {
        var name: String = "Forest"
        var difficulty: Int = 3
    
        func complete() {
            print("You have completed the Forest level!")
        }
    
        func start() {
            print("Starting the Forest level...")
        }
    }
    

    In this example, the ForestLevel class adopts the Level protocol and provides an implementation of its requirements. This means that the ForestLevel class can be used as a level in the game, and it will have the properties and methods specified by the Level protocol.

    Spread the love
  • Create Notification on Android in Java

    Create Notification on Android in Java

    Create Notification on Android in Java

    To create a notification on Android in Java, you can use the NotificationCompat.Builder class and its setContentTitle(), setContentText(), and setSmallIcon() methods to set the title, text, and icon for the notification, respectively. You can then use the getNotification() method to retrieve the notification object, and the notify() method of the NotificationManager class to post the notification.

    Here is an example of how you could create and post a notification in Java on Android:

    // Create the NotificationCompat.Builder object
    NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
            .setContentTitle("Notification Title")
            .setContentText("This is the notification text.")
            .setSmallIcon(R.drawable.notification_icon);
    
    // Get the Notification object
    Notification notification = builder.build();
    
    // Get the NotificationManager and post the notification
    NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    manager.notify(NOTIFICATION_ID, notification);
    

    This code will create a notification with the specified title and text, and using the icon specified by the notification_icon drawable resource. When the notification is posted, it will be assigned the ID specified by the NOTIFICATION_ID constant.

    Spread the love