Search by

rabosh / laravel

yjivkov

Laravel SDK for the Rabosh platform — billing events, usage records, and consent management

1.0.0 2026-04-22 19:38 UTC

This package is auto-updated.

Last update: 2026-09-14 06:50:14 UTC


README

Laravel package for pushing billing events and usage records to the Rabosh billing platform.

Installation

composer require rabosh/laravel

Configuration

Publish the config file:

php artisan vendor:publish --tag=rabosh-config

Add to your .env:

RABOSH_API_KEY=your-api-key
RABOSH_API_SECRET=your-api-secret
RABOSH_SITE_IDENTIFIER=your-site-identifier
RABOSH_WEBHOOK_SECRET=your-webhook-secret

RABOSH_WEBHOOK_SECRET is the shared secret Rabosh uses to sign the webhooks it sends to your site. It is generated in the Rabosh dashboard when you configure a webhook endpoint for your app.

Optionally override the API URL (defaults to https://api.rabosh.com):

RABOSH_BASE_URL=https://custom-instance.example.com

Usage

Push a billing event

use Rabosh\Facades\Rabosh;

// Simple event
Rabosh::event('api.request', [
    'endpoint' => '/users',
    'method' => 'GET',
]);

// With timestamp
Rabosh::event('sms.sent', [
    'recipient' => '+1234567890',
    'segments' => 2,
], now()->toIso8601String());

Async (queued) events

Enable in .env:

RABOSH_QUEUE_ENABLED=true
RABOSH_QUEUE_CONNECTION=redis
RABOSH_QUEUE_NAME=billing
Rabosh::eventAsync('api.request', ['endpoint' => '/users']);

Push usage records

use Rabosh\Facades\Rabosh;

// Single usage record
Rabosh::usageRecord('api-calls', 1.0);

// With timestamp and metadata
Rabosh::usageRecord('storage-gb', 2.5, now()->toIso8601String(), [
    'bucket' => 'uploads',
]);

// Async (queued)
Rabosh::usageRecordAsync('api-calls', 1.0);

// Batch — send multiple records in one request
Rabosh::usageRecordBatch([
    ['metered_feature_id' => 'api-calls', 'amount' => 5],
    ['metered_feature_id' => 'storage-gb', 'amount' => 1.2, 'metadata' => ['bucket' => 'media']],
]);

Dependency injection

use Rabosh\RaboshClient;

class MyService
{
    public function __construct(private RaboshClient $rabosh) {}

    public function doWork(): void
    {
        $this->rabosh->event('work.completed', ['duration_ms' => 150]);
        $this->rabosh->usageRecord('jobs-processed', 1.0);
    }
}

Buffer & Retry (Offline Resilience)

When the Rabosh API is unreachable, the SDK can buffer failed requests locally so no billing data is lost. Buffered requests are stored as JSON files and retried later.

Enable the buffer

In .env:

RABOSH_BUFFER_ENABLED=true
RABOSH_BUFFER_PATH=   # optional, defaults to storage/rabosh/buffer

Retry buffered requests

Run manually:

php artisan rabosh:retry-buffered

Or schedule it in your app/Console/Kernel.php:

$schedule->command('rabosh:retry-buffered')->everyFiveMinutes();

Check buffer status

$pending = Rabosh::bufferCount();

Document Consent

Manage versioned legal documents (Terms & Conditions, Privacy Policy, etc.) and track user consent per version.

Check if a user needs to consent

use Rabosh\Facades\Rabosh;

// Check all documents published on this site
$result = Rabosh::checkConsent('user-123');

// Check specific documents
$result = Rabosh::checkConsent('user-123', ['terms-and-conditions', 'privacy-policy']);

// With locale preference
$result = Rabosh::checkConsent('user-123', null, 'bg');

// Response structure:
// ['data' => [
//     ['slug' => 'terms-and-conditions', 'status' => 'pending', 'enforcement' => 'blocking', ...],
//     ['slug' => 'privacy-policy', 'status' => 'consented', ...],
// ]]

Fetch a published document

// Get the document content in the user's locale
$doc = Rabosh::getDocument('terms-and-conditions', 'bg');

// $doc['title'], $doc['body'], $doc['version_label'], $doc['served_locale']

Record consent

Rabosh::recordConsent(
    externalUserId: 'user-123',
    documentSlug: 'terms-and-conditions',
    source: 'web',
    locale: 'en',
    metadata: [
        'consent_text_presented' => 'I have read and agree to the Terms and Conditions',
        'policy_url' => 'https://mysite.com/terms',
        'geo_country' => 'BG',
    ],
);

// Returns 201 on success, or empty array on failure (buffered for retry)
// Returns 409 if user already consented to this version — treat as success

Marketing Consent

Track channel-based marketing permissions (email, SMS, push, etc.) with grant/revoke lifecycle and TTL expiration.

Grant consent

use Rabosh\Facades\Rabosh;

Rabosh::grantMarketingConsent(
    externalUserId: 'user-123',
    consentTypeSlug: 'email_marketing',
    source: 'web',
    locale: 'en',
    options: [
        'ttl_days' => 365,
        'consent_text_presented' => 'I agree to receive promotional emails',
        'policy_url' => 'https://mysite.com/preferences',
    ],
);

// With idempotency key for safe retries
Rabosh::grantMarketingConsent(
    externalUserId: 'user-123',
    consentTypeSlug: 'email_marketing',
    source: 'web',
    locale: 'en',
    options: ['ttl_days' => 365],
    idempotencyKey: 'grant-user123-email-' . date('Ymd'),
);

Revoke consent

Rabosh::revokeMarketingConsent(
    externalUserId: 'user-123',
    consentTypeSlug: 'email_marketing',
    source: 'web',
    locale: 'en',
);

Check marketing consent status

// Check all consent types
$result = Rabosh::checkMarketingConsent('user-123');

// Check specific types
$result = Rabosh::checkMarketingConsent('user-123', ['email_marketing', 'sms_promotions']);

// Response structure:
// ['data' => [
//     ['slug' => 'email_marketing', 'status' => 'granted', 'expires_at' => '2027-07-22T...'],
//     ['slug' => 'sms_promotions', 'status' => 'expired', ...],
// ]]

Get consent history

// Document consent history (default)
$history = Rabosh::consentHistory('user-123');

// Explicitly document-only
$history = Rabosh::consentHistory('user-123', 'document');

// Marketing consent history
$history = Rabosh::consentHistory('user-123', 'marketing');

Practical Example: Consent Gate Middleware

namespace App\Http\Middleware;

use Closure;
use Rabosh\Facades\Rabosh;

class RequireConsent
{
    public function handle($request, Closure $next)
    {
        $user = $request->user();
        $result = Rabosh::checkConsent($user->external_id);

        $pending = collect($result['data'] ?? [])
            ->where('status', 'pending')
            ->where('enforcement', 'blocking');

        if ($pending->isNotEmpty()) {
            return redirect()->route('consent.show', [
                'documents' => $pending->pluck('slug')->toArray(),
            ]);
        }

        return $next($request);
    }
}

Inbound Webhooks (payment notifications)

Rabosh can notify your site when a payment for your customers is created and when it is paid (e.g. payment.created, payment.paid). The SDK ships a ready-made receiver so you only write the business logic.

The package automatically registers POST /webhooks/rabosh (configurable). Because the request is HMAC-signed, exempt the route from CSRF in your bootstrap/app.php:

->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: ['webhooks/rabosh']);
})

