<?php
namespace Pusher\PushNotifications;
use Firebase\JWT\JWT;
use GuzzleHttp;
/**
* Pusher Push Notifications client class
* Used to publish notifications to the Pusher Push Notifications API
* http://www.pusher.com/push-notifications
*/
class PushNotifications {
const SDK_VERSION = "2.0.0";
const MAX_INTERESTS = 100;
const MAX_INTEREST_LENGTH = 164;
const INTEREST_REGEX = "/^(_|-|=|@|,|\\.|;|[A-Z]|[a-z]|[0-9])+$/";
const MAX_USERS = 1000;
const MAX_USER_ID_LENGTH = 164;
const AUTH_TOKEN_DURATION_SECS = 24 * 60 * 60;
private GuzzleHTTP\Client $client;
public function __construct(private array $options, GuzzleHTTP\Client|null $client = null) {
if (!array_key_exists("instanceId", $this->options)) {
throw new \Exception("Required 'instanceId' in Pusher\PushNotifications constructor options");
}
if (!is_string($this->options["instanceId"])) {
throw new \Exception("'instanceId' must be a string");
}
if ($this->options["instanceId"] === "") {
throw new \Exception("'instanceId' cannot be the empty string");
}
if (!array_key_exists("secretKey", $this->options)) {
throw new \Exception("Required 'secretKey' in Pusher\PushNotifications constructor options");
}
if (!is_string($this->options["secretKey"])) {
throw new \Exception("'secretKey' must be a string");
}
if ($this->options["secretKey"] === "") {
throw new \Exception("'secretKey' cannot be the empty string");
}
if (!array_key_exists("endpoint", $this->options)) {
$this->options["endpoint"] = "https://" . $options["instanceId"] . ".pushnotifications.pusher.com";
} else {
if (!is_string($this->options["endpoint"])) {
throw new \Exception("'endpoint' must be a string");
}
if ($this->options["endpoint"] === "") {
throw new \Exception("'endpoint' cannot be the empty string");
}
}
if (!$client) {
$this->client = new GuzzleHttp\Client();
} else {
$this->client = $client;
}
}
private function makeRequest(string $method, string $path, array $pathParams, array|null $body = null): mixed {
$escapedPathParams = [];
foreach ($pathParams as $k => $v) {
$escapedPathParams[$k] = urlencode($v);
}
$endpoint = $this->options["endpoint"];
$interpolatedPath = strtr($path, $escapedPathParams);
$url = $endpoint . $interpolatedPath;
try {
$response = $this->client->request(
$method,
$url,
[
"headers" => [
"Authorization" => "Bearer " . $this->options["secretKey"],
"X-Pusher-Library" => "pusher-push-notifications-php " . PushNotifications::SDK_VERSION
],
"json" => $body
]
);
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
$response = $e->GetResponse();
$parsedResponse = json_decode($response->GetBody());
$badJSON = $parsedResponse === null;
if (
$badJSON ||
!property_exists($parsedResponse, 'error') ||
!property_exists($parsedResponse, 'description')
) {
throw new \Exception("An unexpected server error has occurred");
}
throw new \Exception("{$parsedResponse->error}: {$parsedResponse->description}");
}
$parsedResponse = json_decode($response->GetBody());
return $parsedResponse;
}
/**
* @param array $interests
* @param array<string> $publishRequest
* @return mixed
* @throws \Exception
*/
public function publishToInterests(array $interests, array $publishRequest): mixed {
if (count($interests) === 0) {
throw new \Exception("Publishes must target at least one interest");
}
if (count($interests) > PushNotifications::MAX_INTERESTS) {
throw new \Exception("Number of interests exceeds maximum of " . PushNotifications::MAX_INTERESTS);
}
foreach($interests as $interest) {
if (!is_string($interest)) {
throw new \Exception("Interest \"$interest\" is not a string");
}
if (mb_strlen($interest) > PushNotifications::MAX_INTEREST_LENGTH) {
throw new \Exception("Interest \"$interest\" is longer than the maximum length of " . PushNotifications::MAX_INTEREST_LENGTH . " chars.");
}
if ( $interest === '' ) {
throw new \Exception("Interest names cannot be the empty string");
}
if (!preg_match(PushNotifications::INTEREST_REGEX, $interest)) {
throw new \Exception(implode([
"Interest \"$interest\" contains a forbidden character.",
" Allowed characters are: ASCII upper/lower-case letters,",
" numbers or one of _=@,.;-"
]));
}
}
$publishRequest['interests'] = $interests;
$path = '/publish_api/v1/instances/INSTANCE_ID/publishes/interests';
$pathParams = [
'INSTANCE_ID' => $this->options["instanceId"]
];
$response = $this->makeRequest("POST", $path, $pathParams, $publishRequest);
if ($response === null) {
throw new \Exception("An unexpected server error has occurred");
}
return $response;
}
public function publishToUsers(array $userIds, array $publishRequest): mixed {
if (count($userIds) === 0) {
throw new \Exception("Publishes must target at least one user");
}
if (count($userIds) > PushNotifications::MAX_USERS) {
throw new \Exception("Number of user ids exceeds maximum of " . PushNotifications::MAX_USERS);
}
foreach($userIds as $userId) {
$this->checkUserId($userId);
}
$publishRequest['users'] = $userIds;
$path = '/publish_api/v1/instances/INSTANCE_ID/publishes/users';
$pathParams = [
'INSTANCE_ID' => $this->options["instanceId"]
];
$response = $this->makeRequest("POST", $path, $pathParams, $publishRequest);
if ($response === null) {
throw new \Exception("An unexpected server error has occurred");
}
return $response;
}
public function deleteUser(string $userId): void {
$this->checkUserId($userId);
$path = '/customer_api/v1/instances/INSTANCE_ID/users/USER_ID';
$pathParams = [
'INSTANCE_ID' => $this->options["instanceId"],
'USER_ID' => $userId
];
$this->makeRequest("DELETE", $path, $pathParams);
}
public function generateToken(string $userId): array {
$this->checkUserId($userId);
$instanceId = $this->options["instanceId"];
$secretKey = $this->options["secretKey"];
$issuer = "https://$instanceId.pushnotifications.pusher.com";
$claims = [
"iss" => $issuer,
"sub" => $userId,
"exp" => time() + PushNotifications::AUTH_TOKEN_DURATION_SECS
];
$token = JWT::encode($claims, $secretKey, 'HS256');
return [
"token" => $token
];
}
private function checkUserId(string $userId): void {
if ($userId === '') {
throw new \Exception("User id cannot be the empty string");
}
if (mb_strlen($userId) > PushNotifications::MAX_USER_ID_LENGTH) {
throw new \Exception("User id \"$userId\" is longer than the maximum length of " . PushNotifications::MAX_USER_ID_LENGTH . " chars.");
}
}
public function getClient(): GuzzleHttp\Client
{
return $this->client;
}
}
Service Section
Discover how our services are designed to enhance your NFC Pay experience with convenience, security, and innovative solutions. From managing transactions to secure payments, we are dedicated to providing seamless support every step of the way.
Easily save your credit and debit card details within our app for quick and secure transactions. This feature ensures that your payment information is protected with advanced encryption and can be used for future purchases with just a tap.
Transfer funds quickly and securely between users with our streamlined money transfer service. Simply select the recipient, enter the amount, and authorize the transaction for instant, hassle-free transfers.
Activate your merchant account effortlessly to start receiving payments. Our intuitive setup process ensures that you can begin accepting transactions smoothly, helping your business thrive with minimal setup time.
Keep track of all your transactions in real time through our app. Monitor payment statuses, view transaction history, and manage your account efficiently, ensuring complete control over your financial activities.
Our dedicated support team is available to assist you with any queries or issues. Whether you need help with setting up your account or resolving transaction-related questions, we’re here to provide prompt and reliable assistance.