baseUrl(rtrim(config('services.moneybird.base_url'), '/').'/'.$administrationId) ->acceptJson() ->timeout(15); } public function findContactByCustomerId(string $customerId): ?array { $response = $this->http()->get("contacts/customer_id/{$customerId}.json"); if ($response->status() === 404) { return null; } return $this->decode($response, "GET contacts/customer_id/{$customerId}"); } /** * @param array $attributes */ public function createContact(array $attributes): array { $response = $this->http()->post('contacts.json', ['contact' => $attributes]); return $this->decode($response, 'POST contacts'); } /** * @param array $attributes */ public function createSalesInvoice(array $attributes): array { $response = $this->http()->post('sales_invoices.json', ['sales_invoice' => $attributes]); return $this->decode($response, 'POST sales_invoices'); } public function findSalesInvoice(string $id): array { $response = $this->http()->get("sales_invoices/{$id}.json"); return $this->decode($response, "GET sales_invoices/{$id}"); } /** * @param array $attributes */ public function createRecurringSalesInvoice(array $attributes): array { $response = $this->http()->post('recurring_sales_invoices.json', ['recurring_sales_invoice' => $attributes]); return $this->decode($response, 'POST recurring_sales_invoices'); } /** * @param array $attributes */ public function updateRecurringSalesInvoice(string $id, array $attributes): array { $response = $this->http()->patch("recurring_sales_invoices/{$id}.json", ['recurring_sales_invoice' => $attributes]); return $this->decode($response, "PATCH recurring_sales_invoices/{$id}"); } public function deleteRecurringSalesInvoice(string $id): void { $response = $this->http()->delete("recurring_sales_invoices/{$id}.json"); // Already gone is fine — deleting is meant to be idempotent here. if ($response->status() === 404) { return; } $this->decode($response, "DELETE recurring_sales_invoices/{$id}", allowEmpty: true); } /** * @return array> */ public function listAdministrations(): array { $response = Http::withToken(config('services.moneybird.api_token')) ->acceptJson() ->timeout(15) ->get(rtrim(config('services.moneybird.base_url'), '/').'/administrations.json'); return $this->decode($response, 'GET administrations'); } /** * @return array> */ public function listTaxRates(): array { $response = $this->http()->get('tax_rates.json'); return $this->decode($response, 'GET tax_rates'); } /** * @return array> */ public function listLedgerAccounts(): array { $response = $this->http()->get('ledger_accounts.json'); return $this->decode($response, 'GET ledger_accounts'); } /** * @param array $attributes */ public function createWebhook(array $attributes): array { $response = $this->http()->post('webhooks.json', $attributes); return $this->decode($response, 'POST webhooks'); } /** * @return array */ private function decode(Response $response, string $context, bool $allowEmpty = false): array { if ($response->failed()) { Log::error("Moneybird API request failed: {$context}", [ 'status' => $response->status(), 'body' => $response->body(), ]); throw new MoneybirdException("Moneybird API request failed ({$context}): HTTP {$response->status()}"); } if ($allowEmpty && blank($response->body())) { return []; } return $response->json() ?? []; } }