gateway_card_id)) { return $paymentMethod->gateway_card_id; } if (empty($paymentMethod->token)) { throw new \InvalidArgumentException('Token do cartao e obrigatorio para salvar cartao no Pagar.me.'); } $paymentMethod->loadMissing('client.user'); $customerId = $this->pagarmeCustomerService->createCustomerForClient( $paymentMethod->client, [] ); if (empty($customerId)) { throw new \RuntimeException('Cliente precisa ter customer_id do Pagar.me para salvar cartao.'); } $payload = $this->filterFilledRecursive([ 'token' => $paymentMethod->token, 'label' => $paymentMethod->card_name, 'billing_address' => $this->buildBillingAddress($paymentMethod), ]); $endpoint = $this->pagarmeUrl("/customers/{$customerId}/cards"); PagarmeHttpLogger::logRequest('POST', $endpoint, $payload); $response = $this->pagarmeRequest($paymentMethod->id) ->post($endpoint, $payload); if ($response->failed()) { $responseBody = $response->json() ?? $response->body(); Log::channel('pagarme')->error('Pagar.me card creation failed', [ 'client_payment_method_id' => $paymentMethod->id, 'client_id' => $paymentMethod->client_id, 'customer_id' => $customerId, 'status' => $response->status(), 'body' => $responseBody, 'payload' => $payload, ]); throw new \RuntimeException($this->gatewayErrorMessage($responseBody)); } $cardData = $response->json(); $cardId = $cardData['id'] ?? null; if (empty($cardId)) { Log::channel('pagarme')->error('Pagar.me card creation returned empty id', [ 'client_payment_method_id' => $paymentMethod->id, 'client_id' => $paymentMethod->client_id, 'customer_id' => $customerId, 'response' => $cardData, ]); throw new \RuntimeException('Pagar.me card creation returned an empty id.'); } $paymentMethod->forceFill([ 'gateway_card_id' => $cardId, 'brand' => $paymentMethod->brand ?: ($cardData['brand'] ?? null), 'last_four_digits' => $paymentMethod->last_four_digits ?: ($cardData['last_four_digits'] ?? null), ])->save(); return $cardId; } // private function pagarmeRequest(int $paymentMethodId) { $secretKey = config('services.pagarme.secret_key'); if (empty($secretKey)) { Log::channel('pagarme')->error('PAGARME_SECRET_KEY is not configured.'); throw new \RuntimeException('PAGARME_SECRET_KEY is not configured.'); } return Http::withBasicAuth($secretKey, '') ->withHeaders([ 'Idempotency-Key' => "client-payment-method-{$paymentMethodId}-card", 'Content-Type' => 'application/json', 'Accept' => 'application/json', ]); } private function pagarmeUrl(string $path): string { return rtrim(config('services.pagarme.base_url'), '/').'/'.ltrim($path, '/'); } // private function buildBillingAddress(ClientPaymentMethod $paymentMethod): array { $address = Address::query() ->with(['city.state', 'state']) ->where('source', 'client') ->where('source_id', $paymentMethod->client_id) ->orderByDesc('is_primary') ->latest('id') ->first(); if (! $address) { throw new \InvalidArgumentException('Cliente precisa ter endereco para salvar cartao no Pagar.me.'); } $state = $address->state?->code ?? $address->city?->state?->code; $city = $address->city?->name; $line1 = implode(', ', array_filter([ $address->number ?: 'S/N', $address->address, $address->district, ])); foreach ([ 'estado' => $state, 'cidade' => $city, 'cep' => $this->digits($address->zip_code), 'endereco' => $line1, ] as $field => $value) { if ($value === null || $value === '') { throw new \InvalidArgumentException("Cliente precisa ter {$field} valido para salvar cartao no Pagar.me."); } } return [ 'line_1' => $line1, 'line_2' => $address->complement ?: $address->instructions, 'zip_code' => $this->digits($address->zip_code), 'city' => $city, 'state' => $state, 'country' => 'BR', ]; } private function digits(?string $value): string { return preg_replace('/\D+/', '', (string) $value) ?? ''; } 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 gatewayErrorMessage(mixed $responseBody): string { $message = is_array($responseBody) ? ($responseBody['message'] ?? null) : null; if ($message === 'Token not found.') { return 'Token do cartao nao encontrado no Pagar.me. Gere o token com a public key do mesmo ambiente da secret key.'; } return 'Erro ao salvar cartao no Pagar.me.'; } }