paypal/paypalhttp
Deprecated PayPalHttp SDK: a lightweight generic PHP HTTP client for PayPal-style REST APIs. Provides Environment base URL handling, request/response objects, injectors for pre-flight logic (logging, auth), and exception-based error handling.
Install the Package Add the package to your Laravel project via Composer:
composer require paypal/paypalhttp:1.0.1
Set Up Environment
Define your PayPal API environment (e.g., sandbox or live) in config/services.php:
'paypal' => [
'environment' => env('PAYPAL_ENVIRONMENT', 'sandbox'),
'base_url' => env('PAYPAL_BASE_URL', 'https://api.sandbox.paypal.com'),
'client_id' => env('PAYPAL_CLIENT_ID'),
'client_secret' => env('PAYPAL_CLIENT_SECRET'),
],
Register the Client
Bind the HttpClient to Laravel’s service container in AppServiceProvider.php:
use PayPalHttp\HttpClient;
use PayPalHttp\Environment;
public function register()
{
$this->app->singleton(HttpClient::class, function ($app) {
$env = new Environment(config('services.paypal.base_url'));
$client = new HttpClient($env);
// Add injectors (e.g., logging, auth headers)
$client->addInjector(new AuthInjector(
config('services.paypal.client_id'),
config('services.paypal.client_secret')
));
return $client;
});
}
First Use Case: Fetch Transactions Create a service to interact with PayPal’s API:
use PayPalHttp\HttpRequest;
use PayPalHttp\HttpClient;
class PayPalService {
public function __construct(private HttpClient $client) {}
public function getTransactions()
{
$request = new HttpRequest('/v1/reporting/transactions', 'GET');
$request->header('Authorization', 'Bearer ' . $this->getAccessToken());
return $this->client->execute($request);
}
private function getAccessToken()
{
// Implement OAuth2 token retrieval logic
}
}
Call the Service Use the service in a controller or command:
use App\Services\PayPalService;
public function index(PayPalService $paypal)
{
$response = $paypal->getTransactions();
return response()->json($response->result);
}
Request Construction Build requests dynamically based on API endpoints:
$request = new HttpRequest('/v1/payments/payment', 'POST');
$request->header('Content-Type', 'application/json');
$request->body = json_encode(['intent' => 'sale', 'payer' => [...]]);
Injectors for Cross-Cutting Concerns Use injectors to handle repetitive tasks like authentication, logging, or retries:
class AuthInjector implements Injector {
public function __construct(private string $clientId, private string $clientSecret) {}
public function inject(HttpRequest $request)
{
if ($request->path === '/v1/oauth2/token') return;
$request->header('Authorization', 'Bearer ' . $this->getAccessToken());
}
private function getAccessToken(): string
{
// Fetch and return OAuth token
}
}
Response Handling Parse responses generically or use Laravel’s helpers:
$response = $client->execute($request);
if ($response->statusCode === 200) {
$data = json_decode($response->result, true);
return $data['transactions'];
}
throw new \RuntimeException('PayPal API error: ' . $response->result);
Error Handling
Wrap IOException in Laravel exceptions for consistency:
try {
$response = $client->execute($request);
} catch (IOException $e) {
throw new \Illuminate\Http\Client\ConnectionException(
'PayPal API error: ' . $e->getMessage(),
previous: $e
);
}
OAuth2 Flow Integration Use injectors to manage token refresh:
class TokenInjector implements Injector {
public function inject(HttpRequest $request)
{
if ($this->needsTokenRefresh()) {
$this->refreshToken();
}
$request->header('Authorization', 'Bearer ' . $this->getToken());
}
}
Idempotency Keys Add idempotency headers for safe retries:
$request->header('Idempotency-Key', Str::uuid());
Batch Processing Handle paginated responses:
$request = new HttpRequest('/v1/reporting/transactions', 'GET');
$request->query['count'] = 100;
$request->query['start_index'] = 0;
do {
$response = $client->execute($request);
$transactions = json_decode($response->result, true);
// Process transactions
$request->query['start_index'] += 100;
} while ($response->statusCode === 200);
Laravel Facades Create a facade for cleaner syntax:
// PayPalFacade.php
class PayPalFacade extends \Illuminate\Support\Facades\Facade {
protected static function getFacadeAccessor() { return 'paypal'; }
}
Register the facade in AppServiceProvider:
$this->app->bind('paypal', function ($app) {
return new PayPalService($app->make(HttpClient::class));
});
Usage:
$transactions = PayPal::getTransactions();
Testing
Mock the HttpClient in tests:
$mockClient = Mockery::mock(HttpClient::class);
$mockClient->shouldReceive('execute')
->once()
->andReturn(new HttpResponse(200, [], '{"transactions": [...]}'));
$this->app->instance(HttpClient::class, $mockClient);
Logging Use injectors to log requests/responses:
class LoggingInjector implements Injector {
public function inject(HttpRequest $request)
{
\Log::debug('PayPal Request', [
'path' => $request->path,
'method' => $request->method,
'headers' => $request->headers,
'body' => $request->body,
]);
}
}
Configuration
Centralize PayPal config in config/paypal.php:
return [
'environments' => [
'sandbox' => 'https://api.sandbox.paypal.com',
'live' => 'https://api.paypal.com',
],
'default' => env('PAYPAL_ENVIRONMENT', 'sandbox'),
'auth' => [
'client_id' => env('PAYPAL_CLIENT_ID'),
'client_secret' => env('PAYPAL_CLIENT_SECRET'),
],
];
Deprecation Risk
Case-Sensitive Headers
Content-Type are lowercase.$request->header('content-type', 'application/json'); // Works
$request->header('Content-Type', 'application/json'); // May fail
Error Handling Quirks
IOException does not extend Laravel’s Exception hierarchy. Wrap it for consistency:
catch (IOException $e) {
throw new \RuntimeException(
'PayPal API error [' . $e->response->statusCode . ']: ' . $e->response->result,
0,
$e
);
}
No Built-in JSON Handling
$data = json_decode($response->result, true);
CURL Dependency
ext-curl enabled. Add to php.ini or Dockerfile if missing:
extension=curl
No Retry Logic
HttpClient, this package lacks built-in retry mechanisms. Implement manually:
$attempts = 0;
while ($attempts < 3) {
try {
$response = $client->execute($request);
break;
} catch (IOException $e) {
if ($att
How can I help you explore Laravel packages today?