Register handlers

Register handlers in a service provider's boot():

use Rabosh\Facades\Rabosh;
use Rabosh\Webhooks\Webhook;

public function boot(): void
{
    $webhooks = app('rabosh.webhooks');

    $webhooks->on('payment.created', function (Webhook $webhook) {
        $orderId = $webhook->data['order_id'] ?? null;
        // Mark the order as awaiting payment
    });

    $webhooks->on('payment.paid', function (Webhook $webhook) {
        Log::info('Payment received', $webhook->data);
        // Fulfil the order
    });

    // Wildcards and catch-all work too
    $webhooks->on('payment.*', fn (Webhook $webhook) => ...);
}

Webhook exposes eventType, eventId, timestamp, appId, and data (the payment details incl. transaction_id, payment_intent_id, invoice_id, order_id, amount, currency, status, captured_at).

Manual / custom routes

If you prefer your own route, disable auto-registration in .env:

RABOSH_WEBHOOK_ROUTE_ENABLED=false

then map Rabosh\Webhooks\WebhookController to any path you like:

use Rabosh\Webhooks\WebhookController;

Route::post('/payments/callback', WebhookController::class)
    ->name('payments.callback');

Or reuse the Rabosh\Webhooks\VerifiesWebhookSignature trait in your own controller to validate the X-Signature-256 header, then call app('rabosh.webhooks')->handle($payload).

Response & retries

The receiver responds 200 (with the event_id) on success, 401 on a bad signature, and 400/422 on malformed payloads. Rabosh retries failed deliveries, so keep handlers idempotent (key on $webhook->eventId).

Error Handling

By default, the package will never throw exceptions into your application. All failures (network errors, auth errors, server errors) are caught internally and logged via Laravel's Log facade with a [Rabosh] prefix. Your application continues to run normally.

// This is safe — if the Rabosh API is down, the call logs the error,
// buffers the request (if enabled), and returns [].
Rabosh::event('api.request', ['endpoint' => '/users']);

Queued events (eventAsync, usageRecordAsync) also handle failures gracefully. The jobs retry 3 times and log the failure if all retries are exhausted — they will never surface an unhandled exception in your application.

Checking for failures

Since event() and usageRecord() return an empty array on failure, you can detect issues:

$result = Rabosh::event('api.request', ['endpoint' => '/users']);

if (empty($result)) {
    // The event was not delivered — check your logs for details.
    // If buffer is enabled, it's stored for retry.
}