Browse Source

fix: corrige payload de payment

Gustavo Mantovani 3 weeks ago
parent
commit
0746db2766

+ 7 - 0
app/Http/Controllers/PaymentController.php

@@ -57,6 +57,13 @@ class PaymentController extends Controller
             ],
         );
 
+        if ($item->status === 'failed') {
+            return response()->json([
+                'payload' => new PaymentResource($item),
+                'message' => $item->failure_message ?: $this->paymentMessage($item->status),
+            ], 422);
+        }
+
         return $this->successResponse(
             payload: new PaymentResource($item),
             message: $this->paymentMessage($item->status),

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

@@ -92,12 +92,56 @@ class PagarmeCustomerService
 
     //
 
-    private function idempotencyKey(int $clientId): string
+    public function updateCustomer(string $customerId, int $clientId, array $data): void
     {
-        return "client-{$clientId}-customer";
+        $payload = $this->filterFilledRecursive([
+            'name'          => $data['name'] ?? null,
+            'email'         => $data['email'] ?? null,
+            'document'      => $this->sanitizeDigits($data['document'] ?? null),
+            'type'          => $data['type'] ?? null,
+            'document_type' => $data['document_type'] ?? null,
+            'code'          => $data['code'] ?? null,
+            'address'       => $data['address'] ?? null,
+            'phones'        => $data['phones'] ?? null,
+        ]);
+
+        if (empty($payload)) {
+            return;
+        }
+
+        $endpoint = $this->pagarmeUrl("/customers/{$customerId}");
+
+        PagarmeHttpLogger::logRequest('PATCH', $endpoint, $payload);
+
+        $request  = $this->pagarmeRequest($clientId, "customer-update-{$customerId}");
+        $response = $request->patch($endpoint, $payload);
+
+        if ($response->status() === 405) {
+            PagarmeHttpLogger::logRequest('PUT', $endpoint, $payload);
+            $response = $request->put($endpoint, $payload);
+        }
+
+        if ($response->failed()) {
+            Log::channel('pagarme')->error('Pagar.me customer update failed', [
+                'status'      => $response->status(),
+                'body'        => $response->json() ?? $response->body(),
+                'payload'     => $payload,
+                'customer_id' => $customerId,
+                'client_id'   => $clientId,
+            ]);
+
+            throw new \RuntimeException('Erro ao atualizar cliente no Pagar.me.');
+        }
+    }
+
+    //
+
+    private function idempotencyKey(int $clientId, string $suffix = 'customer'): string
+    {
+        return "client-{$clientId}-{$suffix}";
     }
 
-    private function pagarmeRequest(int $clientId)
+    private function pagarmeRequest(int $clientId, string $suffix = 'customer')
     {
         $secretKey = config('services.pagarme.secret_key');
 
@@ -109,7 +153,7 @@ class PagarmeCustomerService
 
         return Http::withBasicAuth($secretKey, '')
             ->withHeaders([
-                'Idempotency-Key' => $this->idempotencyKey($clientId),
+                'Idempotency-Key' => $this->idempotencyKey($clientId, $suffix),
                 'Content-Type'    => 'application/json',
                 'Accept'          => 'application/json',
             ]);
@@ -178,8 +222,6 @@ class PagarmeCustomerService
         ];
     }
 
-    //
-
     private function documentType(string $document): string
     {
         return strlen($document) === 14 ? 'CNPJ' : 'CPF';

+ 9 - 10
app/Services/Pagarme/PagarmePaymentService.php

@@ -81,13 +81,10 @@ class PagarmePaymentService
             throw new \InvalidArgumentException('payment_method deve ser credit_card ou pix.');
         }
 
-        $customerPayload = $options['customer_id'] ?? null;
+        $customerIdPayload     = $options['customer_id'] ?? null;
+        $customerObjectPayload = $this->filterFilledRecursive($customer);
 
-        if (! $this->filled($customerPayload)) {
-            $customerPayload = $this->filterFilledRecursive($customer);
-        }
-
-        if (empty($customerPayload)) {
+        if (! $this->filled($customerIdPayload) && empty($customerObjectPayload)) {
             throw new \InvalidArgumentException('customer ou customer_id e obrigatorio.');
         }
 
@@ -105,10 +102,12 @@ class PagarmePaymentService
             ], $options['metadata'] ?? []),
         ];
 
