|
@@ -78,213 +78,6 @@ class PaymentService
|
|
|
return $this->pagarmePaymentService->platformFeeRates();
|
|
return $this->pagarmePaymentService->platformFeeRates();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- //
|
|
|
|
|
-
|
|
|
|
|
- public function paySchedule(
|
|
|
|
|
- int $scheduleId,
|
|
|
|
|
- string $paymentMethod,
|
|
|
|
|
- ?int $clientPaymentMethodId = null,
|
|
|
|
|
- array $options = [],
|
|
|
|
|
- ): Payment {
|
|
|
|
|
- $schedule = Schedule::query()
|
|
|
|
|
- ->with(['client', 'provider', 'customSchedule.serviceType'])
|
|
|
|
|
- ->findOrFail($scheduleId);
|
|
|
|
|
-
|
|
|
|
|
- if ($schedule->client?->user_id !== (int) Auth::id()) {
|
|
|
|
|
- throw new AuthorizationException;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return $this->processAcceptedSchedulePayment(
|
|
|
|
|
- schedule: $schedule,
|
|
|
|
|
- paymentMethod: $paymentMethod,
|
|
|
|
|
- clientPaymentMethodId: $clientPaymentMethodId,
|
|
|
|
|
-
|
|
|
|
|
- options: $options,
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private function processAcceptedSchedulePayment(
|
|
|
|
|
- Schedule $schedule,
|
|
|
|
|
- string $paymentMethod,
|
|
|
|
|
- ?int $clientPaymentMethodId = null,
|
|
|
|
|
- array $options = [],
|
|
|
|
|
- ): Payment {
|
|
|
|
|
- $schedule->loadMissing(['client', 'provider', 'customSchedule.serviceType']);
|
|
|
|
|
-
|
|
|
|
|
- if ($schedule->status !== 'accepted') {
|
|
|
|
|
- throw new PaymentException;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (! in_array($paymentMethod, ['credit_card', 'pix'], true)) {
|
|
|
|
|
- throw new PaymentException;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (! $schedule->provider_id || ! $schedule->provider) {
|
|
|
|
|
- throw new PaymentException;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if ((float) $schedule->total_amount <= 0) {
|
|
|
|
|
- throw new PaymentException;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (empty($schedule->provider->recipient_id)) {
|
|
|
|
|
- throw new PaymentException;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- $this->ensureScheduleCanBePaidIndividually($schedule);
|
|
|
|
|
-
|
|
|
|
|
- $existingPayment = Payment::query()
|
|
|
|
|
- ->where('schedule_id', $schedule->id)
|
|
|
|
|
- ->whereIn('status', [
|
|
|
|
|
- PaymentStatusEnum::PENDING->value,
|
|
|
|
|
- PaymentStatusEnum::PROCESSING->value,
|
|
|
|
|
- PaymentStatusEnum::AUTHORIZED->value,
|
|
|
|
|
- PaymentStatusEnum::PAID->value,
|
|
|
|
|
- ])
|
|
|
|
|
- ->latest('id')
|
|
|
|
|
- ->first();
|
|
|
|
|
-
|
|
|
|
|
- if ($existingPayment) {
|
|
|
|
|
- if ($this->isIncompleteGatewayPayment($existingPayment)) {
|
|
|
|
|
- $existingPayment->forceFill([
|
|
|
|
|
- 'status' => PaymentStatusEnum::FAILED,
|
|
|
|
|
- 'failed_at' => now(),
|
|
|
|
|
- 'failure_message' => 'Pagamento pendente sem retorno do gateway.',
|
|
|
|
|
- ])->save();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- elseif ($this->isExpiredPixPayment($existingPayment)) {
|
|
|
|
|
- $existingPayment->forceFill([
|
|
|
|
|
- 'status' => PaymentStatusEnum::FAILED,
|
|
|
|
|
- 'failed_at' => now(),
|
|
|
|
|
- 'failure_message' => 'Pagamento Pix expirado.',
|
|
|
|
|
- ])->save();
|
|
|
|
|
-
|
|
|
|
|
- PaymentSplit::query()
|
|
|
|
|
- ->where('payment_id', $existingPayment->id)
|
|
|
|
|
- ->update(['status' => PaymentSplitStatusEnum::FAILED]);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- else {
|
|
|
|
|
- if ($existingPayment->payment_method !== $paymentMethod && $existingPayment->status !== PaymentStatusEnum::PAID) {
|
|
|
|
|
- throw new PaymentException;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- $this->syncScheduleStatusAfterPayment($schedule, $existingPayment);
|
|
|
|
|
-
|
|
|
|
|
- return $existingPayment;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- $clientPaymentMethod = null;
|
|
|
|
|
- $cardId = null;
|
|
|
|
|
-
|
|
|
|
|
- if ($paymentMethod === 'credit_card') {
|
|
|
|
|
- if (! $clientPaymentMethodId && empty(data_get($options, 'card_id'))) {
|
|
|
|
|
- throw new PaymentException;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if ($clientPaymentMethodId) {
|
|
|
|
|
- $clientPaymentMethod = ClientPaymentMethod::query()
|
|
|
|
|
- ->where('client_id', $schedule->client_id)
|
|
|
|
|
- ->where('id', $clientPaymentMethodId)
|
|
|
|
|
- ->where('is_active', true)
|
|
|
|
|
- ->first();
|
|
|
|
|
-
|
|
|
|
|
- if (! $clientPaymentMethod) {
|
|
|
|
|
- throw new PaymentException;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- $cardId = data_get($options, 'card_id', $clientPaymentMethod?->gateway_card_id);
|
|
|
|
|
-
|
|
|
|
|
- if (empty($cardId)) {
|
|
|
|
|
- throw new PaymentException;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- $serviceAmount = (float) $schedule->total_amount;
|
|
|
|
|
-
|
|
|
|
|
- $amounts = $this->pagarmePaymentService->calculatePaymentAmounts(
|
|
|
|
|
- serviceAmount: $serviceAmount,
|
|
|
|
|
- paymentMethod: $paymentMethod,
|
|
|
|
|
- schedule: $schedule,
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- $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) : null,
|
|
|
|
|
- 'gateway_provider' => 'pagarme',
|
|
|
|
|
- 'gateway_code' => 'payment-'.(string) Str::uuid(),
|
|
|
|
|
- 'payment_method' => $paymentMethod,
|
|
|
|
|
- 'status' => PaymentStatusEnum::PENDING,
|
|
|
|
|
- 'gross_amount' => data_get($amounts, 'gross_amount'),
|
|
|
|
|
- 'gateway_fee_amount' => 0,
|
|
|
|
|
- 'platform_fee_amount' => data_get($amounts, 'platform_fee_amount'),
|
|
|
|
|
- 'net_amount' => data_get($amounts, 'gross_amount'),
|
|
|
|
|
- 'currency' => 'BRL',
|
|
|
|
|
- 'installments' => 1,
|
|
|
|
|
- 'expires_at' => $paymentMethod === 'pix' ? Carbon::now()->addMinutes(30) : null,
|
|
|
|
|
-
|
|
|
|
|
- 'metadata' => [
|
|
|
|
|
- 'service_amount' => number_format(data_get($amounts, 'service_amount'), 2, '.', ''),
|
|
|
|
|
- 'platform_fee' => number_format(data_get($amounts, 'platform_fee_amount'), 2, '.', ''),
|
|
|
|
|
- ],
|
|
|
|
|
- ]);
|
|
|
|
|
-
|
|
|
|
|
- PaymentSplit::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' => PaymentSplitStatusEnum::PENDING,
|
|
|
|
|
- 'gross_amount' => $serviceAmount,
|
|
|
|
|
- 'gateway_fee_amount' => 0,
|
|
|
|
|
- 'net_amount' => $serviceAmount,
|
|
|
|
|
-
|
|
|
|
|
- 'metadata' => [
|
|
|
|
|
- 'schedule_id' => (string) $schedule->id,
|
|
|
|
|
- ],
|
|
|
|
|
- ]);
|
|
|
|
|
-
|
|
|
|
|
- $schedule->ensureCustomerPhone(data_get($options, 'phone'));
|
|
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- $orderResponse = $this->pagarmePaymentService->processPayment(
|
|
|
|
|
- payment: $payment,
|
|
|
|
|
- schedule: $schedule,
|
|
|
|
|
- paymentMethod: $paymentMethod,
|
|
|
|
|
- cardId: $cardId,
|
|
|
|
|
- options: $options,
|
|
|
|
|
- );
|
|
|
|
|
- } catch (\Throwable $e) {
|
|
|
|
|
- $payment->forceFill([
|
|
|
|
|
- 'status' => PaymentStatusEnum::FAILED,
|
|
|
|
|
- 'failed_at' => now(),
|
|
|
|
|
- 'failure_message' => $e->getMessage(),
|
|
|
|
|
- ])->save();
|
|
|
|
|
-
|
|
|
|
|
- PaymentSplit::query()
|
|
|
|
|
- ->where('payment_id', $payment->id)
|
|
|
|
|
- ->update(['status' => PaymentSplitStatusEnum::FAILED]);
|
|
|
|
|
-
|
|
|
|
|
- throw $e;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- $payment = $this->pagarmePaymentService->applyGatewayResponseToPayment($payment, $orderResponse);
|
|
|
|
|
-
|
|
|
|
|
- $this->syncScheduleStatusAfterPayment($schedule, $payment);
|
|
|
|
|
-
|
|
|
|
|
- if ($payment->status === PaymentStatusEnum::FAILED) {
|
|
|
|
|
- throw new PaymentFailedException;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return $payment;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
public function payServicePackage(
|
|
public function payServicePackage(
|
|
|
int $servicePackageId,
|
|
int $servicePackageId,
|
|
|
string $paymentMethod,
|
|
string $paymentMethod,
|
|
@@ -469,12 +262,16 @@ class PaymentService
|
|
|
|
|
|
|
|
//
|
|
//
|
|
|
|
|
|
|
|
- public function getOrCreatePixPayment(Schedule $schedule): Payment
|
|
|
|
|
|
|
+ public function getOrCreateServicePackagePixPayment(ServicePackage $servicePackage): Payment
|
|
|
{
|
|
{
|
|
|
- $this->ensureScheduleCanBePaidIndividually($schedule);
|
|
|
|
|
|
|
+ $userId = (int) Auth::id();
|
|
|
|
|
+
|
|
|
|
|
+ if ($servicePackage->client?->user_id !== $userId) {
|
|
|
|
|
+ throw new AuthorizationException;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
$existingPayment = Payment::query()
|
|
$existingPayment = Payment::query()
|
|
|
- ->where('schedule_id', $schedule->id)
|
|
|
|
|
|
|
+ ->where('service_package_id', $servicePackage->id)
|
|
|
->where('payment_method', 'pix')
|
|
->where('payment_method', 'pix')
|
|
|
->whereIn('status', [
|
|
->whereIn('status', [
|
|
|
PaymentStatusEnum::PENDING->value,
|
|
PaymentStatusEnum::PENDING->value,
|
|
@@ -511,16 +308,103 @@ class PaymentService
|
|
|
->where('payment_id', $existingPayment->id)
|
|
->where('payment_id', $existingPayment->id)
|
|
|
->update(['status' => PaymentSplitStatusEnum::FAILED]);
|
|
->update(['status' => PaymentSplitStatusEnum::FAILED]);
|
|
|
} else {
|
|
} else {
|
|
|
- $this->syncScheduleStatusAfterPayment($schedule, $existingPayment);
|
|
|
|
|
|
|
+ $this->syncPaymentTargets($existingPayment);
|
|
|
|
|
|
|
|
return $existingPayment;
|
|
return $existingPayment;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return $this->processAcceptedSchedulePayment(
|
|
|
|
|
- schedule: $schedule,
|
|
|
|
|
- paymentMethod: 'pix',
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ $paymentData = DB::transaction(function () use ($servicePackage, $userId): array {
|
|
|
|
|
+ $servicePackage = ServicePackage::query()
|
|
|
|
|
+ ->lockForUpdate()
|
|
|
|
|
+ ->with(['client', 'provider', 'items.schedule.client', 'items.schedule.provider', 'items.schedule.customSchedule.serviceType'])
|
|
|
|
|
+ ->findOrFail($servicePackage->id);
|
|
|
|
|
+
|
|
|
|
|
+ if ($servicePackage->client?->user_id !== $userId) {
|
|
|
|
|
+ throw new AuthorizationException;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $schedules = $servicePackage->items->pluck('schedule')->filter()->values();
|
|
|
|
|
+
|
|
|
|
|
+ $this->validateServicePackageForPayment($servicePackage, $schedules);
|
|
|
|
|
+
|
|
|
|
|
+ if ($servicePackage->status !== ServicePackageStatusEnum::OPEN) {
|
|
|
|
|
+ throw new PaymentException;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $totals = $this->servicePackagePaymentTotals($schedules, 'pix');
|
|
|
|
|
+
|
|
|
|
|
+ $payment = Payment::create([
|
|
|
|
|
+ 'schedule_id' => null,
|
|
|
|
|
+ 'service_package_id' => $servicePackage->id,
|
|
|
|
|
+ 'client_id' => $servicePackage->client_id,
|
|
|
|
|
+ 'provider_id' => $servicePackage->provider_id,
|
|
|
|
|
+ 'client_payment_method_id' => null,
|
|
|
|
|
+ 'gateway_provider' => 'pagarme',
|
|
|
|
|
+ 'gateway_code' => 'payment-'.(string) Str::uuid(),
|
|
|
|
|
+ 'payment_method' => 'pix',
|
|
|
|
|
+ 'status' => PaymentStatusEnum::PENDING,
|
|
|
|
|
+ 'gross_amount' => data_get($totals, 'gross_amount'),
|
|
|
|
|
+ 'gateway_fee_amount' => 0,
|
|
|
|
|
+ 'platform_fee_amount' => data_get($totals, 'platform_fee_amount'),
|
|
|
|
|
+ 'net_amount' => data_get($totals, 'gross_amount'),
|
|
|
|
|
+ 'currency' => 'BRL',
|
|
|
|
|
+ 'installments' => 1,
|
|
|
|
|
+ 'expires_at' => Carbon::now()->addMinutes(30),
|
|
|
|
|
+
|
|
|
|
|
+ 'metadata' => [
|
|
|
|
|
+ 'service_package_id' => (string) $servicePackage->id,
|
|
|
|
|
+ 'schedule_ids' => $schedules->pluck('id')->map(fn ($id) => (string) $id)->all(),
|
|
|
|
|
+ 'service_amount' => number_format(data_get($totals, 'service_amount'), 2, '.', ''),
|
|
|
|
|
+ 'platform_fee' => number_format(data_get($totals, 'platform_fee_amount'), 2, '.', ''),
|
|
|
|
|
+ ],
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ PaymentSplit::create([
|
|
|
|
|
+ 'payment_id' => $payment->id,
|
|
|
|
|
+ 'provider_id' => $servicePackage->provider_id,
|
|
|
|
|
+ 'gateway_provider' => 'pagarme',
|
|
|
|
|
+ 'gateway_transfer_target_reference' => $servicePackage->provider->recipient_id,
|
|
|
|
|
+ 'gateway_transfer_target_label' => 'recipient',
|
|
|
|
|
+ 'status' => PaymentSplitStatusEnum::PENDING,
|
|
|
|
|
+ 'gross_amount' => data_get($totals, 'service_amount'),
|
|
|
|
|
+ 'gateway_fee_amount' => 0,
|
|
|
|
|
+ 'net_amount' => data_get($totals, 'service_amount'),
|
|
|
|
|
+
|
|
|
|
|
+ 'metadata' => [
|
|
|
|
|
+ 'service_package_id' => (string) $servicePackage->id,
|
|
|
|
|
+ 'schedule_ids' => $schedules->pluck('id')->map(fn ($id) => (string) $id)->all(),
|
|
|
|
|
+ ],
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ return compact('payment', 'schedules');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ /** @var Payment $payment */
|
|
|
|
|
+ $payment = data_get($paymentData, 'payment');
|
|
|
|
|
+
|
|
|
|
|
+ /** @var SupportCollection $schedules */
|
|
|
|
|
+ $schedules = data_get($paymentData, 'schedules');
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ $orderResponse = $this->pagarmePaymentService->processServicePackagePayment(
|
|
|
|
|
+ payment: $payment,
|
|
|
|
|
+ schedules: $schedules,
|
|
|
|
|
+ paymentMethod: 'pix',
|
|
|
|
|
+ cardId: null,
|
|
|
|
|
+ options: [],
|
|
|
|
|
+ );
|
|
|
|
|
+ } catch (\Throwable $e) {
|
|
|
|
|
+ $this->failPayment($payment, $e->getMessage());
|
|
|
|
|
+
|
|
|
|
|
+ throw $e;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $payment = $this->pagarmePaymentService->applyGatewayResponseToPayment($payment, $orderResponse);
|
|
|
|
|
+
|
|
|
|
|
+ $this->syncPaymentTargets($payment);
|
|
|
|
|
+
|
|
|
|
|
+ return $payment->fresh(['client.user', 'provider.user', 'servicePackage.items.schedule']);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
@@ -747,17 +631,4 @@ class PaymentService
|
|
|
->where('payment_id', $payment->id)
|
|
->where('payment_id', $payment->id)
|
|
|
->update(['status' => PaymentSplitStatusEnum::FAILED]);
|
|
->update(['status' => PaymentSplitStatusEnum::FAILED]);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- private function ensureScheduleCanBePaidIndividually(Schedule $schedule): void
|
|
|
|
|
- {
|
|
|
|
|
- $belongsToMultiItemServicePackage = ServicePackage::query()
|
|
|
|
|
- ->where('status', ServicePackageStatusEnum::OPEN->value)
|
|
|
|
|
- ->whereHas('items', fn ($query) => $query->where('schedule_id', $schedule->id))
|
|
|
|
|
- ->whereHas('items', null, '>', 1)
|
|
|
|
|
- ->exists();
|
|
|
|
|
-
|
|
|
|
|
- if ($belongsToMultiItemServicePackage) {
|
|
|
|
|
- throw new PaymentException;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|