recurly/recurly-client
Official PHP client for Recurly’s V3 API. Install via Composer, create a Client with your API key (US or EU region), and access all API operations in one place. Supports optional PSR-3 logging.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require recurly/recurly-client
Add to composer.json under require:
"recurly/recurly-client": "^4"
Initialize Client:
Store your API key securely (e.g., Laravel .env):
RECURLY_API_KEY=your_api_key_here
Initialize in a service provider or controller:
use Recurly\Client;
$client = new Client(config('recurly.api_key'));
First Use Case: Fetch a subscription by ID:
$subscription = $client->getSubscription('sub_123');
return $subscription->getPlan()->getName();
Create Subscription:
$subscription = $client->createSubscription([
'account' => ['first_name' => 'John', 'email' => 'john@example.com'],
'plan_code' => 'premium-monthly',
'currency' => 'USD',
'billing_info' => ['first_name' => 'John', 'last_name' => 'Doe', 'email' => 'john@example.com']
]);
Update Subscription:
$client->updateSubscription('sub_123', ['plan_code' => 'premium-annual']);
Cancel Subscription:
$client->cancelSubscription('sub_123', ['reason' => 'customer_request']);
List Accounts with Pagination:
$accounts = $client->listAccounts(['limit' => 100, 'email' => 'user@example.com']);
foreach ($accounts as $account) {
// Process each account
}
Count Past Due Invoices:
$pastDueAccounts = $client->listAccounts(['past_due' => true]);
$count = $pastDueAccounts->getCount();
Generate Invoice:
$invoice = $client->createInvoice('sub_123');
Collect Payment:
$client->collectPayment('inv_456');
Use Laravel's queue:listen to process Recurly webhooks:
// routes/web.php
Route::post('/recurly/webhook', [RecurlyWebhookHandler::class, 'handle']);
// app/Services/RecurlyWebhookHandler.php
use Recurly\Client;
use Illuminate\Support\Facades\Log;
class RecurlyWebhookHandler {
public function handle(Request $request) {
$client = new Client(config('recurly.api_key'));
$event = $client->verifyWebhook($request->getContent());
// Process event (e.g., subscription canceled)
if ($event->getType() === 'subscription_canceled') {
Log::info("Subscription canceled: {$event->getSubscription()->getId()}");
}
}
}
Laravel Service Provider: Bind the client to the container for dependency injection:
// app/Providers/AppServiceProvider.php
public function register() {
$this->app->singleton(Client::class, function ($app) {
return new Client(config('recurly.api_key'));
});
}
API Rate Limiting:
Use the getRateLimitRemaining() method to handle rate limits gracefully:
$response = $client->getSubscription('sub_123')->getResponse();
if ($response->getRateLimitRemaining() < 5) {
sleep(1); // Throttle requests
}
Error Handling Middleware: Create a middleware to catch Recurly errors globally:
// app/Http/Middleware/HandleRecurlyErrors.php
public function handle($request, Closure $next) {
try {
return $next($request);
} catch (\Recurly\RecurlyError $e) {
Log::error("Recurly Error: " . $e->getMessage());
return response()->json(['error' => 'Recurly API error'], 500);
}
}
Testing:
Use Laravel's Mockery to mock the client in tests:
$mockClient = Mockery::mock(Client::class);
$mockClient->shouldReceive('getSubscription')
->with('sub_123')
->andReturn(new \Recurly\Subscription(['state' => 'active']));
API Key Exposure:
.env and config('recurly.api_key') for secure access.Pagination Lazy Loading:
list* methods return a Pager that does not make API calls until iterated.getCount() or getFirst() in loops; it triggers API calls per invocation.Time Zone Handling:
$createdAt = $subscription->getCreatedAt()->format('Y-m-d H:i:s');
Webhook Verification:
verifyWebhook() to prevent spoofing:
$event = $client->verifyWebhook($request->getContent(), $request->header('X-Recurly-Signature'));
Idempotency:
idempotency_key for create* operations to avoid duplicate charges:
$client->createSubscription($data, ['idempotency_key' => 'unique_key_123']);
Enable Logging:
DEBUG in development (but never in production):
$logger = new \Recurly\Logger('Recurly', \Psr\Log\LogLevel::DEBUG);
$client = new Client(config('recurly.api_key'), $logger);
Inspect HTTP Metadata:
getResponse() to debug API calls:
$response = $client->getSubscription('sub_123')->getResponse();
Log::debug("Request ID: " . $response->getRequestId());
Log::debug("Response Body: " . $response->getBodyAsJson());
Common Errors:
Validation: Check err.params for invalid fields.NotFound: Verify IDs (e.g., subscription/account IDs).RateLimit: Implement exponential backoff for retries.Custom Resources:
\Recurly\Resource to add domain-specific methods:
class CustomSubscription extends \Recurly\Subscription {
public function isPremium() {
return $this->getPlan()->getCode() === 'premium';
}
}
Middleware for API Calls:
class RecurlyClientDecorator {
protected $client;
public function __construct(Client $client) {
$this->client = $client;
}
public function getSubscription($id) {
$subscription = $this->client->getSubscription($id);
// Add custom logic (e.g., cache results)
return $subscription;
}
}
Event Dispatching:
event(new SubscriptionCanceled($event->getSubscription()));
Testing Utilities:
trait RecurlyTestTrait {
protected function mockRecurlyClient() {
$mock = Mockery::mock(Client::class);
// Setup mock responses...
return $mock;
}
}
EU Region Support:
eu or us) when initializing the client:
$client = new Client(config('recurly.api_key'), ['region' => 'eu']);
---
How can I help you explore Laravel packages today?