APNs (Apple Push Notification service) using Laravel
Published: 2024-06-28 15:28:40

In this post, I will discuss the only Laravel end functionality, assuming you have 'certificate.p12', 'password', and 'app_bundel_id' from Apple 

**Step 1**
If you have 'Certificates.p12' you need to convert this to 'Certificates.pem' file. I have stored my 'Certificates.p12' file in the 'storage' directory.

In .env 

```
APN_ENVIRONMENT=sandbox
APN_CERTIFICATE_PATH=storage/Certificates.p12
APN_CERTIFICATE_PASSWORD=123456
APN_APP_BUNDLE_ID=com.btrac.ems
```
Now run the below comment to your project terminal
`openssl pkcs12 -in storage/Certificates.p12 -out storage/Certificates.pem -nodes -clcerts`
that command convert my 'Certificates.p12' file into 'Certificates.pem' file

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qwogf0o32v7brprnyasf.png)

**Step 2**
Create app/helper.php 

```
<?php

if (! function_exists('createApnPayload')) {
    function createApnPayload($title, $body) {
        return json_encode([
            'aps' => [
                'alert' => [
                    'title' => $title,
                    'body' => $body,
                ],
                'sound' => 'default',
            ],
        ]);
    }
}

if (! function_exists('sendApnNotification')) {
    function sendApnNotification($deviceToken, $payload) {
        $certificatePath = storage_path('Certificates.pem');
        $passphrase = env('APN_CERTIFICATE_PASSWORD', ''); // Use the passphrase if set, otherwise empty

        $url = "https://api.sandbox.push.apple.com/3/device/$deviceToken"; // Use 'api.push.apple.com' for production

        $headers = [
            'apns-topic: ' . env('APN_APP_BUNDLE_ID'),
            'Content-Type: application/json',
        ];

        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_PORT, 443);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSLCERT, $certificatePath);
        curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $passphrase);
        curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);

        $response = curl_exec($ch);

        if (curl_errno($ch)) {
            throw new Exception('cURL error: ' . curl_error($ch));
        }

        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        curl_close($ch);

        if ($httpCode == 200) {
            return 'Notification sent successfully';
        } else {
            return 'Notification failed with HTTP code ' . $httpCode . ' and response ' . $response;
        }
    }
}


```


**Step 3**
Create a Controller for example 'NotificationController.php' 

```
public function sendNotification()
    {
        $deviceToken = 'Your_device_token';
        $title = 'Hello';
        $body = 'Hello from the other side';

        $payload = createApnPayload($title, $body);

        try {
            $result = sendApnNotification($deviceToken, $payload);
            return response()->json(['message' => $result]);
        } catch (\Exception $e) {
            return response()->json(['error' => 'Notification failed: ' . $e->getMessage()], 500);
        }
    }
```

**Step 4**
Make a new route for your notification function in web.php
`Route::get('/sendNotification3', [NotificationController::class, 'sendNotification']);`

Now you can test the notification functionality form Postman or browser or call the function from any other function depending on your business logic.


**Save device token**

```
public function userDeviceToken(Request $request, $profileId)
    {
        $token = $request->device_token;

        $user = User::find($profileId);

        if ($user) {
            $user->device_token = $token;
            $user->save();
            $response = [
                'code' => 200,
                'message' => 'Device token updated successfully',
                'device_token' => $token
            ];
            return response()->json([
                'results' => $response
            ], 200);
        } else {
            return response()->json([
                'results' => [
                    'code' => 404,
                    'message' => 'User not found'
                ]
            ], 404);
        }
    }
```


Please note that you need to be more careful about error handling since in this post I have used CURL rather than any packages