›
byrcsc/laravel-payrex · 1.x
Test PayRex requests and webhooks without making network calls.
Laravel PayRex uses Laravel's HTTP client throughout, so Http::fake() prevents
network calls and lets a test assert the exact request.
use ByRcsc\LaravelPayrex\Facades\Payrex;
use Illuminate\Support\Facades\Http;
it('creates a payment intent', function () {
Http::fake([
'*' => Http::response([
'resource' => 'payment_intent',
'id' => 'pi_test_1042',
'amount' => 10_000,
'currency' => 'PHP',
'status' => 'awaiting_payment_method',
'client_secret' => 'pi_test_1042_secret_test',
]),
]);
$intent = Payrex::paymentIntents()->create(
amount: 10_000,
paymentMethods: ['card'],
metadata: ['order_id' => '1042'],
);
expect($intent->id)->toBe('pi_test_1042');
Http::assertSent(function ($request) {
parse_str($request->body(), $body);
return $request->method() === 'POST'
&& $request->url() === 'https://api.payrexhq.com/payment_intents'
&& $body['amount'] === '10000'
&& $body['payment_methods'] === ['card']
&& $body['metadata'] === ['order_id' => '1042'];
});
});Parameters are form-encoded, never JSON. POST and PUT send them as an
application/x-www-form-urlencoded body; GET and DELETE send them in the
query string, so assert those with $request->url() rather than
$request->body(). Lists are encoded with empty brackets
(payment_methods[]=card), nested associative keys are preserved
(metadata[order_id]=1042), booleans become true/false, enums are unwrapped
to their values, and nulls are dropped entirely.
Use Laravel's protection against un-faked requests in your test bootstrap:
use Illuminate\Support\Facades\Http;
Http::preventStrayRequests();Then fake every PayRex call made by the behavior under test.
Return a PayRex-shaped error document and assert the typed exception:
use ByRcsc\LaravelPayrex\Exceptions\InvalidRequestException;
Http::fake([
'*' => Http::response([
'errors' => [[
'code' => 'parameter_invalid',
'detail' => 'Amount is invalid.',
'parameter' => 'amount',
]],
], 400),
]);
expect(fn () => Payrex::post('/payment_intents', ['amount' => 1]))
->toThrow(InvalidRequestException::class);Use Http::failedConnection() to exercise an ApiConnectionException.
The package command dispatches a synthetic generic and typed event:
php artisan payrex:webhook-test "payment_intent.succeeded"It performs no network request and no signature check. Use it during local development to verify that event discovery, listener registration, and queues are wired correctly.
In automated tests, dispatch the typed event with a WebhookEvent:
use ByRcsc\LaravelPayrex\Data\WebhookEvent;
use ByRcsc\LaravelPayrex\Events\PaymentIntentSucceeded;
use Illuminate\Support\Facades\Event;
Event::fake();
$webhook = WebhookEvent::from([
'id' => 'evt_test_1042',
'type' => 'payment_intent.succeeded',
'data' => [
'resource' => [
'resource' => 'payment_intent',
'id' => 'pi_test_1042',
'status' => 'succeeded',
],
],
]);
event(new PaymentIntentSucceeded($webhook));
Event::assertDispatched(PaymentIntentSucceeded::class);Test your listener class directly when you want to assert domain changes.
WebhookSignature::header() builds the same header PayRex sends, so a test can
exercise the real route, middleware, and signature check:
use ByRcsc\LaravelPayrex\Events\PaymentIntentSucceeded;
use ByRcsc\LaravelPayrex\Support\WebhookSignature;
use Illuminate\Support\Facades\Event;
it('accepts a signed delivery', function () {
config()->set('payrex.webhook_secret', 'whsk_test_signing_secret');
Event::fake();
$payload = json_encode([
'id' => 'evt_test_1042',
'resource' => 'event',
'type' => 'payment_intent.succeeded',
'data' => [
'resource' => [
'resource' => 'payment_intent',
'id' => 'pi_test_1042',
'status' => 'succeeded',
],
],
], JSON_THROW_ON_ERROR);
$this->call(
method: 'POST',
uri: '/payrex/webhook',
server: [
'CONTENT_TYPE' => 'application/json',
'HTTP_PAYREX_SIGNATURE' => WebhookSignature::header(
$payload,
'whsk_test_signing_secret',
),
],
content: $payload,
)->assertOk()->assertJson([
'received' => true,
'id' => 'evt_test_1042',
]);
Event::assertDispatched(PaymentIntentSucceeded::class);
});Pass timestamp: to produce a stale delivery and assert the 400, or
livemode: true for a live-mode signature. This is the only way to cover
signature verification; payrex:webhook-test deliberately skips it.
The HasPayrexCustomer concern needs a model table with its configured ID
column. Use an in-memory database or a test migration, fake the customer
response, then assert both the remote request and persisted ID:
Http::fake([
'*' => Http::response([
'resource' => 'customer',
'id' => 'cus_test_1042',
'name' => 'Ada Lovelace',
'email' => 'ada@example.com',
'currency' => 'PHP',
]),
]);
$customer = $user->createAsPayrexCustomer();
expect($customer->id)->toBe('cus_test_1042')
->and($user->fresh()->payrex_customer_id)->toBe('cus_test_1042');Override the base URL in a test environment when an integration test needs a local HTTP server:
PAYREX_BASE_URL=http://127.0.0.1:8787Do not point automated tests at a real PayRex account unless the test is explicitly designed, isolated, and approved for that environment.
The package repository's own suite needs no external database, .env, or
PayRex account:
composer install
composer test
composer analyse
vendor/bin/pint --test