Bläddra i källkod

refactor: trait de format do pagarme

Gustavo Mantovani 1 vecka sedan
förälder
incheckning
91c7596120

+ 0 - 44
_ide_helper.php

@@ -22782,49 +22782,6 @@ namespace Illuminate\Support\Facades {
             }
     }
 
-namespace Kreait\Laravel\Firebase\Facades {
-    /**
-     * @method static AppCheck appCheck()
-     * @method static Auth auth()
-     * @method static Database database()
-     * @method static Firestore firestore()
-     * @method static Messaging messaging()
-     * @method static RemoteConfig remoteConfig()
-     * @method static Storage storage()
-     * @see FirebaseProjectManager
-     * @see FirebaseProject
-     */
-    class Firebase {
-        /**
-         * @static
-         */
-        public static function project($name = null)
-        {
-            /** @var \Kreait\Laravel\Firebase\FirebaseProjectManager $instance */
-            return $instance->project($name);
-        }
-
-        /**
-         * @static
-         */
-        public static function getDefaultProject()
-        {
-            /** @var \Kreait\Laravel\Firebase\FirebaseProjectManager $instance */
-            return $instance->getDefaultProject();
-        }
-
-        /**
-         * @static
-         */
-        public static function setDefaultProject($name)
-        {
-            /** @var \Kreait\Laravel\Firebase\FirebaseProjectManager $instance */
-            return $instance->setDefaultProject($name);
-        }
-
-            }
-    }
-
 namespace Illuminate\Http {
     /**
      */
@@ -27721,7 +27678,6 @@ namespace  {
     class Validator extends \Illuminate\Support\Facades\Validator {}
     class View extends \Illuminate\Support\Facades\View {}
     class Vite extends \Illuminate\Support\Facades\Vite {}
-    class Firebase extends \Kreait\Laravel\Firebase\Facades\Firebase {}
 }
 
 

+ 2 - 0
app/Data/Pagarme/Response/CardResponseData.php

@@ -2,6 +2,8 @@
 
 namespace App\Data\Pagarme\Response;
 
+use App\Data\Pagarme\PagarmeResponseData;
+
 final readonly class CardResponseData extends PagarmeResponseData
 {
     public function __construct(

+ 2 - 0
app/Data/Pagarme/Response/TransferResponseData.php

@@ -2,6 +2,8 @@
 
 namespace App\Data\Pagarme\Response;
 
+use App\Data\Pagarme\PagarmeResponseData;
+
 final readonly class TransferResponseData extends PagarmeResponseData
 {
     public function __construct(

+ 16 - 2
app/Models/Client.php

@@ -13,8 +13,6 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @property int $id
  * @property string|null $document
  * @property int $user_id
- * @property int|null $profile_media_id
- * @property-read \App\Models\Media|null $profileMedia
  * @property \Illuminate\Support\Carbon|null $created_at
  * @property \Illuminate\Support\Carbon|null $updated_at
  * @property \Illuminate\Support\Carbon|null $deleted_at
@@ -26,6 +24,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @property-read int|null $blocked_by_providers_count
  * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ClientProviderBlock> $blockedProviders
  * @property-read int|null $blocked_providers_count
+ * @property-read \App\Models\Media|null $profileMedia
  * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Schedule> $schedules
  * @property-read int|null $schedules_count
  * @property-read \App\Models\User $user
@@ -107,4 +106,19 @@ class Client extends Model
     {
         return $this->hasMany(Schedule::class);
     }
+
+    //
+
+    public function ensureGatewayCode(): string
+    {
+        if (! empty($this->external_customer_code)) {
+            return $this->external_customer_code;
+        }
+
+        $code = 'client-'.(string) \Illuminate\Support\Str::uuid();
+
+        $this->forceFill(['external_customer_code' => $code])->save();
+
+        return $code;
+    }
 }

+ 4 - 12
app/Models/DeviceToken.php

@@ -7,19 +7,11 @@ use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
 /**
- * @property int $id
- * @property int $user_id
- * @property string $token
- * @property string $platform
  * @property PushNotificationTargetEnum $app_type
- * @property bool $active
- * @property \Illuminate\Support\Carbon|null $created_at
- * @property \Illuminate\Support\Carbon|null $updated_at
- * @property-read \App\Models\User $user
- * @method static \Illuminate\Database\Eloquent\Builder<static>|DeviceToken whereAppType($value)
- * @method static \Illuminate\Database\Eloquent\Builder<static>|DeviceToken whereUserId($value)
- * @method static \Illuminate\Database\Eloquent\Builder<static>|DeviceToken whereToken($value)
- * @method static \Illuminate\Database\Eloquent\Builder<static>|DeviceToken whereActive($value)
+ * @property-read \App\Models\User|null $user
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|DeviceToken newModelQuery()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|DeviceToken newQuery()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|DeviceToken query()
  * @mixin \Eloquent
  */
 class DeviceToken extends Model

+ 10 - 0
app/Models/Notification.php

@@ -6,6 +6,16 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\SoftDeletes;
 
+/**
+ * @property-read \App\Models\User|null $user
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Notification newModelQuery()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Notification newQuery()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Notification onlyTrashed()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Notification query()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Notification withTrashed(bool $withTrashed = true)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Notification withoutTrashed()
+ * @mixin \Eloquent
+ */
 class Notification extends Model
 {
     use HasFactory, SoftDeletes;

+ 15 - 0
app/Models/Payment.php

@@ -169,4 +169,19 @@ class Payment extends Model
     {
         return $this->hasMany(Webhook::class);
     }
+
+    //
+
+    public function ensureGatewayCode(): string
+    {
+        if (! empty($this->gateway_code)) {
+            return $this->gateway_code;
+        }
+
+        $code = 'payment-'.(string) \Illuminate\Support\Str::uuid();
+
+        $this->forceFill(['gateway_code' => $code])->save();
+
+        return $code;
+    }
 }

+ 30 - 13
app/Models/Provider.php

@@ -15,24 +15,22 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @property string $document
  * @property string|null $rg
  * @property int $user_id
- * @property float|null $average_rating
+ * @property numeric|null $average_rating
  * @property int $total_services
- * @property string|null $birth_date
+ * @property \Illuminate\Support\Carbon|null $birth_date
  * @property bool $selfie_verified
  * @property bool $document_verified
- * @property string $approval_status
- * @property float|null $daily_price_8h
- * @property float|null $daily_price_6h
- * @property float|null $daily_price_4h
- * @property float|null $daily_price_2h
+ * @property numeric|null $daily_price_8h
+ * @property numeric|null $daily_price_6h
+ * @property numeric|null $daily_price_4h
+ * @property numeric|null $daily_price_2h
  * @property int|null $profile_media_id
- * @property int|null $document_front_media_id
- * @property int|null $document_back_media_id
- * @property-read \App\Models\Media|null $documentFrontMedia
- * @property-read \App\Models\Media|null $documentBackMedia
  * @property \Illuminate\Support\Carbon|null $created_at
  * @property \Illuminate\Support\Carbon|null $updated_at
  * @property \Illuminate\Support\Carbon|null $deleted_at
+ * @property string|null $selfie_media_base64
+ * @property string|null $document_front_media_base64
+ * @property string|null $document_back_media_base64
  * @property ApprovalStatusEnum $approval_status
  * @property string|null $recipient_id
  * @property string|null $recipient_name
@@ -52,8 +50,11 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @property-read int|null $blocked_by_clients_count
  * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProviderClientBlock> $blockedClients
  * @property-read int|null $blocked_clients_count
+ * @property-read \App\Models\Media|null $documentBackMedia
+ * @property-read \App\Models\Media|null $documentFrontMedia
  * @property-read \App\Models\Address|null $primaryAddress
  * @property-read \App\Models\Media|null $profileMedia
+ * @property-read \App\Models\User $user
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider newModelQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider newQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider onlyTrashed()
@@ -68,8 +69,8 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDailyPrice8h($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDeletedAt($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDocument($value)
- * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDocumentBackMediaId($value)
- * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDocumentFrontMediaId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDocumentBackMediaBase64($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDocumentFrontMediaBase64($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDocumentVerified($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereId($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereProfileMediaId($value)
@@ -86,6 +87,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereRecipientTransferSettings($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereRecipientType($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereRg($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereSelfieMediaBase64($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereSelfieVerified($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereTotalServices($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereUpdatedAt($value)
@@ -209,4 +211,19 @@ class Provider extends Model
       ->where("source", "provider")
       ->orderBy("is_primary", "desc");
   }
+
+  //
+
+  public function ensureGatewayCode(): string
+  {
+      if (! empty($this->recipient_code)) {
+          return $this->recipient_code;
+      }
+
+      $code = 'provider-'.(string) \Illuminate\Support\Str::uuid();
+
+      $this->forceFill(['recipient_code' => $code])->save();
+
+      return $code;
+  }
 }

+ 4 - 12
app/Models/PushNotificationLog.php

@@ -7,19 +7,11 @@ use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
 /**
- * @property int $id
- * @property string $label
- * @property int $user_id
  * @property PushNotificationTargetEnum $target
- * @property string $category
- * @property \Illuminate\Support\Carbon $sent_at
- * @property \Illuminate\Support\Carbon|null $created_at
- * @property \Illuminate\Support\Carbon|null $updated_at
- * @property-read \App\Models\User $user
- * @method static \Illuminate\Database\Eloquent\Builder<static>|PushNotificationLog whereLabel($value)
- * @method static \Illuminate\Database\Eloquent\Builder<static>|PushNotificationLog whereUserId($value)
- * @method static \Illuminate\Database\Eloquent\Builder<static>|PushNotificationLog whereTarget($value)
- * @method static \Illuminate\Database\Eloquent\Builder<static>|PushNotificationLog whereCategory($value)
+ * @property-read \App\Models\User|null $user
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|PushNotificationLog newModelQuery()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|PushNotificationLog newQuery()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|PushNotificationLog query()
  * @mixin \Eloquent
  */
 class PushNotificationLog extends Model

+ 2 - 2
app/Models/Review.php

@@ -19,12 +19,12 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @property \Illuminate\Support\Carbon|null $created_at
  * @property \Illuminate\Support\Carbon|null $updated_at
  * @property \Illuminate\Support\Carbon|null $deleted_at
- * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ReviewMedia> $reviewMedia
- * @property-read int|null $review_media_count
  * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ImprovementType> $improvements
  * @property-read int|null $improvements_count
  * @property-read \App\Models\Client|null $originClient
  * @property-read \App\Models\Provider|null $originProvider
+ * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ReviewMedia> $reviewMedia
+ * @property-read int|null $review_media_count
  * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ReviewImprovement> $reviewsImprovements
  * @property-read int|null $reviews_improvements_count
  * @property-read \App\Models\Schedule $schedule

+ 6 - 6
app/Models/ReviewMedia.php

@@ -6,12 +6,12 @@ use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
 /**
- * @property int $id
- * @property int $review_id
- * @property int $media_id
- * @property string $origin
- * @property-read \App\Models\Media $media
- * @property-read \App\Models\Review $review
+ * @property-read \App\Models\Media|null $media
+ * @property-read \App\Models\Review|null $review
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|ReviewMedia newModelQuery()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|ReviewMedia newQuery()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|ReviewMedia query()
+ * @mixin \Eloquent
  */
 class ReviewMedia extends Model
 {

+ 16 - 0
app/Models/Schedule.php

@@ -133,4 +133,20 @@ class Schedule extends Model
     {
         return $this->hasMany(Review::class);
     }
+
+    //
+
+    public function ensureCustomerPhone(?string $fallbackPhone = null): void
+    {
+        $phone  = $this->client?->user?->phone ?? $fallbackPhone;
+        $digits = preg_replace('/\D+/', '', (string) $phone) ?? '';
+
+        if (strlen($digits) >= 10) {
+            return;
+        }
+
+        throw new \InvalidArgumentException(
+            'Voce precisa cadastrar um numero de celular valido no seu perfil para concluir o pagamento.'
+        );
+    }
 }

+ 4 - 0
app/Models/User.php

@@ -27,11 +27,15 @@ use Laravel\Sanctum\HasApiTokens;
  * @property bool $validated_code
  * @property bool $registration_complete
  * @property-read \App\Models\Client|null $client
+ * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\DeviceToken> $deviceTokens
+ * @property-read int|null $device_tokens_count
  * @property-read \Illuminate\Notifications\DatabaseNotificationCollection<int, \Illuminate\Notifications\DatabaseNotification> $notifications
  * @property-read int|null $notifications_count
  * @property-read \Kalnoy\Nestedset\Collection<int, \App\Models\Permission> $permissions
  * @property-read int|null $permissions_count
  * @property-read \App\Models\Provider|null $provider
+ * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\PushNotificationLog> $pushNotificationLogs
+ * @property-read int|null $push_notification_logs_count
  * @property-read \Illuminate\Database\Eloquent\Collection<int, \Laravel\Sanctum\PersonalAccessToken> $tokens
  * @property-read int|null $tokens_count
  * @method static \Database\Factories\UserFactory factory($count = null, $state = [])

+ 93 - 0
app/Services/Pagarme/Concerns/FormatsPagarmeData.php

@@ -0,0 +1,93 @@
+<?php
+
+namespace App\Services\Pagarme\Concerns;
+
+use Carbon\Carbon;
+use Illuminate\Support\Str;
+
+trait FormatsPagarmeData
+{
+    protected function digits(?string $value): string
+    {
+        return preg_replace('/\D+/', '', (string) $value) ?? '';
+    }
+
+    protected function extractAddressParts(array $data): array
+    {
+        $addressLine = trim((string) ($data['address'] ?? ''));
+
+        $segments = array_map('trim', explode(',', $addressLine));
+
+        $streetSegment = $segments[0] ?? '';
+
+        if (($data['number'] ?? null) === null) {
+            preg_match('/^(\d+)/', $streetSegment, $matches);
+        }
+
+        return [
+            'street_number'   => (string) ($data['number'] ?? $matches[1] ?? 'S/N'),
+            'neighborhood'    => (string) ($data['district'] ?? $segments[1] ?? 'N/A'),
+            'reference_point' => (string) ($data['reference_point'] ?? 'N/A'),
+            'complementary'   => (string) ($data['complement'] ?? 'N/A'),
+        ];
+    }
+
+    protected function formatBirthdate(mixed $birthdate): ?string
+    {
+        if ($birthdate === null || $birthdate === '') {
+            return null;
+        }
+
+        if ($birthdate instanceof \DateTimeInterface) {
+            return Carbon::instance($birthdate)->format('d/m/Y');
+        }
+
+        $birthdate = trim((string) $birthdate);
+
+        if (preg_match('/^\d{2}\/\d{2}\/\d{4}$/', $birthdate) === 1) {
+            return $birthdate;
+        }
+
+        if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $birthdate) === 1) {
+            return Carbon::createFromFormat('Y-m-d', $birthdate)->format('d/m/Y');
+        }
+
+        return Carbon::parse($birthdate)->format('d/m/Y');
+    }
+
+    protected function normalizeHolderName(string $holderName): string
+    {
+        $holderName = trim(preg_replace('/\s+/', ' ', $holderName) ?? '');
+
+        if (Str::length($holderName) < 30) {
+            return $holderName;
+        }
+
+        $parts = explode(' ', $holderName);
+
+        if (count($parts) >= 3) {
+            $firstName = array_shift($parts);
+
+            $lastName = array_pop($parts);
+
+            $initials = array_map(
+                static fn (string $part): string => Str::upper(Str::substr($part, 0, 1)),
+                $parts
+            );
+
+            $abbreviated = trim($firstName.' '.implode(' ', $initials).' '.$lastName);
+
+            if (Str::length($abbreviated) < 30) {
+                return $abbreviated;
+            }
+
+            $firstAndLast = trim($firstName.' '.$lastName);
+
+            if (Str::length($firstAndLast) < 30) {
+                return $firstAndLast;
+            }
+        }
+
+        return Str::limit($holderName, 29, '');
+    }
+}

+ 3 - 6
app/Services/Pagarme/Concerns/SendsPagarmeRequests.php

@@ -2,6 +2,7 @@
 
 namespace App\Services\Pagarme\Concerns;
 
+use App\Data\Pagarme\PagarmeData;
 use Illuminate\Support\Facades\Http;
 use Illuminate\Support\Facades\Log;
 use Throwable;
@@ -9,13 +10,9 @@ use Throwable;
 trait SendsPagarmeRequests
 {
     protected function pagarmeRequest(
-        string $method,
-        string $path,
-        string $idempotencyKey,
-        string $errorMessage,
-        array|Data $payload,
+        string $method, string $path, string $idempotencyKey, string $errorMessage, array|PagarmeData $payload,
     ): array {
-        $payload = $payload instanceof Data ? $payload->toArray() : $payload;
+        $payload = $payload instanceof PagarmeData ? $payload->toArray() : $payload;
 
         $endpoint = $this->pagarmeUrl($path);
 

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

@@ -8,11 +8,12 @@ use App\Data\Pagarme\Request\CustomerRequestData\CustomerPhonesRequestData\Custo
 use App\Data\Pagarme\Request\CustomerRequestData\CustomerRequestData;
 use App\Data\Pagarme\Response\CustomerResponseData\CustomerResponseData;
 use App\Models\Client;
+use App\Services\Pagarme\Concerns\FormatsPagarmeData;
 use App\Services\Pagarme\Concerns\SendsPagarmeRequests;
-use Illuminate\Support\Str;
 
 class PagarmeCustomerService
 {
+    use FormatsPagarmeData;
     use SendsPagarmeRequests;
 
     public function createCustomerForClient(Client $client, array $data): ?string
@@ -25,9 +26,9 @@ class PagarmeCustomerService
 
         $name = $client->user?->name ?? $data['name'] ?? 'Cliente';
         $email = $client->user?->email ?? $data['email'] ?? null;
-        $code = $this->ensureCustomerCode($client);
+        $code = $client->ensureGatewayCode();
 
-        $document = $this->onlyDigits($client->document ?? $data['document'] ?? null);
+        $document = $this->digits($client->document ?? $data['document'] ?? null);
         $documentLen = strlen($document);
 
         $address = $this->buildAddressData($data);
@@ -75,7 +76,7 @@ class PagarmeCustomerService
         return new CustomerAddressRequestData(
             line1: implode(', ', $line1Parts),
             line2: (string) ($data['complement'] ?? $data['instructions'] ?? ''),
-            zipCode: $this->onlyDigits($data['zip_code'] ?? null),
+            zipCode: $this->digits($data['zip_code'] ?? null),
             city: (string) ($data['city'] ?? ''),
             state: (string) ($data['state'] ?? ''),
             country: (string) ($data['country'] ?? 'BR'),
@@ -84,7 +85,7 @@ class PagarmeCustomerService
 
     private function buildPhones(?string $phone): CustomerPhonesRequestData
     {
-        $digits = $this->onlyDigits($phone);
+        $digits = $this->digits($phone);
 
         if ($digits === '') {
             return new CustomerPhonesRequestData;
@@ -107,28 +108,10 @@ class PagarmeCustomerService
         );
     }
 
-    private function onlyDigits(?string $value): string
-    {
-        return preg_replace('/\D+/', '', (string) $value) ?? '';
-    }
-
     // evita criacao duplicada de customer
 
     private function idempotencyKey(int $clientId, string $suffix = 'customer'): string
     {
         return "client-{$clientId}-{$suffix}";
     }
-
-    private function ensureCustomerCode(Client $client): string
-    {
-        if (! empty($client->external_customer_code)) {
-            return $client->external_customer_code;
-        }
-
-        $code = 'client-'.(string) Str::uuid();
-
-        $client->forceFill(['external_customer_code' => $code])->save();
-
-        return $code;
-    }
 }

+ 17 - 47
app/Services/Pagarme/PagarmePaymentService.php

@@ -21,11 +21,13 @@ use App\Models\Address;
 use App\Models\Payment;
 use App\Models\PaymentSplit;
 use App\Models\Schedule;
+use App\Services\Pagarme\Concerns\FormatsPagarmeData;
 use App\Services\Pagarme\Concerns\SendsPagarmeRequests;
 use Illuminate\Support\Str;
 
 class PagarmePaymentService
 {
+    use FormatsPagarmeData;
     use SendsPagarmeRequests;
 
     public function processPayment(
@@ -136,7 +138,7 @@ class PagarmePaymentService
         ], $options['metadata'] ?? []);
 
         $requestData = new OrderRequestData(
-            code: $this->ensurePaymentCode($payment),
+            code: $payment->ensureGatewayCode(),
             items: $items,
             payments: [$paymentMethod],
             metadata: $metadata,
@@ -204,33 +206,6 @@ class PagarmePaymentService
 
     //
 
-    public function ensureCustomerPhone(Schedule $schedule, array $options): void
-    {
-        $phone = $this->buildPhonePayload($schedule->client?->user?->phone)
-            ?: $this->buildPhonePayload($options['phone'] ?? null);
-
-        if (! $phone) {
-            throw new \InvalidArgumentException(
-                'Voce precisa cadastrar um numero de celular valido no seu perfil para concluir o pagamento.'
-            );
-        }
-    }
-
-    //
-
-    private function buildOrderItems(Schedule $schedule, float $grossAmount): array
-    {
-        $description = $schedule->customSchedule?->serviceType?->description
-            ?? "Servico {$schedule->id}";
-
-        return [new OrderItemData(
-            code: "schedule-{$schedule->id}",
-            amount: OrderRequestData::amountInCents($grossAmount),
-            quantity: 1,
-            description: $description,
-        )];
-    }
-
     private function buildCustomer(Schedule $schedule, array $options = []): CustomerRequestData
     {
         $client = $schedule->client;
@@ -312,6 +287,19 @@ class PagarmePaymentService
         );
     }
 
+    private function buildOrderItems(Schedule $schedule, float $grossAmount): array
+    {
+        $description = $schedule->customSchedule?->serviceType?->description
+            ?? "Servico {$schedule->id}";
+
+        return [new OrderItemData(
+            code: "schedule-{$schedule->id}",
+            amount: OrderRequestData::amountInCents($grossAmount),
+            quantity: 1,
+            description: $description,
+        )];
+    }
+
     private function buildPhonePayload(?string $phone): ?array
     {
         $digits = $this->digits($phone);
@@ -359,28 +347,10 @@ class PagarmePaymentService
         return $split;
     }
 
-    private function digits(?string $value): string
-    {
-        return preg_replace('/\D+/', '', (string) $value) ?? '';
-    }
-
-    //
+    // evita criacao duplicada de payment
 
     private function idempotencyKey(Payment $payment): string
     {
         return "payment-{$payment->id}-schedule-{$payment->schedule_id}";
     }
-
-    private function ensurePaymentCode(Payment $payment): string
-    {
-        if (! empty($payment->gateway_code)) {
-            return $payment->gateway_code;
-        }
-
-        $code = 'payment-'.(string) Str::uuid();
-
-        $payment->forceFill(['gateway_code' => $code])->save();
-
-        return $code;
-    }
 }

+ 8 - 102
app/Services/Pagarme/PagarmeRecipientService.php

@@ -13,12 +13,12 @@ use App\Data\Pagarme\Request\RecipientRequestData\RecipientRequestData;
 use App\Data\Pagarme\Request\RecipientRequestData\RecipientTransferSettingsData;
 use App\Data\Pagarme\Response\RecipientResponseData\RecipientResponseData;
 use App\Models\Provider;
+use App\Services\Pagarme\Concerns\FormatsPagarmeData;
 use App\Services\Pagarme\Concerns\SendsPagarmeRequests;
-use Carbon\Carbon;
-use Illuminate\Support\Str;
 
 class PagarmeRecipientService
 {
+    use FormatsPagarmeData;
     use SendsPagarmeRequests;
 
     public function createRecipientForProvider(Provider $provider, array $data): string
@@ -29,14 +29,14 @@ class PagarmeRecipientService
 
         $metadata = $data['recipient_metadata'] ?? [];
         $paymentMode = $data['recipient_payment_mode'];
-        $recipientCode = $this->ensureRecipientCode($provider);
+        $recipientCode = $provider->ensureGatewayCode();
 
         $addressParts = $this->extractAddressParts($data);
 
         $registerInformation = new RecipientRegisterInformationData(
             name: $data['recipient_name'],
             email: $data['recipient_email'],
-            document: $this->onlyDigits($data['recipient_document'] ?? null),
+            document: $this->digits($data['recipient_document'] ?? null),
             type: $data['recipient_type'] ?? 'individual',
             birthdate: $this->formatBirthdate($data['birth_date'] ?? null),
             monthlyIncome: isset($data['monthly_income']) ? (int) $data['monthly_income'] : 1000,
@@ -53,7 +53,7 @@ class PagarmeRecipientService
                 neighborhood: $addressParts['neighborhood'],
                 city: $data['city'] ?? null,
                 state: $data['state'] ?? null,
-                zipCode: $this->onlyDigits($data['zip_code'] ?? null),
+                zipCode: $this->digits($data['zip_code'] ?? null),
                 referencePoint: $addressParts['reference_point'],
             ),
         );
@@ -125,7 +125,7 @@ class PagarmeRecipientService
         $payload = new BankAccountUpdateRequestData(
             holderName: $this->normalizeHolderName($bankAccountData['holder_name']),
             holderType: $bankAccountData['holder_type'],
-            holderDocument: $this->onlyDigits($bankAccountData['holder_document']),
+            holderDocument: $this->digits($bankAccountData['holder_document']),
             bank: $bankAccountData['bank'],
             branchNumber: $bankAccountData['branch_number'],
             branchCheckDigit: $bankAccountData['branch_check_digit'] ?? null,
@@ -158,7 +158,7 @@ class PagarmeRecipientService
         return new RecipientBankAccountData(
             holderName: $this->normalizeHolderName($data['holder_name']),
             holderType: $data['holder_type'],
-            holderDocument: $this->onlyDigits($data['holder_document']),
+            holderDocument: $this->digits($data['holder_document']),
             bank: $data['bank'],
             branchNumber: $data['branch_number'],
             branchCheckDigit: $data['branch_check_digit'] ?? null,
@@ -170,7 +170,7 @@ class PagarmeRecipientService
 
     private function buildRecipientPhone(?string $phone): RecipientPhoneData
     {
-        $digits = $this->onlyDigits($phone);
+        $digits = $this->digits($phone);
 
         if (strlen($digits) < 10) {
             return new RecipientPhoneData(
@@ -191,104 +191,10 @@ class PagarmeRecipientService
         );
     }
 
-    private function extractAddressParts(array $data): array
-    {
-        $addressLine = trim((string) ($data['address'] ?? ''));
-        $segments = array_map('trim', explode(',', $addressLine));
-        $streetSegment = $segments[0] ?? '';
-
-        if (($data['number'] ?? null) === null) {
-            preg_match('/^(\d+)/', $streetSegment, $matches);
-        }
-
-        return [
-            'street_number'   => (string) ($data['number'] ?? $matches[1] ?? 'S/N'),
-            'neighborhood'    => (string) ($data['district'] ?? $segments[1] ?? 'N/A'),
-            'reference_point' => (string) ($data['reference_point'] ?? 'N/A'),
-            'complementary'   => (string) ($data['complement'] ?? 'N/A'),
-        ];
-    }
-
-    private function formatBirthdate(mixed $birthdate): ?string
-    {
-        if ($birthdate === null || $birthdate === '') {
-            return null;
-        }
-
-        if ($birthdate instanceof \DateTimeInterface) {
-            return Carbon::instance($birthdate)->format('d/m/Y');
-        }
-
-        $birthdate = trim((string) $birthdate);
-
-        if (preg_match('/^\d{2}\/\d{2}\/\d{4}$/', $birthdate) === 1) {
-            return $birthdate;
-        }
-
-        if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $birthdate) === 1) {
-            return Carbon::createFromFormat('Y-m-d', $birthdate)->format('d/m/Y');
-        }
-
-        return Carbon::parse($birthdate)->format('d/m/Y');
-    }
-
-    private function normalizeHolderName(string $holderName): string
-    {
-        $holderName = trim(preg_replace('/\s+/', ' ', $holderName) ?? '');
-
-        if (Str::length($holderName) < 30) {
-            return $holderName;
-        }
-
-        $parts = explode(' ', $holderName);
-
-        if (count($parts) >= 3) {
-            $firstName = array_shift($parts);
-            $lastName = array_pop($parts);
-
-            $initials = array_map(
-                static fn (string $part): string => Str::upper(Str::substr($part, 0, 1)),
-                $parts
-            );
-
-            $abbreviated = trim($firstName.' '.implode(' ', $initials).' '.$lastName);
-
-            if (Str::length($abbreviated) < 30) {
-                return $abbreviated;
-            }
-
-            $firstAndLast = trim($firstName.' '.$lastName);
-
-            if (Str::length($firstAndLast) < 30) {
-                return $firstAndLast;
-            }
-        }
-
-        return Str::limit($holderName, 29, '');
-    }
-
-    private function onlyDigits(?string $value): string
-    {
-        return preg_replace('/\D+/', '', (string) $value) ?? '';
-    }
-
     // evita criacao duplica de recipient
 
     private function idempotencyKey(int $providerId, string $suffix = 'recipient'): string
     {
         return "provider-{$providerId}-{$suffix}";
     }
-
-    private function ensureRecipientCode(Provider $provider): string
-    {
-        if (! empty($provider->recipient_code)) {
-            return $provider->recipient_code;
-        }
-
-        $recipientCode = 'provider-'.(string) Str::uuid();
-
-        $provider->forceFill(['recipient_code' => $recipientCode])->save();
-
-        return $recipientCode;
-    }
 }

+ 29 - 27
app/Services/Pagarme/PagarmeTransferService.php

@@ -52,37 +52,18 @@ class PagarmeTransferService
 
     //
 
-    private function shouldMockTransferRequest(): bool
+    private function mockTransferId(string $idempotencyKey): string
     {
-        return app()->environment('local', 'development');
+        return (string) abs(crc32($idempotencyKey));
     }
 
-    private function mockTransferResponse(int $amountInCents, string $recipientId, string $idempotencyKey): TransferResponseData
+    private function shouldMockTransferRequest(): bool
     {
-        $provider = Provider::query()->where('recipient_id', $recipientId)->first();
-        $createdAt = Carbon::now();
-
-        return new TransferResponseData(
-            id: $this->mockTransferId($idempotencyKey),
-            amount: $amountInCents,
-            type: 'credito_em_conta',
-            status: 'pending_transfer',
-            fee: 0,
-            fundingDate: null,
-            fundingEstimatedDate: $createdAt->copy()->addWeekday()->toISOString(),
-            bankAccount: $provider?->recipient_default_bank_account,
-            bankResponse: null,
-            createdAt: $createdAt->toISOString(),
-            metadata: [
-                'mocked'          => true,
-                'environment'     => app()->environment(),
-                'recipient_id'    => $recipientId,
-                'idempotency_key' => $idempotencyKey,
-                'provider_id'     => $provider?->id,
-            ],
-        );
+        return app()->environment('local', 'development');
     }
 
+    //
+
     private function mockTransferLookupResponse(string $transferId): TransferResponseData
     {
         $withdrawal = ProviderWithdrawal::query()->where('transfer_id', $transferId)->first();
@@ -111,8 +92,29 @@ class PagarmeTransferService
         );
     }
 
-    private function mockTransferId(string $idempotencyKey): string
+    private function mockTransferResponse(int $amountInCents, string $recipientId, string $idempotencyKey): TransferResponseData
     {
-        return (string) abs(crc32($idempotencyKey));
+        $provider = Provider::query()->where('recipient_id', $recipientId)->first();
+        $createdAt = Carbon::now();
+
+        return new TransferResponseData(
+            id: $this->mockTransferId($idempotencyKey),
+            amount: $amountInCents,
+            type: 'credito_em_conta',
+            status: 'pending_transfer',
+            fee: 0,
+            fundingDate: null,
+            fundingEstimatedDate: $createdAt->copy()->addWeekday()->toISOString(),
+            bankAccount: $provider?->recipient_default_bank_account,
+            bankResponse: null,
+            createdAt: $createdAt->toISOString(),
+            metadata: [
+                'mocked'          => true,
+                'environment'     => app()->environment(),
+                'recipient_id'    => $recipientId,
+                'idempotency_key' => $idempotencyKey,
+                'provider_id'     => $provider?->id,
+            ],
+        );
     }
 }

+ 1 - 1
app/Services/PaymentService.php

@@ -198,7 +198,7 @@ class PaymentService
             ],
         ]);
 
-        $this->pagarmePaymentService->ensureCustomerPhone($schedule, $options);
+        $schedule->ensureCustomerPhone($options['phone'] ?? null);
 
         try {
             $orderResponse = $this->pagarmePaymentService->processPayment(