PHP code example of evolvex / laravel-remote-operations

1. Go to this page and download the library: Download evolvex/laravel-remote-operations library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

evolvex / laravel-remote-operations example snippets




namespace App\RemoteOperations;

use Evolvex\RemoteOperations\Contracts\ProbesRemoteOperations;
use Evolvex\RemoteOperations\Contracts\RemoteOperationHandler;
use Evolvex\RemoteOperations\Domain\Enums\RetrySafety;
use Evolvex\RemoteOperations\Domain\Enums\TransportPhase;
use Evolvex\RemoteOperations\Domain\ValueObjects\ProbeConsistency;
use Evolvex\RemoteOperations\Domain\ValueObjects\ProbeOutcome;
use Evolvex\RemoteOperations\Domain\ValueObjects\ProviderCapabilities;
use Evolvex\RemoteOperations\Domain\ValueObjects\RemoteOperationContext;
use Evolvex\RemoteOperations\Domain\ValueObjects\SendOutcome;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Support\Facades\Http;

final class ProviderXWithdrawal implements RemoteOperationHandler, ProbesRemoteOperations
{
    public function provider(RemoteOperationContext $context): string
    {
        return 'provider-x';
    }

    public function capabilities(RemoteOperationContext $context): ProviderCapabilities
    {
        return ProviderCapabilities::make(
            supportsIdempotency: true,
            supportsStatusProbe: true,
            retrySafety: RetrySafety::IDEMPOTENT,
            idempotencyTtlSeconds: 86_400,
            probeConsistency: ProbeConsistency::eventual(
                visibilityDelaySeconds: 5,
                definitiveNotFoundAfterSeconds: 120,
            ),
            maxSendDurationSeconds: 30,
        );
    }

    public function send(RemoteOperationContext $context): SendOutcome
    {
        try {
            $response = Http::withHeaders([
                'Idempotency-Key' => $context->idempotencyKey(),
            ])->timeout(30)->post('https://provider.example/withdrawals', $context->command);
        } catch (ConnectionException $e) {
            return SendOutcome::unknown(
                reason: 'connection_exception',
                phase: TransportPhase::UNKNOWN,
                metadata: ['endpoint' => 'https://provider.example/withdrawals', 'http_method' => 'POST'],
            );
        }

        if ($response->successful() && $response->status() !== 202) {
            return SendOutcome::success(
                externalId: $response->json('id'),
                metadata: [
                    'provider_status' => $response->json('status'),
                    'provider_request_id' => $response->header('X-Request-Id'),
                    'endpoint' => 'https://provider.example/withdrawals',
                    'http_method' => 'POST',
                ],
                httpStatus: $response->status(),
            );
        }

        if ($response->status() === 202) {
            return SendOutcome::remotePending(
                externalId: $response->json('id'),
                metadata: ['provider_status' => 'processing'],
            );
        }

        if ($response->status() === 422) {
            return SendOutcome::definiteFailure('provider_validation_rejected', httpStatus: 422);
        }

        return SendOutcome::unknown(
            reason: 'provider_5xx_with_unknown_commit_state',
            phase: TransportPhase::RESPONSE_RECEIVED,
            httpStatus: $response->status(),
        );
    }

    public function probe(RemoteOperationContext $context): ProbeOutcome
    {
        $response = Http::get('https://provider.example/withdrawals/by-reference/'.$context->businessKey());

        if ($response->status() === 404) {
            return ProbeOutcome::notFound();
        }

        return match ($response->json('status')) {
            'completed' => ProbeOutcome::succeeded($response->json('id')),
            'failed' => ProbeOutcome::failed('provider_failed'),
            default => ProbeOutcome::pending($response->json('id')),
        };
    }
}

use Evolvex\RemoteOperations\Facades\RemoteOperation;

$result = RemoteOperation::for('withdrawal', $withdrawal->uuid)
    ->handler(\App\RemoteOperations\ProviderXWithdrawal::class)
    ->subject($withdrawal)
    ->idempotencyKey($withdrawal->uuid)
    ->externalReference('withdrawal:'.$withdrawal->uuid)
    ->command([
        'withdrawal_id' => $withdrawal->id,
        'reference' => $withdrawal->uuid,
        'amount' => (string) $withdrawal->amount,
        'currency' => $withdrawal->currency,
    ])
    ->execute();

$operation = RemoteOperation::for('refund', $refund->uuid)
    ->handler(ProviderXRefund::class)
    ->idempotencyKey($refund->uuid)
    ->command([...])
    ->dispatch();

DB::transaction(function () use ($withdrawal) {
    RemoteOperation::for('withdrawal', $withdrawal->uuid)
        ->handler(ProviderXWithdrawal::class)
        ->idempotencyKey($withdrawal->uuid)
        ->command([...])
        ->dispatchAfterCommit();
});

use Illuminate\Support\Facades\Schedule;

Schedule::command('remote-operations:dispatch-outbox')->everyMinute()->onOneServer();
Schedule::command('remote-operations:recover-stale')->everyMinute()->onOneServer();
Schedule::command('remote-operations:reconcile-due')->everyMinute()->onOneServer();
Schedule::command('remote-operations:prune')->daily()->onOneServer();
bash
php artisan vendor:publish --tag=remote-operations-config
php artisan vendor:publish --tag=remote-operations-migrations
bash
php artisan remote-operations:list --state=unknown
php artisan remote-operations:show 01K...
php artisan remote-operations:reconcile 01K...
php artisan remote-operations:reconcile-due
php artisan remote-operations:recover-stale
php artisan remote-operations:dispatch-outbox
php artisan remote-operations:stats
php artisan remote-operations:doctor
php artisan remote-operations:providers
php artisan remote-operations:prune
bash
php artisan remote-operations:retry 01K... \
  --acknowledge-duplicate-risk \
  --reason="provider confirmed operation never existed" \
  --actor=admin:42