atanunu / laravel-xpresswallet
Laravel integration for Providus Xpress Wallet API with token storage, caching, logging and audit.
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 3
pkg:composer/atanunu/laravel-xpresswallet
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.8|^8.0
- illuminate/support: ^11.0|^12.0
Requires (Dev)
- infection/infection: ^0.29 || ^0.31
- larastan/larastan: ^2.9
- laravel/pint: ^1.14
- orchestra/testbench: ^9.0|^10.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
- phpstan/phpstan: ^1.11
- roave/security-advisories: dev-latest
- vimeo/psalm: ^5.26 || ^6.0 || dev-master
This package is auto-updated.
Last update: 2026-01-11 16:45:31 UTC
README
This is a Laravel SDK for the Providus Bank XpressWallet API. It handles:
- Login and token refresh (X-Access-Token / X-Refresh-Token)
- Secure token storage (DB) + caching
- Request/response logging & auditing (masking, truncation, correlation IDs)
- Automatic retries with exponential (full-jitter) backoff & 429 handling
- Automatic 401 refresh
- Circuit breaker (configurable failure threshold & cool-down)
- Rate limiting detection & events
- Optional GET response caching
- Idempotency-Key automatic header for unsafe methods
- Pagination helper (
paginate()) - Webhook verification (multi-secret rotation & optional async queue)
- OpenTelemetry tracing (optional)
- Example controllers & routes
- Pruning command for old logs & limiting token history
- Publishable config & migrations
- Testbench-powered tests, GitHub Actions CI, PHPStan level 8, Psalm (complementary), mutation testing config
- Public endpoint coverage dashboard: https://atanunu.github.io/laravel-xpresswallet/
- HTML API Guide (optional proxy routes): https://atanunu.github.io/laravel-xpresswallet/api-guide.html
- Automatic Packagist sync on pushes & tags (set secrets
PACKAGIST_USERNAME&PACKAGIST_TOKEN).
Installation
composer require atanunu/laravel-xpresswallet php artisan vendor:publish --provider="Atanunu\XpressWallet\XpressWalletServiceProvider" --tag=xpresswallet-config php artisan vendor:publish --provider="Atanunu\XpressWallet\XpressWalletServiceProvider" --tag=xpresswallet-migrations php artisan migrate
Configuration Overview
Once published, open config/xpresswallet.php. Key sections & env flags:
Core Credentials:
XPRESSWALLET_BASE_URL– API base (e.g. sandbox URL)XPRESSWALLET_EMAIL,XPRESSWALLET_PASSWORD– raw credentials (auto base64 on login)
Retry & Backoff:
XPRESSWALLET_RETRY_ATTEMPTS(default 2 total attempts)XPRESSWALLET_RETRY_DELAY/XPRESSWALLET_RETRY_MAX_DELAYXPRESSWALLET_RETRY_FULL_JITTER(bool) – full jitter algorithmXPRESSWALLET_RATE_LIMIT_ATTEMPTS– separate cap for 429 retries
Authentication & Tokens:
XPRESSWALLET_AUTO_REFRESH– auto refresh on first 401- Cache TTL & keys under
cachearray
Logging:
XPRESSWALLET_LOG_BODIES– log raw bodies (careful in prod)XPRESSWALLET_MAX_BODY– truncate lengthXPRESSWALLET_MASK_TOKENS– mask auth headers
Retention & Pruning:
XPRESSWALLET_RETENTION_DAYS– older logs/webhooks removed by prune commandXPRESSWALLET_MAX_TOKENS– keep only latest N token rows
Webhooks:
XPRESSWALLET_WEBHOOK_SECRET– primary secret (legacy)XPRESSWALLET_WEBHOOK_SECRETS– comma-separated list for rotationXPRESSWALLET_WEBHOOK_SIGNATURE_HEADERXPRESSWALLET_WEBHOOK_TOLERANCEXPRESSWALLET_WEBHOOK_ASYNC– queue processing (provide queue config)
Response Caching:
XPRESSWALLET_CACHE_GET_TTL– seconds to cache successful GETs (0 disables)
Correlation IDs:
XPRESSWALLET_CORRELATION_HEADER– header injected; logged & can propagate downstream
Circuit Breaker:
XPRESSWALLET_CB_ENABLEDXPRESSWALLET_CB_FAILURES– consecutive failures to openXPRESSWALLET_CB_COOLDOWN– cool-down in seconds
Idempotency:
XPRESSWALLET_IDEMPOTENCY_AUTO(bool)XPRESSWALLET_IDEMPOTENCY_HEADER(defaultIdempotency-Key)
OpenTelemetry:
XPRESSWALLET_OTEL_ENABLEDXPRESSWALLET_OTEL_SERVICE
All settings may be overridden per environment. Keep secrets in .env and do not commit them.
Quick start
use Atanunu\XpressWallet\Facades\XpressWallet; $response = XpressWallet::customers()->all();
Example routes
After installing in a Laravel app, you can load example routes:
Route::prefix('xpress-demo')->middleware('web')->group(function() { \Atanunu\XpressWallet\Routes\routes(); });
Built-in API Routes (Optional)
The package can automatically expose a comprehensive set of REST-style proxy endpoints (customers, wallets, transfers, cards, merchant, team, transactions). They are DISABLED by default to avoid accidentally exposing sensitive actions.
Enable in .env (after publishing config):
XPRESSWALLET_ROUTES_ENABLED=true XPRESSWALLET_ROUTES_PREFIX=xpresswallet XPRESSWALLET_ROUTES_MIDDLEWARE=api,auth:sanctum
Notes:
- Use strong auth / rate limiting. These proxy real money-moving operations.
- Adjust prefix & middleware in
config/xpresswallet.phpunder therouteskey. - Routes register only when
enabledand routes cache is not already built (standard Laravel behavior). - Best suited for internal tools or rapid prototyping; for production domains build thin wrappers enforcing business rules.
Examples (with default prefix):
GET /xpresswallet/customers– list customersPOST /xpresswallet/wallets– create walletPOST /xpresswallet/transfers/bank– bank transferPOST /xpresswallet/cards/setup– initiate card setup
See src/Routes/routes.php for the full route map and names (all names start with xpresswallet.).
Testing & Coverage
Basic test run:
composer test
To generate coverage locally (requires Xdebug or PCOV):
# Enable Xdebug in php.ini (add: zend_extension=xdebug) # Then run composer coverage
If you prefer PCOV (often faster):
pecl install pcov echo "extension=pcov" >> $(php --ini | grep ".ini" | head -1 | awk '{print $NF}') php -d pcov.enabled=1 -d pcov.directory=src vendor/bin/pest --coverage
CI runs a separate coverage job (PHP 8.3) – you can later enforce a minimum threshold by raising the --min value in the workflow once baseline is established.
CI
GitHub Actions workflows:
ci.yml: matrix quality (PHP 8.2/8.3) + dedicated coverage job (8.3 with Xdebug).release-draft.yml: auto-drafts release notes from merged PR labels on tag push (v*.*.*).
Planned enhancements you can enable:
- Upload coverage to Codecov (add a step with
codecov/codecov-action). - Add a minimum coverage gate by increasing
--min=0to your baseline (e.g. 70).
Maintenance / Pruning
Run periodically (e.g. daily):
php artisan xpress:prune
Use --dry-run to preview and --days=30 to override retention for that run.
Webhooks
Add the middleware to your webhook route:
Route::post('/xpress/webhook', XpressWebhookController::class) ->middleware(\Atanunu\XpressWallet\Http\Middleware\VerifyXpressWebhook::class);
Set XPRESSWALLET_WEBHOOK_SECRET and (optionally) XPRESSWALLET_WEBHOOK_SIGNATURE_HEADER.
Events
Dispatched:
Atanunu\XpressWallet\Events\LoginSucceededAtanunu\XpressWallet\Events\TokensRefreshedAtanunu\XpressWallet\Events\RateLimited(each 429 attempt with method/url/attempt/retryAfter)Atanunu\XpressWallet\Events\CircuitBreakerOpened(breaker transition)
Use listeners to instrument metrics, alerts or custom logging.
Example listener registration:
Event::listen(\Atanunu\XpressWallet\Events\RateLimited::class, function($e) { logger()->warning('Xpress rate limited', ['url' => $e->url, 'attempt' => $e->attempt, 'retry_after' => $e->retryAfterSeconds]); });
Pagination Helper
Use paginate() when endpoint accepts page & per_page:
$page1 = XpressWallet::client()->paginate('customers', [], 1, 50); while ($next = $page1['meta']['next_page']) { $page1 = XpressWallet::client()->paginate('customers', [], $next, 50); }
Rate Limiting & Circuit Breaker
On 429, the client retries with full-jitter until rate_limit_max_attempts reached then throws RateLimitException (contains retryAfterSeconds).
Consecutive failures trigger a breaker; once open, calls throw CircuitBreakerOpenException until cool-down passes.
GET Response Caching
Enable by setting XPRESSWALLET_CACHE_GET_TTL>0. Subsequent identical GET calls within TTL return cached payload (per URI+query). Use prudent TTLs for data freshness.
Idempotency
Unsafe methods automatically include an Idempotency-Key header (UUID) unless you override or disable via config. Set your own key by passing it in headers: XpressWallet::client()->post('endpoint', [...]); (add custom header via method overload / PR for header injection if needed).
Tracing (OpenTelemetry)
If OpenTelemetry SDK is installed, spans are created per request (attributes: http.method, http.url, http.status_code, errors flagged). Configure exporter in your host app; this package only emits spans when the global tracer provider is available.
Static Analysis
Run PHPStan (Larastan) locally: Run Psalm (complementary):
composer psalm
composer analyse
Raised to level 8 (strict). Keep code green by adding types / phpdoc when extending.
Dependency Updates
Consider enabling Dependabot (.github/dependabot.yml):
version: 2 updates: - package-ecosystem: "composer" directory: "/" schedule: interval: "weekly" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly"
Security
Recommendations:
- Add
roave/security-advisories(conflict package) inrequire-devfor vulnerable dependency prevention. - Run
composer auditin CI (Composer 2.4+). - CodeQL workflow is currently disabled pending general availability for PHP; rely on PHPStan + Infection.
Release Process
- Update
CHANGELOG.md(optional – or rely on auto draft). - Bump version in your tag:
git tag v0.1.0 && git push origin v0.1.0. - GitHub Action drafts release notes (edit if needed, then publish).
Support / Issues
Open issues or PRs with clear reproduction steps. Use labels (bug, feature, docs, test) to improve autogenerated changelog quality.
Roadmap / Future
Planned and potential enhancements (open to contribution):
Short Term
- Mutation score badge automation (parse latest Infection report and update Shields endpoint / static JSON).
- Psalm baseline generation & gradual tightening of errorLevel.
- Additional feature tests for edge-case error mappings (422 variants, timeout simulation).
- Webhook middleware queueable job example & configurable model table names.
Medium Term
- Pluggable cache store strategy (allow per-endpoint TTL overrides).
- Configurable idempotency key provider interface.
- Extended OpenTelemetry attributes (retry count, cache hit, circuit state).
- Optional Redis-backed circuit breaker (distributed state) fallback.
- Pagination auto-iterator helper (Generator yielding pages transparently).
Long Term / Ideas
- Laravel Horizon metrics integration example (events -> metrics).
- Publish a companion CLI to inspect stored tokens, breaker state, recent API logs.
- Automatic generation of typed response DTOs via schema inference (if API specs become available).
- Multi-tenant token segregation strategy helpers.
- Add a lightweight HTTP fakes/replay tool for offline testing of recorded interactions.
Have another idea? Open a discussion or PR—align proposals with principles: stability, observability, least surprise.
License
MIT