-        if (is_string($customerPayload)) {
-            $payload['customer_id'] = $customerPayload;
-        } else {
-            $payload['customer'] = $customerPayload;
+        if ($this->filled($customerIdPayload)) {
+            $payload['customer_id'] = $customerIdPayload;
+        }
+
+        if (! empty($customerObjectPayload)) {
+            $payload['customer'] = $customerObjectPayload;
         }
 
         if (! empty($options['channel'])) {

+ 9 - 1
app/Services/Pagarme/PagarmeRecipientService.php

@@ -27,7 +27,7 @@ class PagarmeRecipientService
         $occupation    = $data['professional_occupation'] ?? 'autonomo';
 
         $payload = $this->filterFilledRecursive([
-            'code' => preg_replace('/\D+/', '', $data['recipient_code']),
+            'code' => $this->buildRecipientCode($provider, $data),
 
             'register_information' => [
                 'name'                    => $data['recipient_name'],
@@ -230,6 +230,14 @@ class PagarmeRecipientService
         ]];
     }
 
+    private function buildRecipientCode(Provider $provider, array $data): string
+    {
+        $baseCode = preg_replace('/\D+/', '', (string) ($data['recipient_code'] ?? '')) ?: (string) $provider->id;
+
+        // Pagar.me exige external_id unico; usar code deterministico por provider evita colisao entre contas.
+        return Str::limit("provider-{$provider->id}-{$baseCode}", 52, '');
+    }
+
     private function extractAddressParts(array $data): array
     {
         $addressLine = trim((string) ($data['address'] ?? ''));

+ 37 - 18
app/Services/PaymentService.php

@@ -3,7 +3,6 @@
 namespace App\Services;
 
 use App\Models\Address;
-use App\Models\Client;
 use App\Models\ClientPaymentMethod;
 use App\Models\Payment;
 use App\Models\PaymentTransfer;
@@ -142,13 +141,12 @@ class PaymentService
             }
         }
 
-        $serviceAmount       = (float) $schedule->total_amount;
-        $platformFee         = round($serviceAmount * 0.11, 2);
-        $grossAmount         = round($serviceAmount + $platformFee, 2);
-        $items               = $this->buildOrderItems($schedule, $grossAmount);
-        $client              = Client::find($schedule->client_id);
-        $customerId          = $client?->external_customer_id;
-        $customer            = empty($customerId) ? $this->buildCustomerPayload($schedule, $options) : [];
+        $serviceAmount = (float) $schedule->total_amount;
+        $platformFee   = round($serviceAmount * 0.11, 2);
+        $grossAmount   = round($serviceAmount + $platformFee, 2);
+        $items         = $this->buildOrderItems($schedule, $grossAmount);
+        $this->ensureCustomerPhoneForPayment($schedule, $options);
+        $customer            = $this->buildCustomerPayload(schedule: $schedule, options: $options, requirePhone: true);
         $platformRecipientId = config('services.pagarme.platform_recipient_id');
 
         if ($platformFee > 0 && empty($platformRecipientId)) {
@@ -234,8 +232,7 @@ class PaymentService
                         'operation_type'       => 'auth_and_capture',
                     ] + $creditCardReference,
                     options: [
-                        'split'       => $split,
-                        'customer_id' => $customerId,
+                        'split' => $split,
                     ],
                 )
                 : $this->pagarmePaymentService->createOrderWithPix(
@@ -246,8 +243,7 @@ class PaymentService
                         'expires_at' => $payment->expires_at?->toISOString(),
                     ],
                     options: [
-                        'split'       => $split,
-                        'customer_id' => $customerId,
+                        'split' => $split,
                     ],
                 );
         } catch (\Throwable $e) {
@@ -284,8 +280,11 @@ class PaymentService
         ]];
     }
 
-    private function buildCustomerPayload(Schedule $schedule, array $options = []): array
-    {
+    private function buildCustomerPayload(
+        Schedule $schedule,
+        array $options = [],
+        bool $requirePhone = true
+    ): array {
         $client  = $schedule->client;
         $user    = $client->user()->first(['id', 'name', 'email', 'phone']);
         $address = Address::with(['city.state', 'state'])->find($schedule->address_id);
@@ -319,14 +318,19 @@ class PaymentService
             $address->district,
         ]));
 
-        foreach ([
+        $requiredFields = [
             'documento' => $document,
-            'telefone'  => $phone,
             'estado'    => $state,
             'cidade'    => $city,
             'cep'       => $zipCode,
             'endereco'  => $line1,
-        ] as $field => $value) {
+        ];
+
+        if ($requirePhone) {
+            $requiredFields['telefone'] = $phone;
+        }
+
+        foreach ($requiredFields as $field => $value) {
             if ($value === null || $value === '' || $value === []) {
                 throw new \InvalidArgumentException("Cliente precisa ter {$field} valido para criar pedido no Pagar.me.");
             }
@@ -349,7 +353,7 @@ class PaymentService
                 'line_2'   => $address->complement ?: $address->instructions,
             ],
 
-            'phones' => ['mobile_phone' => $phone],
+            'phones' => $phone ? ['mobile_phone' => $phone] : null,
         ];
     }
 
@@ -372,6 +376,21 @@ class PaymentService
         ];
     }
 
+    private function ensureCustomerPhoneForPayment(Schedule $schedule, array $options = []): void
+    {
+        $userPhone = $schedule->client?->user?->phone;
+        $phone     = $this->buildPhonePayload($userPhone)
+            ?: $this->buildPhonePayload($options['phone'] ?? null);
+
+        if ($phone) {
+            return;
+        }
+
+        throw new \InvalidArgumentException(
+            'Voce precisa cadastrar um numero de celular valido no seu perfil para concluir o pagamento.'
+        );
+    }
+
     private function digits(?string $value): string
     {
         return preg_replace('/\D+/', '', (string) $value) ?? '';

+ 3 - 2
pint.json

@@ -2,7 +2,8 @@
     "preset": "laravel",
     "exclude": [
         "database/migrations",
-        "routes"
+        "routes",
+        "app/Models"
     ],
     "rules": {
         "binary_operator_spaces": {
@@ -13,4 +14,4 @@
             }
         }
     }
-}
+}