Skip to content

Add FCM HTTP V1 service #171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"php": "^7.1.3|^8.0",
"guzzlehttp/guzzle": "^6.3 || ^7.0.1",
"illuminate/support": "~5.8 || ^6.0 || ^7.0 || ^8.0 || ^9.0|^10.0",
"illuminate/notifications": "~5.8 || ^6.0 || ^7.0 || ^8.0 || ^9.0|^10.0"
"illuminate/notifications": "~5.8 || ^6.0 || ^7.0 || ^8.0 || ^9.0|^10.0",
"google/apiclient": "^2.15"
},
"require-dev": {
"phpunit/phpunit": "^7.5 || ^8.0 || ^9.0"
Expand Down
57 changes: 57 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This is an easy to use package to send push notification.

* GCM
* FCM
* FCMV1
* APN

## Installation
Expand Down Expand Up @@ -59,6 +60,28 @@ $push->setConfig([
]);
```

The default configuration parameters for **FCMV1** are :

* ```priority => 'normal'```
* ```dry_run => false```
* ```projectId => 'my-project-id'```
* ```jsonFile => __DIR__ . '/fcmCertificates/file.json'```

You can dynamically update those values or adding new ones calling the method setConfig like so:
```php
$push->setConfig([
'priority' => 'high',
'projectId' => 'my-real-project-id',
'jsonFile' => 'path/to/credentials.json'
]);
```

To generate a credentials json file for your service account:

* In the Firebase console, open **Settings** > [Service Accounts](https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk).
* Click **Generate New Private Key**, then confirm by clicking **Generate Key**.
* Securely store the JSON file containing the key.


The default configuration parameters for **APN** are:

Expand Down Expand Up @@ -117,6 +140,11 @@ For FCM Service:
$push = new PushNotification('fcm');
```

For FCMV1 Service:
```php
$push = new PushNotification('fcmv1');
```

Now you may use any method that you need. Please see the API List.


Expand All @@ -139,6 +167,11 @@ Now you may use any method that you need. Please see the API List.

- [sendByTopic](https://github.com/edujugon/PushNotification#sendbytopic)

### Only for Fcmv1

- [setProjectId](https://github.com/edujugon/PushNotification#setprojectid)
- [setJsonFile](https://github.com/edujugon/PushNotification#setjsonfile)

> Go to [Usage samples](https://github.com/edujugon/PushNotification#usage-samples) directly.

#### setService
Expand Down Expand Up @@ -173,6 +206,30 @@ object setMessage(array $data)
object setApiKey($api_key)
```

#### setProjectId

> Only for fcmv1

`setProjectId` method sets the Project ID of your App as a string.

**Syntax**

```php
object setProjectId($project_id)
```

#### setJsonFile

> Only for fcmv1

`setJsonFile` method sets the path of credentials json file of your App.

**Syntax**

```php
object setJsonFile($api_key)
```

#### setDevicesToken

`setDevicesToken` method sets the devices' tokens, which you pass the token through parameter as array or string if it was only one.
Expand Down
14 changes: 14 additions & 0 deletions src/Channels/FcmV1Channel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Edujugon\PushNotification\Channels;

class FcmChannel extends GcmChannel
{
/**
* {@inheritdoc}
*/
protected function pushServiceName()
{
return 'fcmv1';
}
}
10 changes: 10 additions & 0 deletions src/Config/config.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

/**
* @see https://github.com/Edujugon/PushNotification
*/
Expand All @@ -20,6 +21,15 @@
// See https://docs.guzzlephp.org/en/stable/request-options.html
'guzzle' => [],
],
'fcmv1' => [
'priority' => 'normal',
'dry_run' => false,
'projectId' => 'my-project-id',
'jsonFile' => __DIR__ . '/fcmCertificates/file.json',
// Optional: Default Guzzle request options for each FCM request
// See https://docs.guzzlephp.org/en/stable/request-options.html
'guzzle' => [],
],
'apn' => [
'certificate' => __DIR__ . '/iosCertificates/apns-dev-cert.pem',
'passPhrase' => 'secret', //Optional
Expand Down
Empty file.
154 changes: 154 additions & 0 deletions src/FcmV1.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<?php

namespace Edujugon\PushNotification;

use Carbon\Carbon;
use Edujugon\PushNotification\Fcm;
use Exception;
use Google\Client as GoogleClient;
use Google\Service\FirebaseCloudMessaging;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;

class FcmV1 extends Fcm
{
const CACHE_SECONDS = 55 * 60; // 55 minutes

/**
* Fcm constructor.
* Override parent constructor.
*/
public function __construct()
{
$this->config = $this->initializeConfig('fcmv1');

$this->url = 'https://fcm.googleapis.com/v1/projects/' . $this->config['projectId'] . '/messages:send';

$this->client = new Client($this->config['guzzle'] ?? []);
}

/**
* Set the apiKey for the notification
* @param string $apiKey
*/
public function setApiKey($apiKey)
{
throw new Exception('Not available on FCM V1');
}

/**
* Set the projectId for the notification
* @param string $projectId
*/
public function setProjectId($projectId)
{
$this->config['projectId'] = $projectId;

$this->url = 'https://fcm.googleapis.com/v1/projects/' . $this->config['projectId'] . '/messages:send';
}

/**
* Set the jsonFile path for the notification
* @param string $jsonFile
*/
public function setJsonFile($jsonFile)
{
$this->config['jsonFile'] = $jsonFile;
}

/**
* Set the needed headers for the push notification.
*
* @return array
*/
protected function addRequestHeaders()
{
return [
'Authorization' => 'Bearer ' . $this->getOauthToken(),
'Content-Type' => 'application/json',
];
}

/**
* Send Push Notification
*
* @param array $deviceTokens
* @param array $message
*
* @return \stdClass GCM Response
*/
public function send(array $deviceTokens, array $message)
{
// FCM v1 does not allows multiple devices at once

$headers = $this->addRequestHeaders();
$jsonData = ['message' => $this->buildMessage($message)];

$feedbacks = [];

foreach ($deviceTokens as $deviceToken) {
try {
$jsonData['message']['token'] = $deviceToken;

$result = $this->client->post(
$this->url,
[
'headers' => $headers,
'json' => $jsonData,
]
);

$json = $result->getBody();

$feedbacks[] = json_decode($json, false, 512, JSON_BIGINT_AS_STRING);
} catch (ClientException $e) {
$feedbacks[] = ['success' => false, 'error' => json_encode($e->getResponse())];
} catch (\Exception $e) {
$feedbacks[] = ['success' => false, 'error' => $e->getMessage()];
}
}

$this->setFeedback($feedbacks);
}

/**
* Prepare the data to be sent
*
* @param $topic
* @param $message
* @param $isCondition
* @return array
*/
protected function buildData($topic, $message, $isCondition)
{
$condition = $isCondition ? ['condition' => $topic] : ['to' => '/topics/' . $topic];

return [
'message' => array_merge($condition, $this->buildMessage($message)),
];
}

protected function getOauthToken()
{
return Cache::remember(
Str::slug('fcm-v1-oauth-token-' . $this->config['projectId']),
Carbon::now()->addSeconds(self::CACHE_SECONDS),
function () {
$jsonFilePath = $this->config['jsonFile'];

$googleClient = new GoogleClient();

$googleClient->setAuthConfig($jsonFilePath);
$googleClient->addScope(FirebaseCloudMessaging::FIREBASE_MESSAGING);

$accessToken = $googleClient->fetchAccessTokenWithAssertion();

$oauthToken = $accessToken['access_token'];

return $oauthToken;
}
);
}
}
3 changes: 2 additions & 1 deletion src/PushNotification.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ class PushNotification
protected $servicesList = [
'gcm' => Gcm::class,
'apn' => Apn::class,
'fcm' => Fcm::class
'fcm' => Fcm::class,
'fcmv1' => FcmV1::class,
];

/**
Expand Down