orderBy('created_at', 'desc') ->get(); } public function findById(int $id): ?Payment { return Payment::find($id); } public function create(array $data): Payment { return Payment::create($data); } public function update(int $id, array $data): ?Payment { $model = $this->findById($id); if (! $model) { return null; } $model->update($data); return $model->fresh(); } public function delete(int $id): bool { $model = $this->findById($id); if (! $model) { return false; } return $model->delete(); } 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> */ 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 */ 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|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) ?? ''; } }