Eight new features landed since 3.1.0, all fully backward-compatible. No removed methods, no signature changes, nothing to migrate.
429 handling with Retry-After (#674)
The retry middleware now handles 429 Too Many Requests. When PayPal sends a Retry-After header the delay respects it exactly; without it, the existing exponential backoff kicks in (500ms, 1s, 2s, up to 8s). Nothing to configure.
WebhookEvent value object (#676)
Typed readonly wrapper around PayPal webhook payloads:
use Srmklive\PayPal\Events\WebhookEvent;
$event = WebhookEvent::fromRawBody($request->getContent());
if ($event->is('PAYMENT.CAPTURE.COMPLETED')) {
$captureId = $event->resource['id'];
}Fields: id, eventType, resourceType, summary, createTime, resource, rawPayload.
Transaction reporting helpers (#678)
Four wrappers on top of listTransactions() for the common cases:
// Single transaction by ID, looks back up to 31 days
$tx = $provider->getTransactionDetails('5TY05013RG002845M');
// Date range shorthand, no manual ISO 8601 formatting needed
$txns = $provider->listTransactionsForDateRange('2025-01-01', '2025-01-31');
// Filter by PayPal transaction type code
$txns = $provider->listTransactionsByType('T0006', '2025-01-01', '2025-01-31');
// Filter by status (S = success, P = pending, etc.)
$txns = $provider->listTransactionsByStatus('S', '2025-01-01', '2025-01-31');Subscription lifecycle helpers (#675)
// Alias for activateSubscription() with a sensible default reason
$provider->reactivateSubscription($subscriptionId);
// True only when status === 'ACTIVE'
if ($provider->isSubscriptionActive($subscriptionId)) { ... }MockPayPalClient (#672)
A public MockPayPalClient lives at Srmklive\PayPal\Testing\MockPayPalClient. Queue responses in tests without touching the sandbox:
use Srmklive\PayPal\Testing\MockPayPalClient;
$client = new MockPayPalClient($credentials);
$client->addResponse(201, ['id' => 'ORDER-123', 'status' => 'CREATED']);
$order = $client->createOrder([...]);
// $client->requests() to inspect what was sentBillingPlanBuilder (#673)
Fluent builder for billing plan payloads. Covers all cycle types, trial pricing, and payment preferences:
use Srmklive\PayPal\Services\BillingPlanBuilder;
$plan = BillingPlanBuilder::make('Pro Plan', 'PROD-ABC123')
->trialCycle(days: 14, price: 0)
->monthlyPrice(29.99)
->build();
$response = $provider->createBillingPlan($plan);setPartnerAttributionId() (#677)
Set the PayPal-Partner-Attribution-Id BN code once after initialisation and it sticks for all subsequent requests from that instance:
$provider->setPartnerAttributionId('YourBNCode_Cart');Pay Upon Invoice / BNPL for DE/AT (#679)
setPaymentSourcePayUponInvoice() joins the existing payment source setters. Available for merchants in Germany and Austria:
$provider->setPaymentSourcePayUponInvoice([
'name' => ['given_name' => 'John', 'surname' => 'Doe'],
'email' => 'john.doe@example.com',
'birth_date' => '1990-01-01',
'phone' => ['country_code' => '49', 'national_number' => '1234567890'],
'billing_address' => [
'address_line_1' => 'Hauptstraße 1',
'admin_area_2' => 'Berlin',
'postal_code' => '10115',
'country_code' => 'DE',
],
'experience_context' => [
'locale' => 'de-DE',
'return_url' => 'https://example.com/paypal-success',
'cancel_url' => 'https://example.com/paypal-cancel',
],
]);
$order = $provider->createOrderWithPaymentSource([
'intent' => 'CAPTURE',
'purchase_units' => [['amount' => ['currency_code' => 'EUR', 'value' => '99.00']]],
]);Upgrading
composer update srmklive/paypalNo code changes required.