Certified/tests/Feature/Billing/MoneybirdWebhookTest.php
2026-08-09 16:33:53 +02:00

91 lines
3 KiB
PHP

<?php
use App\Enums\SubscriptionPlan;
use App\Enums\SubscriptionStatus;
use App\Models\Subscription;
use App\Models\SubscriptionInvoice;
use App\Models\User;
use App\Notifications\SubscriptionActivatedNotification;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Notification;
beforeEach(function () {
config([
'services.moneybird.api_token' => 'test-token',
'services.moneybird.administration_id' => '123456',
'services.moneybird.webhook_token' => 'the-webhook-token',
]);
});
test('a request with the wrong webhook token is rejected', function () {
Http::fake();
$user = User::factory()->create();
Subscription::factory()->for($user)->starter()->create(['moneybird_contact_id' => 'contact-1']);
$this->post(route('webhooks.moneybird'), [
'webhook_token' => 'not-the-right-token',
'entity_type' => 'SalesInvoice',
'entity_id' => 'invoice-1',
])->assertForbidden();
Http::assertNothingSent();
});
test('a paid sales invoice activates the matching subscription', function () {
Notification::fake();
Http::fake([
'*/sales_invoices/invoice-1.json' => Http::response([
'id' => 'invoice-1',
'contact_id' => 'contact-1',
'state' => 'paid',
'invoice_date' => today()->toDateString(),
'due_date' => today()->addDays(14)->toDateString(),
'paid_at' => now()->toIso8601String(),
'total_price_excl_tax' => number_format(SubscriptionPlan::Starter->priceInCents() / 100, 2, '.', ''),
]),
]);
$user = User::factory()->create();
$subscription = Subscription::factory()->for($user)->starter()->create([
'moneybird_contact_id' => 'contact-1',
'status' => SubscriptionStatus::AwaitingPayment,
'current_period_starts_at' => today(),
'current_period_ends_at' => today()->addYear(),
]);
$this->post(route('webhooks.moneybird'), [
'webhook_token' => 'the-webhook-token',
'entity_type' => 'SalesInvoice',
'entity_id' => 'invoice-1',
])->assertNoContent();
expect($subscription->refresh()->status)->toBe(SubscriptionStatus::Active)
->and($subscription->grace_ends_at)->toBeNull();
$invoice = SubscriptionInvoice::sole();
expect($invoice->moneybird_invoice_id)->toBe('invoice-1')
->and($invoice->state)->toBe('paid')
->and($invoice->paid_at)->not->toBeNull();
Notification::assertSentTo($user, SubscriptionActivatedNotification::class);
});
test('an invoice for an unknown contact is ignored without error', function () {
Http::fake([
'*/sales_invoices/invoice-1.json' => Http::response([
'id' => 'invoice-1',
'contact_id' => 'someone-elses-contact',
'state' => 'paid',
]),
]);
$this->post(route('webhooks.moneybird'), [
'webhook_token' => 'the-webhook-token',
'entity_type' => 'SalesInvoice',
'entity_id' => 'invoice-1',
])->assertNoContent();
expect(SubscriptionInvoice::count())->toBe(0);
});