SyncStudentChargeJob.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. <?php
  2. namespace App\Jobs;
  3. use App\Models\StudentContractInstallment;
  4. use App\Models\UnitPaymentAccount;
  5. use App\Services\Integrations\Asaas\AsaasClient;
  6. use App\Services\Integrations\Asaas\AsaasCustomerService;
  7. use Exception;
  8. use Illuminate\Bus\Queueable;
  9. use Illuminate\Contracts\Queue\ShouldQueue;
  10. use Illuminate\Foundation\Bus\Dispatchable;
  11. use Illuminate\Queue\InteractsWithQueue;
  12. use Illuminate\Queue\SerializesModels;
  13. use Illuminate\Support\Facades\Crypt;
  14. use Illuminate\Support\Facades\Log;
  15. class SyncStudentChargeJob implements ShouldQueue
  16. {
  17. use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
  18. public int $installmentId;
  19. public int $tries = 3;
  20. public function __construct(int $installmentId)
  21. {
  22. $this->installmentId = $installmentId;
  23. }
  24. public function handle(AsaasCustomerService $customerService): void
  25. {
  26. $installment = StudentContractInstallment::with(['student', 'unit', 'studentContract'])->find($this->installmentId);
  27. if (!$installment) {
  28. Log::warning("SyncStudentChargeJob: Parcela {$this->installmentId} não encontrada.");
  29. return;
  30. }
  31. if ($installment->asaas_id) {
  32. Log::info("SyncStudentChargeJob: Parcela {$installment->id} já possui asaas_id ({$installment->asaas_id}). Ignorando.");
  33. return;
  34. }
  35. if ($installment->value <= 0) {
  36. Log::info("SyncStudentChargeJob: Parcela {$installment->id} possui valor zerado. Ignorando Asaas.");
  37. return;
  38. }
  39. // Pegar a API Key da Subconta da Franquia
  40. $paymentAccount = UnitPaymentAccount::where('unit_id', $installment->unit_id)->first();
  41. if (!$paymentAccount || empty($paymentAccount->asaas_api_key)) {
  42. throw new Exception("Unidade {$installment->unit_id} não possui subconta Asaas ou API Key configurada.");
  43. }
  44. $apiKey = Crypt::decryptString($paymentAccount->asaas_api_key);
  45. // Garantir que o Aluno seja um Customer na subconta
  46. $asaasCustomerId = $customerService->ensureStudentCustomer($installment->student, $apiKey);
  47. $installment->update(['asaas_customer_id' => $asaasCustomerId]);
  48. // Criar a Cobrança no Asaas usando a apiKey da franquia
  49. $client = new AsaasClient($apiKey);
  50. $payload = [
  51. 'customer' => $asaasCustomerId,
  52. 'billingType' => 'UNDEFINED', // Deixa o aluno escolher PIX ou BOLETO
  53. 'value' => $installment->value,
  54. 'dueDate' => \Carbon\Carbon::parse($installment->due_date)->format('Y-m-d'),
  55. 'description' => "Mensalidade / {$installment->history} - {$installment->student->name}",
  56. // Prefixo crucial para o webhook identificar qual tabela procurar!
  57. 'externalReference' => "STU_{$installment->id}",
  58. 'postalService' => false,
  59. ];
  60. // Lidar com juros e multas padrão do Asaas ou do contrato se houver:
  61. // $payload['interest'] = ['value' => 1];
  62. // $payload['fine'] = ['value' => 2];
  63. try {
  64. $response = $client->post('/payments', $payload);
  65. // 4. Salvar as referências no nosso banco local
  66. $installment->update([
  67. 'asaas_id' => $response['id'],
  68. 'invoice_url' => $response['invoiceUrl'] ?? null,
  69. 'asaas_status' => $response['status'] ?? 'PENDING',
  70. 'billing_type' => $response['billingType'] ?? null,
  71. ]);
  72. Log::info("SyncStudentChargeJob: Cobrança criada no Asaas para a parcela {$installment->id} (Subconta da Unidade {$installment->unit_id}).");
  73. } catch (Exception $e) {
  74. Log::error("SyncStudentChargeJob: Erro ao criar cobrança no Asaas. " . $e->getMessage(), [
  75. 'installment_id' => $installment->id,
  76. 'payload' => $payload
  77. ]);
  78. throw $e;
  79. }
  80. }
  81. }