Download the PHP package esanj/notification-client without Composer

On this page you can find all versions of the php package esanj/notification-client. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package notification-client

Esanj Notification Client

Laravel client package for the Esanj Notification Microservice. Handles OAuth 2.0 token acquisition, automatic caching, token refresh, and retry logic out of the box.

Supports: Laravel 12 & 13 · PHP 8.2+


Installation

Publish the config file:


Configuration

Add the following variables to your .env file:

The first three are required. If any is missing when the client is resolved, you get a ConfigurationException naming the variable — not a TypeError from inside the package. NOTIFICATION_SERVICE_URL must be a full URL, and must use https:// when the app runs in production: the client-credentials call carries your client_secret, and plain HTTP hands it to anyone on the network path.

Full config reference (config/esanj/notification.php):


Token Management

Token handling is fully automatic:

  1. On the first request the package fetches a token via the OAuth 2.0 client-credentials flow (POST /api/v1/oauth/token).
  2. The response is validated before anything is cached — it must carry a usable access_token and a numeric expires_in longer than buffer_seconds. A response missing either is rejected with an AuthenticationException rather than cached as a token that is already expired.
  3. The token is stored in your configured cache store with a TTL equal to expires_in - buffer_seconds. Set NOTIFICATION_TOKEN_ENCRYPT=true to encrypt that cache entry with your APP_KEY — worth doing when the store is shared with anything you don't fully trust.
  4. A fast in-memory copy avoids cache I/O on subsequent calls within the same process.
  5. Fetching happens behind a cache lock. When the cache is cold — a deploy, a Redis restart, an invalidation — one process fetches the token while the others wait and then read its result, instead of twenty workers hitting the throttled token endpoint at once.
  6. If a request receives an HTTP 401, the package invalidates the cached token, fetches a fresh one, and replays the request once. A second 401 means the credentials themselves are wrong, so it throws instead of hammering the token endpoint.
  7. An HTTP 403 is left alone: the token is valid, the client simply has no permission for that endpoint. Refreshing would drop a healthy token for nothing — see $e->isForbidden().
  8. If all retries fail, an ApiException (or AuthenticationException) is thrown and the error is logged.

Retry & Idempotency

A lost response does not mean the service ignored the request. The service creates the notification and queues the job before it answers, so replaying a POST /api/v1/send that timed out sends the message a second time. The client therefore retries by method:

Situation Retried?
GET on a 5xx or connection error Yes, up to retry.attempts.
401 on any method Once — the token is refreshed and the call replayed immediately (a rejected token proves the request was never processed). A second 401 throws.
429 on any method Yes — waits for the server's Retry-After (capped at 30s) and replays. The throttle rejects before any processing happens, so this is safe for sends too.
send / sendBatch on a 5xx or timeout Only when idempotency.enabled is true.
400, 403, and every other 4xx Never — an ApiException is thrown at once.

Waits grow exponentially and carry jitter: retry.sleep_ms doubles per attempt (1s, 2s, 4s… capped at 10s) with half of each delay randomised, so clients that fail at the same moment don't all retry at the same moment. A 429 keeps the server's Retry-After as the floor and adds up to a second of spread on top.

With idempotency.enabled = false (the default) a failed send throws after a single attempt. Handle it yourself — usually by letting the queued job retry with a key of your own, see below.

Set NOTIFICATION_IDEMPOTENCY=true only if the service honours the Idempotency-Key header and replays the original response for a repeated key. The client then sends a fresh key with every send and reuses it across that call's retries, so a duplicate never reaches your users.

For a send that your application itself can issue twice — a queued job that gets retried, a form the user double-submits — pass a stable key so both attempts collapse into one notification:

The same parameter exists on SendBatchNotificationData.


Usage

Dependency Injection (recommended)

Facade


Sending Notifications

SMS — plain message

SMS — pattern (template code)

Email

Push Notification

Using a Template (any channel)

Targeting a Specific Provider

Adding Tags


Batch Notifications


Querying Notifications

List with filters

Walk every page

eachNotification() pulls one page at a time and yields the items lazily, so the whole result set never has to fit in memory:

Driving the loop by hand works too — advance the filter, otherwise you keep re-fetching page 1:

Get single notification

Batches


Providers & Tags


Error Handling

All exceptions extend Esanj\NotificationClient\Exceptions\NotificationClientException.

