Эх сурвалжийг харах

feat: add integracao ao fechar servico

Gustavo Mantovani 3 долоо хоног өмнө
parent
commit
560785f224
31 өөрчлөгдсөн 583 нэмэгдсэн , 92 устгасан
  1. 1 0
      app/Http/Controllers/ClientController.php
  2. 2 1
      app/Http/Controllers/CustomScheduleController.php
  3. 1 0
      app/Http/Controllers/ProviderController.php
  4. 2 1
      app/Http/Controllers/ProviderServicesTypeController.php
  5. 2 1
      app/Http/Controllers/ProviderSpecialityController.php
  6. 1 0
      app/Http/Controllers/ProviderWorkingDayController.php
  7. 2 1
      app/Http/Controllers/UserTypePermissionController.php
  8. 2 2
      app/Http/Requests/PaymentRequest.php
  9. 3 0
      app/Jobs/FinishScheduleJob.php
  10. 2 0
      app/Jobs/StartScheduleJob.php
  11. 27 1
      app/Services/AuthService.php
  12. 2 1
      app/Services/ClientCalendarService.php
  13. 3 0
      app/Services/ClientFavoriteProviderService.php
  14. 1 0
      app/Services/ClientProviderBlockService.php
  15. 28 3
      app/Services/CustomScheduleService.php
  16. 2 0
      app/Services/CustomScheduleSpecialityService.php
  17. 12 6
      app/Services/DashboardService.php
  18. 31 6
      app/Services/Pagarme/PagarmeCustomerService.php
  19. 108 19
      app/Services/Pagarme/PagarmePaymentService.php
  20. 35 10
      app/Services/Pagarme/PagarmeRecipientService.php
  21. 216 1
      app/Services/PaymentService.php
  22. 2 1
      app/Services/ProviderCalendarService.php
  23. 1 0
      app/Services/ProviderClientBlockService.php
  24. 23 12
      app/Services/ProviderService.php
  25. 1 0
      app/Services/ReviewImprovementService.php
  26. 11 3
      app/Services/ReviewService.php
  27. 11 0
      app/Services/ScheduleService.php
  28. 2 1
      app/Services/SearchService.php
  29. 6 1
      app/Services/UserService.php
  30. 43 0
      database/migrations/2026_05_20_000001_restrict_payment_methods_to_credit_card_and_pix.php
  31. 0 21
      pint.json

+ 1 - 0
app/Http/Controllers/ClientController.php