Exception When thrown
AuthenticationException Cannot fetch/refresh OAuth token (bad credentials, service unreachable)
RateLimitException The token endpoint returned 429. Credentials are valid — you're just asking for tokens too often
ApiException Non-retriable HTTP error (4xx, persistent 5xx, or a 429 that survived the back-off)
ConfigurationException The package isn't configured — a missing NOTIFICATION_* env var, a base_url that isn't a URL, or plain HTTP in production. Thrown when the client is resolved, and the message names the variable to set
UnexpectedResponseException HTTP 200, but the body isn't usable JSON (a proxy or WAF page), or the payload is missing a field the contract guarantees. The message quotes the body or names the field
NotificationClientException Base class — all exceptions above extend this

Fields the service may legitimately leave empty are typed nullable rather than blowing up: providerName and providerChannel (null when the underlying provider record is gone), and updatedAt on notifications, batches and tags (null instead of being parsed into a fake "now").

RateLimitException and ApiException::isRateLimited() both carry $e->retryAfter — the server's Retry-After in seconds, or null when it didn't send one. It's exactly what $job->release() wants.

What the status codes mean

ApiException has a helper for each one, so you never have to compare $e->statusCode by hand:

Status Helper What happened
0 isConnectionError() No response at all — timeout or refused connection
400 isBadRequest() The request is well-formed but unusable: no active provider for this channel, or the chosen provider doesn't support it. Not a field error, so getErrors() is empty — read $e->getMessage()
401 isUnauthorized() The token was rejected. The client already refreshed and retried once
403 isForbidden() / isPermissionDenied() The token is fine; this client lacks the service permission for that endpoint. Grant it on the service
404 isNotFound() No notification, batch, tag or provider with that identifier
422 isValidationError() Field-level validation failed — getErrors() returns the map
429 isRateLimited() Throttled, and still throttled after the client backed off. $e->retryAfter holds the server's hint
5xx isServerError() The service failed, and every allowed retry was used

isClientInputError() covers 400 and 422 — the two cases where the fix is in what you sent, not in retrying. Checking only isValidationError() silently misses the "no active provider" case.


Testing

The package integrates cleanly with Guzzle's MockHandler. In your feature tests:


Available Payload Classes

Class Channel Factory
SmsPayload SMS SmsPayload::fromMessage('text')
SmsPatternPayload SMS SmsPatternPayload::make('key', ['var' => 'val'])
EmailPayload Email EmailPayload::make()->subject(...)->html(...)
PushPayload Push PushPayload::make()->title(...)->body(...)
TemplatePayload Any TemplatePayload::make('key')->variables([...])->language('fa')

Resource Properties

NotificationResource

Property Type Description
uuid string Unique notification identifier
status string pending | queued | processing | sent | failed | delivered | undelivered
channel string sms | email | push
recipient string Recipient address / token
batchUuid string\|null Parent batch UUID if sent as part of a batch
sentAt CarbonImmutable\|null When the message was sent
createdAt CarbonImmutable
updatedAt CarbonImmutable\|null Null when the service didn't send one

BatchResource

Property Type Description
uuid string Unique batch identifier
status string pending | processing | canceled | completed
totalNotifications int Number of notifications in the batch
processedNotifications int Notifications processed so far
createdAt CarbonImmutable
updatedAt CarbonImmutable\|null Null when the service didn't send one
progressPercentage() float Computed progress 0–100

ProviderResource

Property Type Description
id int Client-provider row id
providerName string\|null Null if the provider record no longer exists
providerChannel string\|null Null for the same reason
providerId int The provider this row points at
orderColumn int Selection order
createdAt CarbonImmutable

TagResource

Property Type Description
id int
name string
description string\|null
color string\|null
usedCount int How many notifications carry the tag
createdAt CarbonImmutable
updatedAt CarbonImmutable\|null Null when the service didn't send one

Documentation

For a complete, beginner-friendly, step-by-step walkthrough — installing, sending your first notification, building custom payloads, swapping the client implementation, testing, and troubleshooting — see docs/GUIDE.md.

Changelog

See CHANGELOG.md for release history.

License

MIT — © Esanj


All versions of notification-client with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2|^8.3|^8.4
ext-mbstring Version *
ext-json Version *
illuminate/support Version ^12.0|^13.0
illuminate/contracts Version ^12.0|^13.0
illuminate/cache Version ^12.0|^13.0
illuminate/log Version ^12.0|^13.0
guzzlehttp/guzzle Version ^7.5
nesbot/carbon Version ^2.72|^3.0
psr/http-message Version ^1.1|^2.0
psr/log Version ^1.0|^2.0|^3.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package esanj/notification-client contains the following files

Loading the files please wait ...