@@ -63,6 +63,7 @@ class ClientController extends Controller
     public function register(RegisterClientRequest $request): JsonResponse
     {
         $result = $this->service->register($request->validated());
+
         if (! $result) {
             return $this->errorResponse(message: __('auth.failed'), code: 401);
         }

+ 2 - 1
app/Http/Controllers/CustomScheduleController.php

@@ -33,7 +33,8 @@ class CustomScheduleController extends Controller
     public function store(CustomScheduleRequest $request): JsonResponse
     {
         try {
-            $validated       = $request->validated();
+            $validated = $request->validated();
+
             $customSchedules = $this->customScheduleService->create($validated);
 
             $count   = count($customSchedules);

+ 1 - 0
app/Http/Controllers/ProviderController.php

@@ -98,6 +98,7 @@ class ProviderController extends Controller
     public function register(RegisterProviderRequest $request): JsonResponse
     {
         $result = $this->service->register($request->validated());
+
         if (! $result) {
             return $this->errorResponse(message: __('auth.failed'), code: 401);
         }

+ 2 - 1
app/Http/Controllers/ProviderServicesTypeController.php

@@ -27,7 +27,8 @@ class ProviderServicesTypeController extends Controller
 
     public function store(ProviderServicesTypeRequest $request, int $providerId): JsonResponse
     {
-        $data                = $request->validated();
+        $data = $request->validated();
+
         $data['provider_id'] = $providerId;
 
         $providerServicesType = $this->providerServicesTypeService->create($data);

+ 2 - 1
app/Http/Controllers/ProviderSpecialityController.php

@@ -27,7 +27,8 @@ class ProviderSpecialityController extends Controller
 
     public function store(ProviderSpecialityRequest $request, int $providerId): JsonResponse
     {
-        $data                = $request->validated();
+        $data = $request->validated();
+
         $data['provider_id'] = $providerId;
 
         $providerSpeciality = $this->providerSpecialityService->create($data);

+ 1 - 0
app/Http/Controllers/ProviderWorkingDayController.php

@@ -32,6 +32,7 @@ class ProviderWorkingDayController extends Controller
     {
         $day    = $request->query('day');
         $period = $request->query('period');
+
         $this->service->delete($id, $day, $period);
 
         return $this->successResponse(

+ 2 - 1
app/Http/Controllers/UserTypePermissionController.php

@@ -22,7 +22,8 @@ class UserTypePermissionController extends Controller
 
     public function allPermissionsByUserType(): JsonResponse
     {
-        $user               = Auth::user();
+        $user = Auth::user();
+
         $userTypePermission = $this->userTypePermissionService->allPermissionsByUserType($user->type);
 
         return $this->successResponse(payload: UserTypePermissionResource::collection($userTypePermission));

+ 2 - 2
app/Http/Requests/PaymentRequest.php

@@ -25,7 +25,7 @@ class PaymentRequest extends FormRequest
             'gateway_operation_reference' => ['nullable', 'string', 'max:255'],
             'gateway_operation_label'     => ['nullable', 'string', 'max:255'],
 
-            'payment_method' => ['sometimes', 'in:credit_card,pix,boleto'],
+            'payment_method' => ['sometimes', 'in:credit_card,pix'],
             'status'         => ['sometimes', 'in:pending,processing,authorized,paid,failed,cancelled'],
 
             'gross_amount'        => ['sometimes', 'numeric', 'min:0'],
@@ -50,7 +50,7 @@ class PaymentRequest extends FormRequest
         if ($this->isMethod('POST')) {
             $rules['schedule_id']    = ['required', 'integer', 'exists:schedules,id'];
             $rules['client_id']      = ['required', 'integer', 'exists:clients,id'];
-            $rules['payment_method'] = ['required', 'in:credit_card,pix,boleto'];
+            $rules['payment_method'] = ['required', 'in:credit_card,pix'];
             $rules['gross_amount']   = ['required', 'numeric', 'min:0'];
             $rules['net_amount']     = ['required', 'numeric', 'min:0'];
         }

+ 3 - 0
app/Jobs/FinishScheduleJob.php

@@ -66,14 +66,17 @@ class FinishScheduleJob implements ShouldQueue
             ]);
 
             $provider = Provider::find($schedule->provider_id);
+
             $provider->update([
                 'total_services' => $provider->total_services + 1,
             ]);
 
             $client = Client::find($schedule->client_id);
+
             $client->update([
                 'total_services' => $client->total_services + 1,
             ]);
+
             $emailService  = new EmailService;
             $serviceAmount = (float) $schedule->total_amount;
             $serviceFee    = $serviceAmount * 0.11;

+ 2 - 0
app/Jobs/StartScheduleJob.php

@@ -58,7 +58,9 @@ class StartScheduleJob implements ShouldQueue
             $schedule->update([
                 'status' => 'started',
             ]);
+
             $date_time_dispatch = Carbon::parse($date_cleaned.' '.$schedule->end_time);
+
             FinishScheduleJob::dispatch($schedule->id)->delay($date_time_dispatch);
 
             // dispatch de teste em local

+ 27 - 1
app/Services/AuthService.php

@@ -65,6 +65,7 @@ class AuthService
         }
 
         $user = $tokenModel->tokenable;
+
         if (! $user) {
             return null;
         }
@@ -85,6 +86,7 @@ class AuthService
     public function logout(): void
     {
         $user = Auth::user();
+
         if (! $user) {
             return;
         }
@@ -124,6 +126,7 @@ class AuthService
     {
         try {
             DB::beginTransaction();
+
             $code = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
 
             $user = User::where(function ($query) use ($data) {
@@ -135,28 +138,37 @@ class AuthService
                     });
             })
                 ->first();
+
             $isLogin = false;
+
             if ($user) {
                 if ($user->type->value !== UserTypeEnum::CLIENT->value) {
                     DB::rollBack();
 
                     return ['error' => 'wrong_user_type'];
                 }
+
                 $user->code           = $code;
                 $user->validated_code = false;
+
                 $user->save();
 
-                $client     = Client::where('user_id', $user->id)->first();
+                $client = Client::where('user_id', $user->id)->first();
+
                 $hasAddress = $client && Address::where('source', 'client')
                     ->where('source_id', $client->id)
                     ->exists();
+
                 $isLogin = $client && $hasAddress;
             } else {
                 $user = new User;
+
                 $user->fill($data);
+
                 $user->code = $code;
                 $user->name = $data['name'] ?? 'Usuário';
                 $user->type = $data['type'] ?? UserTypeEnum::CLIENT->value;
+
                 $user->save();
 
                 Client::create(['user_id' => $user->id]);
@@ -192,6 +204,7 @@ class AuthService
     {
         try {
             DB::beginTransaction();
+
             $code = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
 
             $user = User::where(function ($query) use ($data) {
@@ -203,14 +216,18 @@ class AuthService
                     });
             })
                 ->first();
+
             $isLogin = false;
+
             if ($user) {
                 if ($user->type->value !== UserTypeEnum::PROVIDER->value) {
                     DB::rollBack();
 
                     return ['error' => 'wrong_user_type'];
                 }
+
                 $provider = Provider::where('user_id', $user->id)->first();
+
                 if ($provider && $provider->approval_status && $provider->approval_status->value !== ApprovalStatusEnum::ACCEPTED->value) {
                     DB::rollBack();
 
@@ -219,14 +236,19 @@ class AuthService
 
                 $user->code           = $code;
                 $user->validated_code = false;
+
                 $user->save();
+
                 $isLogin = true;
             } else {
                 $user = new User;
+
                 $user->fill($data);
+
                 $user->code = $code;
                 $user->name = $data['name'] ?? 'Usuário';
                 $user->type = $data['type'] ?? UserTypeEnum::PROVIDER->value;
+
                 $user->save();
             }
 
@@ -247,6 +269,7 @@ class AuthService
             return $isLogin;
         } catch (\Exception $e) {
             DB::rollBack();
+
             Log::error('Erro ao enviar código de verificação.', [
                 'error' => $e->getMessage(),
                 'data'  => $data,
@@ -299,6 +322,7 @@ class AuthService
 
         if ($isLogin) {
             $user->load('provider');
+
             $provider = $user->provider ?? null;
 
             if ($provider && $provider->approval_status === ApprovalStatusEnum::PENDING->value) {
@@ -354,11 +378,13 @@ class AuthService
         if (! $user) {
             return null;
         }
+
         $deviceId             = Str::uuid()->toString();
         $accessToken          = $user->createAccessTokenApp($deviceId);
         $refreshToken         = $user->createRefreshTokenApp($deviceId);
         $user->validated_code = true;
         $user->code           = null;
+
         $user->save();
 
         return [

+ 2 - 1
app/Services/ClientCalendarService.php

@@ -13,7 +13,8 @@ class ClientCalendarService
 
     public function getCalendar(): array
     {
-        $user   = Auth::user();
+        $user = Auth::user();
+
         $client = Client::where('user_id', $user->id)->first();
 
         $selectFields = [

+ 3 - 0
app/Services/ClientFavoriteProviderService.php

@@ -49,6 +49,7 @@ class ClientFavoriteProviderService
     public function create(array $data): ClientFavoriteProviderResource
     {
         $favorite = ClientFavoriteProvider::create($data);
+
         $favorite->load('provider');
 
         return new ClientFavoriteProviderResource($favorite);
@@ -57,7 +58,9 @@ class ClientFavoriteProviderService
     public function update(int $id, array $data): ClientFavoriteProviderResource
     {
         $favorite = ClientFavoriteProvider::findOrFail($id);
+
         $favorite->update($data);
+
         $favorite->load('provider');
 
         return new ClientFavoriteProviderResource($favorite);

+ 1 - 0
app/Services/ClientProviderBlockService.php

@@ -51,6 +51,7 @@ class ClientProviderBlockService
         }
 
         $block = ClientProviderBlock::create($data);
+
         $block->load(['provider.user']);
 
         return $block;

+ 28 - 3
app/Services/CustomScheduleService.php

@@ -15,6 +15,10 @@ use Illuminate\Support\Facades\Log;
 
 class CustomScheduleService
 {
+    public function __construct(
+        private readonly PaymentService $paymentService,
+    ) {}
+
     public function getAll()
     {
         $custom_schedules = CustomSchedule::with([
@@ -44,6 +48,7 @@ class CustomScheduleService
     public function create(array $data)
     {
         DB::beginTransaction();
+
         try {
             $quantity      = $data['quantity'] ?? 1;
             $specialityIds = $data['speciality_ids'] ?? [];
@@ -110,23 +115,29 @@ class CustomScheduleService
     public function update($id, array $data)
     {
         DB::beginTransaction();
+
         try {
             $customSchedule = CustomSchedule::findOrFail($id);
             $schedule       = $customSchedule->schedule;
 
             $scheduleUpdateData = [];
+
             if (isset($data['address_id'])) {
                 $scheduleUpdateData['address_id'] = $data['address_id'];
             }
+
             if (isset($data['date'])) {
                 $scheduleUpdateData['date'] = $data['date'];
             }
+
             if (isset($data['period_type'])) {
                 $scheduleUpdateData['period_type'] = $data['period_type'];
             }
+
             if (isset($data['start_time'])) {
                 $scheduleUpdateData['start_time'] = $data['start_time'];
             }
+
             if (isset($data['end_time'])) {
                 $scheduleUpdateData['end_time'] = $data['end_time'];
             }
@@ -136,21 +147,27 @@ class CustomScheduleService
             }
 
             $customScheduleUpdateData = [];
+
             if (isset($data['address_type'])) {
                 $customScheduleUpdateData['address_type'] = $data['address_type'];
             }
+
             if (isset($data['service_type_id'])) {
                 $customScheduleUpdateData['service_type_id'] = $data['service_type_id'];
             }
+
             if (isset($data['description'])) {
                 $customScheduleUpdateData['description'] = $data['description'];
             }
+
             if (isset($data['min_price'])) {
                 $customScheduleUpdateData['min_price'] = $data['min_price'];
             }
+
             if (isset($data['max_price'])) {
                 $customScheduleUpdateData['max_price'] = $data['max_price'];
             }
+
             if (isset($data['offers_meal'])) {
                 $customScheduleUpdateData['offers_meal'] = $data['offers_meal'];
             }
@@ -161,6 +178,7 @@ class CustomScheduleService
 
             if (isset($data['speciality_ids'])) {
                 $custom_schedule = CustomScheduleSpeciality::where('custom_schedule_id', $customSchedule->id);
+
                 $custom_schedule->delete();
 
                 foreach ($data['speciality_ids'] as $specialityId) {
@@ -189,6 +207,7 @@ class CustomScheduleService
     public function delete($id)
     {
         DB::beginTransaction();
+
         try {
             $customSchedule = CustomSchedule::findOrFail($id);
             $schedule       = $customSchedule->schedule;
@@ -333,14 +352,12 @@ class CustomScheduleService
             $proposal = ScheduleProposal::findOrFail($proposalId);
             $schedule = $proposal->schedule;
 
-            Log::info($schedule);
             if ($schedule->provider_id) {
                 throw new \Exception(__('validation.custom.opportunity.already_assigned'));
             }
 
-            Log::info('vrauu2');
-
             $provider = Provider::find($proposal->provider_id);
+
             switch ($schedule->period_type) {
                 case '8':
                     $baseAmount = $provider->daily_price_8h;
@@ -355,8 +372,11 @@ class CustomScheduleService
                     $baseAmount = $provider->daily_price_2h;
                     break;
                 default:
+                    throw new \Exception('Periodo do agendamento invalido.');
             }
+
             $schedule->total_amount = $baseAmount;
+
             $schedule->save();
 
             $schedule->update([
@@ -368,6 +388,8 @@ class CustomScheduleService
                 ->where('id', '!=', $proposalId)
                 ->delete();
 
+            $this->paymentService->createPagarmeOrderForAcceptedSchedule($schedule);
+
             return $schedule->fresh(['provider.user']);
         });
     }
@@ -407,12 +429,14 @@ class CustomScheduleService
             $provider_id,
             $date_ymd
         );
+
         // bloqueio provider trabalha no dia/periodo
         ScheduleBusinessRules::validateWorkingDay(
             $provider_id,
             $dayOfWeek,
             $period
         );
+
         // bloqueio provider tem blockedday para dia/hora
         ScheduleBusinessRules::validateBlockedDay(
             $provider_id,
@@ -436,6 +460,7 @@ class CustomScheduleService
             $startTime,
             $endTime
         );
+
         // bloqueio provider tem outra proposta para o mesmo agendamento
         ScheduleBusinessRules::validateConflictingSameProposal(
             $provider_id,

+ 2 - 0
app/Services/CustomScheduleSpecialityService.php

@@ -33,6 +33,7 @@ class CustomScheduleSpecialityService
     public function update($id, array $data)
     {
         $customScheduleSpeciality = CustomScheduleSpeciality::findOrFail($id);
+
         $customScheduleSpeciality->update($data);
 
         return $customScheduleSpeciality->fresh(['customSchedule', 'speciality']);
@@ -41,6 +42,7 @@ class CustomScheduleSpecialityService
     public function delete($id)
     {
         $customScheduleSpeciality = CustomScheduleSpeciality::findOrFail($id);
+
         $customScheduleSpeciality->delete();
 
         return $customScheduleSpeciality;

+ 12 - 6
app/Services/DashboardService.php

@@ -22,8 +22,10 @@ class DashboardService
 
     public function dadosDashboardCliente(): array
     {
-        $user      = Auth::user();
-        $cliente   = Client::where('user_id', $user->id)->first();
+        $user = Auth::user();
+
+        $cliente = Client::where('user_id', $user->id)->first();
+
         $headerBar = [
             'rating'        => $cliente->average_rating,
             'total_ratings' => Review::where('reviews.origin', 'provider')
@@ -111,7 +113,8 @@ class DashboardService
 
         $blockedProviderIds       = ScheduleBusinessRules::getBlockedProviderIdsForClient($cliente->id);
         $providersWithWorkingDays = ScheduleBusinessRules::getProviderIdsWithWorkingDays();
-        $clientPrimaryAddress     = Address::where('source', 'client')
+
+        $clientPrimaryAddress = Address::where('source', 'client')
             ->where('source_id', $cliente->id)
             ->where('is_primary', true)
             ->first();
@@ -257,7 +260,8 @@ class DashboardService
 
     public function getScheduleClienteDetails(int $scheduleId): array
     {
-        $user    = Auth::user();
+        $user = Auth::user();
+
         $cliente = Client::where('user_id', $user->id)->firstOrFail();
 
         $schedule = Schedule::where('schedules.id', $scheduleId)
@@ -301,7 +305,8 @@ class DashboardService
 
     public function dadosDashboardPrestador(): array
     {
-        $user     = Auth::user();
+        $user = Auth::user();
+
         $provider = Provider::where('user_id', $user->id)->first();
 
         $headerBar = [
@@ -310,7 +315,8 @@ class DashboardService
             'total_services' => $provider->total_services,
         ];
 
-        $address      = Address::where('source', 'provider')->where('source_id', $provider->id)->with(['city', 'state'])->first();
+        $address = Address::where('source', 'provider')->where('source_id', $provider->id)->with(['city', 'state'])->first();
+
         $summaryInfos = [
             'name'             => $user->name,
             'address'          => $address,

+ 31 - 6
app/Services/Pagarme/PagarmeCustomerService.php

@@ -16,7 +16,7 @@ class PagarmeCustomerService
 
         $client->loadMissing('user');
 
-        $name  = $client->user?->name ?? $data['name'] ?? 'Cliente';
+        $name  = $client->user?->name  ?? $data['name']  ?? 'Cliente';
         $email = $client->user?->email ?? $data['email'] ?? null;
 
         $document = $this->sanitizeDigits($client->document ?? $data['document'] ?? null);
@@ -38,7 +38,7 @@ class PagarmeCustomerService
 
         $code = $client->external_customer_code ?? "client-{$client->id}";
 
-        $payload = [
+        $payload = $this->filterFilledRecursive([
             'name'          => $name,
             'email'         => $email,
             'document'      => $document,
@@ -46,7 +46,7 @@ class PagarmeCustomerService
             'document_type' => $this->documentType($document),
             'code'          => $code,
             'address'       => $address,
-        ];
+        ]);
 
         if (! empty($phones)) {
             $payload['phones'] = $phones;
@@ -66,7 +66,8 @@ class PagarmeCustomerService
         }
 
         $customerData = $response->json();
-        $customerId   = $customerData['id'] ?? null;
+
+        $customerId = $customerData['id'] ?? null;
 
         if (! $customerId) {
             Log::channel('pagarme')->error('Customer creation returned an empty id.', [
@@ -137,8 +138,9 @@ class PagarmeCustomerService
         }
 
         $countryCode = '55';
-        $areaCode    = substr($digits, 0, 2);
-        $number      = substr($digits, 2);
+
+        $areaCode = substr($digits, 0, 2);
+        $number   = substr($digits, 2);
 
         if (strlen($digits) <= 2) {
             $areaCode = '';
@@ -166,6 +168,29 @@ class PagarmeCustomerService
         return strlen($document) === 14 ? 'company' : 'individual';
     }
 
+    //
+
+    /**
+     * @param  array<string, mixed>  $data
+     * @return array<string, mixed>
+     */
+    private function filterFilledRecursive(array $data): array
+    {
+        $filtered = [];
+
+        foreach ($data as $key => $value) {
+            if (is_array($value)) {
+                $value = $this->filterFilledRecursive($value);
+            }
+
+            if ($value !== null && $value !== '' && $value !== []) {
+                $filtered[$key] = $value;
+            }
+        }
+
+        return $filtered;
+    }
+
     private function sanitizeDigits(?string $value): string
     {
         return preg_replace('/\D+/', '', (string) $value) ?? '';

+ 108 - 19
app/Services/Pagarme/PagarmePaymentService.php

@@ -61,7 +61,11 @@ class PagarmePaymentService
     }
 
     public function createOrder(
-        Payment $payment, array $items, array $customer, array $paymentMethod, array $options = []
+        Payment $payment, 
+        array   $items,
+        array   $customer,
+        array   $paymentMethod,
+        array   $options = []
     ): array {
         if (empty($items)) {
             throw new \InvalidArgumentException('items nao pode estar vazio.');
@@ -71,11 +75,24 @@ class PagarmePaymentService
             throw new \InvalidArgumentException('payment_method e obrigatorio.');
         }
 
+        if (! in_array($paymentMethod['payment_method'], ['credit_card', 'pix'], true)) {
+            throw new \InvalidArgumentException('payment_method deve ser credit_card ou pix.');
+        }
+
+        $customerPayload = $options['customer_id'] ?? null;
+
+        if (! $this->filled($customerPayload)) {
+            $customerPayload = $this->filterFilledRecursive($customer);
+        }
+
+        if (empty($customerPayload)) {
+            throw new \InvalidArgumentException('customer ou customer_id e obrigatorio.');
+        }
+
         $payload = [
             'code'     => $options['order_code'] ?? "payment-{$payment->id}",
-            'items'    => $items,
-            'customer' => $customer,
-            'payments' => [$paymentMethod],
+            'items'    => $this->validateItems($items),
+            'payments' => [$this->filterFilledRecursive($paymentMethod)],
             'closed'   => $options['closed'] ?? true,
 
             'metadata' => array_merge([
@@ -86,6 +103,12 @@ class PagarmePaymentService
             ], $options['metadata'] ?? []),
         ];
 
+        if (is_string($customerPayload)) {
+            $payload['customer_id'] = $customerPayload;
+        } else {
+            $payload['customer'] = $customerPayload;
+        }
+
         if (! empty($options['channel'])) {
             $payload['channel'] = $options['channel'];
         }
@@ -119,9 +142,11 @@ class PagarmePaymentService
 
     public function applyGatewayResponseToPayment(Payment $payment, array $orderResponse): Payment
     {
-        $charge            = $orderResponse['charges'][0] ?? [];
-        $transaction       = $charge['last_transaction'] ?? [];
-        $chargeStatus      = $charge['status'] ?? null;
+        $charge = $orderResponse['charges'][0] ?? [];
+
+        $transaction  = $charge['last_transaction']  ?? [];
+        $chargeStatus = $charge['status'] ?? null;
+
         $transactionStatus = $transaction['status'] ?? null;
 
         $payment->forceFill([
@@ -131,7 +156,7 @@ class PagarmePaymentService
             'gateway_operation_reference' => $transaction['id'] ?? $charge['id'] ?? $orderResponse['id'] ?? null,
             'gateway_operation_label'     => isset($transaction['id']) ? 'transaction' : (isset($charge['id']) ? 'charge' : 'order'),
             'status'                      => $this->mapPaymentStatus($chargeStatus, $transactionStatus),
-            'paid_at'                     => $charge['paid_at'] ?? null,
+            'paid_at'                     => $this->filledArrayValue($charge, 'paid_at'),
             'authorized_at'               => $this->resolveAuthorizedAt($transactionStatus, $transaction),
             'gateway_payload'             => $orderResponse,
             'failure_code'                => $this->extractFailureCode($transaction),
@@ -151,11 +176,16 @@ class PagarmePaymentService
             ->filter(fn (PaymentTransfer $transfer) => ! empty($transfer->gateway_transfer_target_reference))
             ->map(function (PaymentTransfer $transfer) {
                 return [
-                    'amount'       => $this->toGatewayAmountInCents((float) $transfer->gross_amount),
-                    'recipient_id' => $transfer->gateway_transfer_target_reference,
-                    'type'         => 'flat',
-                ];
-            })
+            'amount'       => $this->toGatewayAmountInCents((float) $transfer->gross_amount),
+            'recipient_id' => $transfer->gateway_transfer_target_reference,
+            'type'         => 'flat',
+            'options'      => [
+                'charge_processing_fee' => false,
+                'charge_remainder_fee'  => false,
+                'liable'                => false,
+            ],
+        ];
+    })
             ->values()
             ->all();
     }
@@ -202,7 +232,7 @@ class PagarmePaymentService
             'channel',
             'payment_origin',
         ] as $field) {
-            if (array_key_exists($field, $creditCard)) {
+            if (array_key_exists($field, $creditCard) && $this->filled($creditCard[$field])) {
                 $payload[$field] = $creditCard[$field];
             }
         }
@@ -227,14 +257,14 @@ class PagarmePaymentService
 
     private function buildPixPayload(array $pix): array
     {
-        if (empty($pix['expires_in']) && empty($pix['expires_at'])) {
+        if (! $this->filled($pix['expires_in'] ?? null) && ! $this->filled($pix['expires_at'] ?? null)) {
             throw new \InvalidArgumentException('pix.expires_in ou pix.expires_at e obrigatorio.');
         }
 
         $payload = [];
 
         foreach (['expires_in', 'expires_at', 'additional_information'] as $field) {
-            if (array_key_exists($field, $pix)) {
+            if (array_key_exists($field, $pix) && $this->filled($pix[$field])) {
                 $payload[$field] = $pix[$field];
             }
         }
@@ -247,7 +277,7 @@ class PagarmePaymentService
     private function resolveAuthorizedAt(?string $transactionStatus, array $transaction): ?string
     {
         if (in_array($transactionStatus, ['authorized_pending_capture', 'captured', 'partial_capture'], true)) {
-            return $transaction['created_at'] ?? now()->toISOString();
+            return $this->filledArrayValue($transaction, 'created_at');
         }
 
         return null;
@@ -257,12 +287,71 @@ class PagarmePaymentService
 
     private function extractFailureCode(array $transaction): ?string
     {
-        return $transaction['gateway_response']['code'] ?? null;
+        return $this->filledArrayValue($transaction['gateway_response'] ?? [], 'code');
     }
 
     private function extractFailureMessage(array $transaction): ?string
     {
-        return $transaction['acquirer_message'] ?? null;
+        return $this->filledArrayValue($transaction, 'acquirer_message');
+    }
+
+    /**
+     * @param  array<int, array<string, mixed>>  $items
+     * @return array<int, array<string, mixed>>
+     */
+    private function validateItems(array $items): array
+    {
+        return collect($items)
+            ->map(function (array $item, int $index) {
+                foreach (['code', 'amount', 'quantity'] as $field) {
+                    if (! array_key_exists($field, $item) || ! $this->filled($item[$field])) {
+                        throw new \InvalidArgumentException("items.{$index}.{$field} e obrigatorio.");
+                    }
+                }
+
+                if ((int) $item['amount'] <= 0 || (int) $item['quantity'] <= 0) {
+                    throw new \InvalidArgumentException("items.{$index}.amount e quantity devem ser maiores que zero.");
+                }
+
+                return $this->filterFilledRecursive($item);
+            })
+            ->values()
+            ->all();
+    }
+
+    private function filledArrayValue(array $data, string $field): ?string
+    {
+        if (! array_key_exists($field, $data) || ! $this->filled($data[$field])) {
+            return null;
+        }
+
+        return (string) $data[$field];
+    }
+
+    /**
+     * @param  array<string, mixed>  $data
+     * @return array<string, mixed>
+     */
+    private function filterFilledRecursive(array $data): array
+    {
+        $filtered = [];
+
+        foreach ($data as $key => $value) {
+            if (is_array($value)) {
+                $value = $this->filterFilledRecursive($value);
+            }
+
+            if ($this->filled($value)) {
+                $filtered[$key] = $value;
+            }
+        }
+
+        return $filtered;
+    }
+
+    private function filled(mixed $value): bool
+    {
+        return $value !== null && $value !== '' && $value !== [];
     }
 
     //

+ 35 - 10
app/Services/Pagarme/PagarmeRecipientService.php

@@ -23,7 +23,7 @@ class PagarmeRecipientService
         $monthlyIncome = isset($data['monthly_income']) ? (int) $data['monthly_income'] : 1000;
         $occupation    = $data['professional_occupation'] ?? 'autonomo';
 
-        $payload = [
+        $payload = $this->filterFilledRecursive([
             'code' => preg_replace('/\D+/', '', $data['recipient_code']),
 
             'register_information' => [
@@ -41,7 +41,7 @@ class PagarmeRecipientService
                     'complementary'   => $addressParts['complementary'],
                     'street_number'   => $addressParts['street_number'],
                     'neighborhood'    => $addressParts['neighborhood'],
-                    'city'            => $data['city'] ?? null,
+                    'city'            => $data['city']  ?? null,
                     'state'           => $data['state'] ?? null,
                     'zip_code'        => preg_replace('/\D+/', '', $data['zip_code']),
                     'reference_point' => $addressParts['reference_point'],
@@ -69,7 +69,7 @@ class PagarmeRecipientService
             'automatic_anticipation_settings' => [
                 'enabled' => false,
             ],
-        ];
+        ]);
 
         $response = $this->pagarmeRequest($provider->id)
             ->post($this->pagarmeUrl('/recipients'), $payload);
@@ -85,7 +85,8 @@ class PagarmeRecipientService
         }
 
         $recipientData = $response->json();
-        $recipientId   = $recipientData['id'] ?? null;
+
+        $recipientId = $recipientData['id'] ?? null;
 
         if (! $recipientId) {
             Log::channel('pagarme')->error('Pagar.me recipient creation returned empty id', [
@@ -179,13 +180,15 @@ class PagarmeRecipientService
     private function extractAddressParts(array $data): array
     {
         $addressLine = trim((string) ($data['address'] ?? ''));
-        $segments    = array_map('trim', explode(',', $addressLine));
 
-        $streetSegment  = $segments[0] ?? '';
-        $streetNumber   = $data['number'] ?? null;
-        $neighborhood   = $data['district'] ?? null;
+        $segments = array_map('trim', explode(',', $addressLine));
+
+        $streetSegment = $segments[0] ?? '';
+
+        $streetNumber   = $data['number']          ?? null;
+        $neighborhood   = $data['district']        ?? null;
         $referencePoint = $data['reference_point'] ?? null;
-        $complementary  = $data['complement'] ?? null;
+        $complementary  = $data['complement']      ?? null;
 
         if ($streetNumber === null) {
             preg_match('/^(\d+)/', $streetSegment, $matches);
@@ -229,7 +232,8 @@ class PagarmeRecipientService
         }
 
         $areaCode = substr($digits, 0, 2);
-        $number   = substr($digits, 2);
+
+        $number = substr($digits, 2);
 
         return [[
             'ddd'    => $areaCode,
@@ -237,4 +241,25 @@ class PagarmeRecipientService
             'type'   => 'mobile',
         ]];
     }
+
+    /**
+     * @param  array<string, mixed>  $data
+     * @return array<string, mixed>
+     */
+    private function filterFilledRecursive(array $data): array
+    {
+        $filtered = [];
+
+        foreach ($data as $key => $value) {
+            if (is_array($value)) {
+                $value = $this->filterFilledRecursive($value);
+            }
+
+            if ($value !== null && $value !== '' && $value !== []) {
+                $filtered[$key] = $value;
+            }
+        }
+
+        return $filtered;
+    }
 }

+ 216 - 1
app/Services/PaymentService.php

@@ -2,11 +2,22 @@
 
 namespace App\Services;
 
+use App\Models\Address;
+use App\Models\ClientPaymentMethod;
 use App\Models\Payment;
+use App\Models\PaymentTransfer;
+use App\Models\Schedule;
+use App\Services\Pagarme\PagarmePaymentService;
+use Carbon\Carbon;
 use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Support\Str;
 
 class PaymentService
 {
+    public function __construct(
+        private readonly PagarmePaymentService $pagarmePaymentService,
+    ) {}
+
     public function getAll(): Collection
     {
         return Payment::query()
@@ -48,5 +59,209 @@ class PaymentService
         return $model->delete();
     }
 
-    // Add custom business logic methods here
+    public function createPagarmeOrderForAcceptedSchedule(Schedule $schedule): Payment
+    {
+        $schedule->loadMissing(['client', 'provider', 'customSchedule.serviceType']);
+
+        if (! $schedule->provider_id || ! $schedule->provider) {
+            throw new \InvalidArgumentException('Agendamento precisa ter prestador confirmado para gerar pagamento.');
+        }
+
+        if ((float) $schedule->total_amount <= 0) {
+            throw new \InvalidArgumentException('Agendamento precisa ter valor maior que zero para gerar pagamento.');
+        }
+
+        if (empty($schedule->provider->recipient_id)) {
+            throw new \InvalidArgumentException('Prestador precisa ter recipient_id do Pagar.me para receber split.');
+        }
+
+        $existingPayment = Payment::query()
+            ->where('schedule_id', $schedule->id)
+            ->whereIn('status', ['pending', 'processing', 'authorized', 'paid'])
+            ->latest('id')
+            ->first();
+
+        if ($existingPayment) {
+            return $existingPayment;
+        }
+
+        $clientPaymentMethod = ClientPaymentMethod::query()
+            ->where('client_id', $schedule->client_id)
+            ->where('is_active', true)
+            ->latest('id')
+            ->first();
+
+        $paymentMethod = $clientPaymentMethod?->token ? 'credit_card' : 'pix';
+        $serviceAmount = (float) $schedule->total_amount;
+        $platformFee   = round($serviceAmount * 0.11, 2);
+        $grossAmount   = round($serviceAmount + $platformFee, 2);
+
+        $payment = Payment::create([
+            'schedule_id'              => $schedule->id,
+            'client_id'                => $schedule->client_id,
+            'provider_id'              => $schedule->provider_id,
+            'client_payment_method_id' => $paymentMethod === 'credit_card' ? $clientPaymentMethod->id : null,
+            'gateway_provider'         => 'pagarme',
+            'payment_method'           => $paymentMethod,
+            'status'                   => 'pending',
+            'gross_amount'             => $grossAmount,
+            'gateway_fee_amount'       => 0,
+            'platform_fee_amount'      => $platformFee,
+            'net_amount'               => $grossAmount,
+            'currency'                 => 'BRL',
+            'installments'             => 1,
+            'expires_at'               => $paymentMethod === 'pix' ? Carbon::now()->addMinutes(30) : null,
+            'metadata'                 => [
+                'service_amount' => number_format($serviceAmount, 2, '.', ''),
+                'platform_fee'   => number_format($platformFee, 2, '.', ''),
+            ],
+        ]);
+
+        $transfer = PaymentTransfer::create([
+            'payment_id'                         => $payment->id,
+            'provider_id'                        => $schedule->provider_id,
+            'gateway_provider'                   => 'pagarme',
+            'gateway_transfer_target_reference'  => $schedule->provider->recipient_id,
+            'gateway_transfer_target_label'      => 'recipient',
+            'status'                             => 'pending',
+            'gross_amount'                       => $serviceAmount,
+            'gateway_fee_amount'                 => 0,
+            'net_amount'                         => $serviceAmount,
+            'metadata'                           => [
+                'schedule_id' => (string) $schedule->id,
+            ],
+        ]);
+
+        $items    = $this->buildOrderItems($schedule, $grossAmount);
+        $customer = $this->buildCustomerPayload($schedule);
+        $split    = $this->pagarmePaymentService->buildSplitFromTransfers(collect([$transfer]));
+
+        $orderResponse = $paymentMethod === 'credit_card'
+            ? $this->pagarmePaymentService->createOrderWithCreditCard(
+                payment: $payment,
+                items: $items,
+                customer: $customer,
+                creditCard: [
+                    'installments'          => 1,
+                    'statement_descriptor'  => Str::limit((string) config('app.name', 'SOFTPAR'), 13, ''),
+                    'operation_type'        => 'auth_and_capture',
+                    'card_token'            => $clientPaymentMethod->token,
+                ],
+                options: ['split' => $split],
+            )
+            : $this->pagarmePaymentService->createOrderWithPix(
+                payment: $payment,
+                items: $items,
+                customer: $customer,
+                pix: [
+                    'expires_at' => $payment->expires_at?->toISOString(),
+                ],
+                options: ['split' => $split],
+            );
+
+        return $this->pagarmePaymentService->applyGatewayResponseToPayment($payment, $orderResponse);
+    }
+
+    /**
+     * @return array<int, array<string, mixed>>
+     */
+    private function buildOrderItems(Schedule $schedule, float $grossAmount): array
+    {
+        $description = $schedule->customSchedule?->serviceType?->description
+            ?? "Servico {$schedule->id}";
+
+        return [[
+            'amount'      => $this->pagarmePaymentService->toGatewayAmountInCents($grossAmount),
+            'description' => $description,
+            'quantity'    => 1,
+            'code'        => "schedule-{$schedule->id}",
+        ]];
+    }
+
+    /**
+     * @return array<string, mixed>
+     */
+    private function buildCustomerPayload(Schedule $schedule): array
+    {
+        $client  = $schedule->client;
+        $user    = $client->user()->first(['id', 'name', 'email', 'phone']);
+        $address = Address::with(['city.state', 'state'])->find($schedule->address_id);
+
+        if (! $user?->name || ! $user?->email || ! $client->document) {
+            throw new \InvalidArgumentException('Cliente precisa ter nome, email e documento para criar pedido no Pagar.me.');
+        }
+
+        if (! $address) {
+            throw new \InvalidArgumentException('Endereco do agendamento nao encontrado para criar pedido no Pagar.me.');
+        }
+
+        $document = $this->digits($client->document);
+        $phone    = $this->buildPhonePayload($user->phone);
+        $state    = $address->state?->code ?? $address->city?->state?->code;
+        $city     = $address->city?->name;
+        $zipCode  = $this->digits($address->zip_code);
+        $line1    = implode(', ', array_filter([
+            $address->number ?: 'S/N',
+            $address->address,
+            $address->district,
+        ]));
+
+        foreach ([
+            'documento' => $document,
+            'telefone'  => $phone,
+            'estado'    => $state,
+            'cidade'    => $city,
+            'cep'       => $zipCode,
+            'endereco'  => $line1,
+        ] as $field => $value) {
+            if ($value === null || $value === '' || $value === []) {
+                throw new \InvalidArgumentException("Cliente precisa ter {$field} valido para criar pedido no Pagar.me.");
+            }
+        }
+
+        return [
+            'name'          => $user->name,
+            'email'         => $user->email,
+            'code'          => "client-{$client->id}",
+            'document'      => $document,
+            'document_type' => strlen($document) === 14 ? 'CNPJ' : 'CPF',
+            'type'          => strlen($document) === 14 ? 'company' : 'individual',
+            'address'       => [
+                'country'  => 'BR',
+                'state'    => $state,
+                'city'     => $city,
+                'zip_code' => $zipCode,
+                'line_1'   => $line1,
+                'line_2'   => $address->complement ?: $address->instructions,
+            ],
+            'phones'        => ['mobile_phone' => $phone],
+        ];
+    }
+
+    /**
+     * @return array<string, string>|null
+     */
+    private function buildPhonePayload(?string $phone): ?array
+    {
+        $digits = $this->digits($phone);
+
+        if (strlen($digits) < 10) {
+            return null;
+        }
+
+        if (str_starts_with($digits, '55')) {
+            $digits = substr($digits, 2);
+        }
+
+        return [
+            'country_code' => '55',
+            'area_code'    => substr($digits, 0, 2),
+            'number'       => substr($digits, 2),
+        ];
+    }
+
+    private function digits(?string $value): string
+    {
+        return preg_replace('/\D+/', '', (string) $value) ?? '';
+    }
 }

+ 2 - 1
app/Services/ProviderCalendarService.php

@@ -13,7 +13,8 @@ class ProviderCalendarService
 
     public function getCalendar(): array
     {
-        $user     = Auth::user();
+        $user = Auth::user();
+
         $provider = Provider::where('user_id', $user->id)->first();
 
         $selectFields = [

+ 1 - 0
app/Services/ProviderClientBlockService.php

@@ -36,6 +36,7 @@ class ProviderClientBlockService
         }
 
         $block = ProviderClientBlock::create($data);
+
         $block->load(['client.user']);
 
         return $block;

+ 23 - 12
app/Services/ProviderService.php

@@ -89,6 +89,7 @@ class ProviderService
     {
         return DB::transaction(function () use ($id) {
             $provider = Provider::findOrFail($id);
+
             $provider->update(['approval_status' => ApprovalStatusEnum::ACCEPTED->value]);
 
             return $provider->fresh(['user', 'profileMedia']);
@@ -99,6 +100,7 @@ class ProviderService
     {
         return DB::transaction(function () use ($id) {
             $provider = Provider::findOrFail($id);
+
             $provider->update(['approval_status' => ApprovalStatusEnum::REJECTED->value]);
 
             return $provider->fresh(['user', 'profileMedia']);
@@ -145,19 +147,24 @@ class ProviderService
 
             $user->save();
 
-            $provider                              = new Provider;
-            $provider->user_id                     = $user->id;
-            $provider->rg                          = $data['rg'] ?? null;
-            $provider->document                    = $this->sanitizeDigits($data['document'] ?? null);
-            $provider->birth_date                  = $data['birth_date'] ?? null;
-            $provider->daily_price_8h              = $data['daily_price_8h'] ?? null;
-            $provider->daily_price_6h              = $data['daily_price_6h'] ?? null;
-            $provider->daily_price_4h              = $data['daily_price_4h'] ?? null;
-            $provider->daily_price_2h              = $data['daily_price_2h'] ?? null;
-            $provider->approval_status             = ApprovalStatusEnum::PENDING->value;
+            $provider = new Provider;
+
+            $provider->user_id    = $user->id;
+            $provider->rg         = $data['rg'] ?? null;
+            $provider->document   = $this->sanitizeDigits($data['document'] ?? null);
+            $provider->birth_date = $data['birth_date'] ?? null;
+
+            $provider->daily_price_8h = $data['daily_price_8h'] ?? null;
+            $provider->daily_price_6h = $data['daily_price_6h'] ?? null;
+            $provider->daily_price_4h = $data['daily_price_4h'] ?? null;
+            $provider->daily_price_2h = $data['daily_price_2h'] ?? null;
+
+            $provider->approval_status = ApprovalStatusEnum::PENDING->value;
+
             $provider->selfie_media_base64         = $data['selfie_base64'] ?? null;
             $provider->document_front_media_base64 = $data['document_front_base64'] ?? null;
             $provider->document_back_media_base64  = $data['document_back_base64'] ?? null;
+
             $provider->save();
             $provider->refresh();
 
@@ -209,7 +216,8 @@ class ProviderService
             $city = $cityQuery->first();
         }
 
-        $address                 = new Address;
+        $address = new Address;
+
         $address->source         = 'provider';
         $address->source_id      = $providerId;
         $address->zip_code       = $this->sanitizeDigits($data['zip_code'] ?? null);
@@ -221,6 +229,7 @@ class ProviderService
         $address->address_type   = $data['address_type'] ?? 'home';
         $address->state_id       = $state?->id;
         $address->city_id        = $city?->id;
+
         $address->save();
     }
 
@@ -241,7 +250,8 @@ class ProviderService
     private function createProviderWorkingDays(int $providerId, array $data): void
     {
         $workingDays = $data['working_days'] ?? [];
-        $seen        = [];
+
+        $seen = [];
 
         foreach ($workingDays as $workingDay) {
             $day    = (int) ($workingDay['day'] ?? -1);
@@ -252,6 +262,7 @@ class ProviderService
             }
 
             $uniqueKey = $day.'-'.$period;
+
             if (isset($seen[$uniqueKey])) {
                 continue;
             }

+ 1 - 0
app/Services/ReviewImprovementService.php

@@ -32,6 +32,7 @@ class ReviewImprovementService
         }
 
         $item = ReviewImprovement::create($data);
+
         $item->load(['improvementType']);
 
         return $item;

+ 11 - 3
app/Services/ReviewService.php

@@ -43,7 +43,9 @@ class ReviewService
     {
         try {
             DB::beginTransaction();
+
             $review = new Review;
+
             $review->fill($data);
             $review->save();
             $review->refresh();
@@ -53,11 +55,13 @@ class ReviewService
                     case 'client':
                         $schedule = Schedule::find($data['schedule_id']);
                         $provider = Provider::find($schedule->provider_id);
+
                         $provider->updateAverageRating((float) $data['stars']);
                         break;
                     case 'provider':
                         $schedule = Schedule::find($data['schedule_id']);
                         $client   = Client::find($schedule->client_id);
+
                         $client->updateAverageRating((float) $data['stars']);
                         break;
                 }
@@ -68,7 +72,8 @@ class ReviewService
             }
 
             if (! empty($data['block_provider'])) {
-                $schedule       = Schedule::find($data['schedule_id']);
+                $schedule = Schedule::find($data['schedule_id']);
+
                 $alreadyBlocked = ClientProviderBlock::where('client_id', $schedule->client_id)
                     ->where('provider_id', $schedule->provider_id)
                     ->whereNull('deleted_at')
@@ -83,7 +88,8 @@ class ReviewService
             }
 
             if (! empty($data['block_client'])) {
-                $schedule       = Schedule::find($data['schedule_id']);
+                $schedule = Schedule::find($data['schedule_id']);
+
                 $alreadyBlocked = ProviderClientBlock::where('provider_id', $schedule->provider_id)
                     ->where('client_id', $schedule->client_id)
                     ->whereNull('deleted_at')
@@ -98,7 +104,8 @@ class ReviewService
             }
 
             if (! empty($data['favorite_provider'])) {
-                $schedule         = Schedule::find($data['schedule_id']);
+                $schedule = Schedule::find($data['schedule_id']);
+
                 $alreadyFavorited = ClientFavoriteProvider::where('client_id', $schedule->client_id)
                     ->where('provider_id', $schedule->provider_id)
                     ->whereNull('deleted_at')
@@ -174,6 +181,7 @@ class ReviewService
     public function update(int $id, array $data): Review
     {
         $review = Review::findOrFail($id);
+
         $review->update($data);
 
         return $review->fresh();

+ 11 - 0
app/Services/ScheduleService.php

@@ -43,17 +43,22 @@ class ScheduleService
         try {
             DB::beginTransaction();
             $createdSchedules = [];
+
             foreach ($schedules as $schedule) {
                 $datasMerged = array_merge($baseData, $schedule);
+
                 $this->validateProviderAvailability($datasMerged, null);
+
                 $scheduleData = array_merge($datasMerged, [
                     'code' => str_pad(random_int(0, 9999), 4, '0', STR_PAD_LEFT),
                 ]);
+
                 $createdSchedules[] = Schedule::create($scheduleData);
             }
             DB::commit();
         } catch (\Exception $e) {
             DB::rollBack();
+
             throw new \Exception(__($e->getMessage()));
         }
 
@@ -73,6 +78,7 @@ class ScheduleService
 
         if (isset($data['date']) || isset($data['start_time']) || isset($data['provider_id'])) {
             $validationData = array_merge($schedule->toArray(), $data);
+
             $this->validateProviderAvailability($validationData, $id);
         }
 
@@ -84,6 +90,7 @@ class ScheduleService
     public function delete($id)
     {
         $schedule = Schedule::findOrFail($id);
+
         $schedule->delete();
 
         return $schedule;
@@ -300,9 +307,11 @@ class ScheduleService
     {
         try {
             DB::beginTransaction();
+
             $schedule = Schedule::findOrFail($id);
 
             $allowedStatuses = ['accepted', 'paid', 'pending'];
+
             if (! in_array($schedule->status, $allowedStatuses)) {
                 throw new \Exception("Cancelamento não permitido para o status atual: {$schedule->status}");
             }
@@ -320,7 +329,9 @@ class ScheduleService
             return $schedule->fresh(['client.user', 'provider.user', 'address']);
         } catch (\Exception $e) {
             DB::rollBack();
+
             Log::error('Erro ao cancelar agendamento: '.$e->getMessage());
+
             throw new \Exception('Não foi possível cancelar o agendamento.');
         }
     }

+ 2 - 1
app/Services/SearchService.php

@@ -15,7 +15,8 @@ class SearchService
 
     public function buscaPrestadores(?string $name = null, ?string $date = null): array
     {
-        $user    = Auth::user();
+        $user = Auth::user();
+
         $cliente = Client::where('user_id', $user->id)->first();
 
         $blockedProviderIds       = ScheduleBusinessRules::getBlockedProviderIdsForClient($cliente->id);

+ 6 - 1
app/Services/UserService.php

@@ -74,8 +74,10 @@ class UserService
             }
 
             if (array_key_exists('document', $data)) {
-                $client           = $user->client ?? Client::create(['user_id' => $user->id]);
+                $client = $user->client ?? Client::create(['user_id' => $user->id]);
+
                 $client->document = $data['document'];
+
                 $client->save();
             }
 
@@ -87,6 +89,7 @@ class UserService
 
             if ($user->registration_complete !== $registrationComplete) {
                 $user->registration_complete = $registrationComplete;
+
                 $user->save();
             }
 
@@ -95,7 +98,9 @@ class UserService
             return $user->fresh(['client']);
         } catch (\Exception $e) {
             DB::rollBack();
+
             Log::error('Erro ao atualizar perfil.', ['error' => $e->getMessage()]);
+
             throw $e;
         }
     }

+ 43 - 0
database/migrations/2026_05_20_000001_restrict_payment_methods_to_credit_card_and_pix.php

@@ -0,0 +1,43 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Support\Facades\DB;
+
+return new class extends Migration
+{
+    public function up(): void
+    {
+        $driver = DB::getDriverName();
+
+        if (DB::table('payments')->where('payment_method', 'boleto')->exists()) {
+            throw new RuntimeException('Remova ou migre pagamentos boleto antes de restringir payment_method para credit_card e pix.');
+        }
+
+        if ($driver === 'pgsql') {
+            DB::statement('ALTER TABLE payments DROP CONSTRAINT IF EXISTS payments_payment_method_check');
+            DB::statement("ALTER TABLE payments ADD CONSTRAINT payments_payment_method_check CHECK (payment_method IN ('credit_card', 'pix'))");
+
+            return;
+        }
+
+        if ($driver === 'mysql') {
+            DB::statement("ALTER TABLE payments MODIFY payment_method ENUM('credit_card', 'pix') NOT NULL");
+        }
+    }
+
+    public function down(): void
+    {
+        $driver = DB::getDriverName();
+
+        if ($driver === 'pgsql') {
+            DB::statement('ALTER TABLE payments DROP CONSTRAINT IF EXISTS payments_payment_method_check');
+            DB::statement("ALTER TABLE payments ADD CONSTRAINT payments_payment_method_check CHECK (payment_method IN ('credit_card', 'pix', 'boleto'))");
+
+            return;
+        }
+
+        if ($driver === 'mysql') {
+            DB::statement("ALTER TABLE payments MODIFY payment_method ENUM('credit_card', 'pix', 'boleto') NOT NULL");
+        }
+    }
+};

+ 0 - 21
pint.json

@@ -1,21 +0,0 @@
-{
-    "preset": "laravel",
-    "exclude": [
-        "routes",
-        "app/Services/Pagarme",
-        "database/migrations"
-    ],
-    "rules": {
-        "binary_operator_spaces": {
-            "default": "single_space",
-            "operators": {
-                "=>": "align_single_space_minimal",
-                "=": "align_single_space_minimal"
-            }
-        },
-        "ordered_imports": {
-            "sort_algorithm": "alpha"
-        },
-        "no_unused_imports": true
-    }
-}