|
|
@@ -9,37 +9,40 @@ use App\Data\Pagarme\Request\CustomerRequestData\CustomerRequestData;
|
|
|
use App\Data\Pagarme\Request\OrderRequestData\OrderItemData;
|
|
|
use App\Data\Pagarme\Request\OrderRequestData\OrderPaymentData\OrderCreditCardData;
|
|
|
use App\Data\Pagarme\Request\OrderRequestData\OrderPaymentData\OrderPaymentData;
|
|
|
-use App\Data\Pagarme\Request\OrderRequestData\OrderPaymentData\OrderPixAdditionalInformationData;
|
|
|
-use App\Data\Pagarme\Request\OrderRequestData\OrderPaymentData\OrderPixData;
|
|
|
-use App\Data\Pagarme\Request\OrderRequestData\OrderPaymentData\OrderSplitData;
|
|
|
-use App\Data\Pagarme\Request\OrderRequestData\OrderPaymentData\OrderSplitOptionsData;
|
|
|
+use App\Data\Pagarme\Request\OrderRequestData\OrderPaymentData\OrderPixData\OrderPixAdditionalInformationData;
|
|
|
+use App\Data\Pagarme\Request\OrderRequestData\OrderPaymentData\OrderPixData\OrderPixData;
|
|
|
+use App\Data\Pagarme\Request\OrderRequestData\OrderPaymentData\OrderSplitData\OrderSplitData;
|
|
|
+use App\Data\Pagarme\Request\OrderRequestData\OrderPaymentData\OrderSplitData\OrderSplitOptionsData;
|
|
|
use App\Data\Pagarme\Request\OrderRequestData\OrderRequestData;
|
|
|
use App\Data\Pagarme\Response\OrderResponseData\OrderResponseData;
|
|
|
use App\Enums\PaymentSplitStatusEnum;
|
|
|
use App\Enums\PaymentStatusEnum;
|
|
|
use App\Models\Address;
|
|
|
+use App\Models\Client;
|
|
|
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(
|
|
|
- Payment $payment,
|
|
|
+ Payment $payment,
|
|
|
Schedule $schedule,
|
|
|
- string $paymentMethod,
|
|
|
- ?string $cardId = null,
|
|
|
- array $options = [],
|
|
|
+ string $paymentMethod,
|
|
|
+ ?string $cardId = null,
|
|
|
+ array $options = [],
|
|
|
): array {
|
|
|
$grossAmount = (float) $payment->gross_amount;
|
|
|
|
|
|
- $items = $this->buildOrderItems($schedule, $grossAmount);
|
|
|
+ $items = $this->buildOrderItems($schedule, $grossAmount);
|
|
|
$customer = $this->buildCustomer($schedule, $options);
|
|
|
- $split = $this->buildSplit($payment, $options);
|
|
|
+ $split = $this->buildSplit($payment, $options);
|
|
|
|
|
|
$pixOptions = config('services.pagarme.pix_disable_split')
|
|
|
? []
|
|
|
@@ -49,84 +52,89 @@ class PagarmePaymentService
|
|
|
|
|
|
if ($paymentMethod === 'credit_card') {
|
|
|
$creditCard = new OrderCreditCardData(
|
|
|
- cardId: $cardId,
|
|
|
- installments: 1,
|
|
|
+ cardId: $cardId,
|
|
|
+ installments: 1,
|
|
|
statementDescriptor: Str::limit((string) config('app.name', 'SOFTPAR'), 13, ''),
|
|
|
- operationType: 'auth_and_capture',
|
|
|
+ operationType: 'auth_and_capture',
|
|
|
);
|
|
|
|
|
|
return $this->createOrderWithCreditCard(
|
|
|
- payment: $payment,
|
|
|
- items: $items,
|
|
|
- customer: $customer,
|
|
|
+ payment: $payment,
|
|
|
+ items: $items,
|
|
|
+ customer: $customer,
|
|
|
creditCard: $creditCard,
|
|
|
- options: $orderOptions,
|
|
|
+ options: $orderOptions,
|
|
|
);
|
|
|
}
|
|
|
|
|
|
$pixData = new OrderPixData(
|
|
|
expiresIn: 1800,
|
|
|
+
|
|
|
additionalInformation: [
|
|
|
new OrderPixAdditionalInformationData(
|
|
|
- name: 'Agendamento',
|
|
|
+ name: 'Agendamento',
|
|
|
value: (string) $schedule->id,
|
|
|
),
|
|
|
],
|
|
|
);
|
|
|
|
|
|
return $this->createOrderWithPix(
|
|
|
- payment: $payment,
|
|
|
- items: $items,
|
|
|
+ payment: $payment,
|
|
|
+ items: $items,
|
|
|
customer: $customer,
|
|
|
- pix: $pixData,
|
|
|
- options: $pixOptions,
|
|
|
+ pix: $pixData,
|
|
|
+ options: $pixOptions,
|
|
|
);
|
|
|
}
|
|
|
|
|
|
public function createOrderWithCreditCard(
|
|
|
- Payment $payment,
|
|
|
- array $items,
|
|
|
+ Payment $payment,
|
|
|
+ array $items,
|
|
|
CustomerRequestData $customer,
|
|
|
OrderCreditCardData $creditCard,
|
|
|
- array $options = []
|
|
|
+ array $options = []
|
|
|
): array {
|
|
|
return $this->createOrder(
|
|
|
- payment: $payment,
|
|
|
- items: $items,
|
|
|
+ payment: $payment,
|
|
|
+ items: $items,
|
|
|
customer: $customer,
|
|
|
+
|
|
|
paymentMethod: OrderRequestData::creditCardPaymentMethod(
|
|
|
creditCard: $creditCard,
|
|
|
- split: is_array($options['split'] ?? null) ? $options['split'] : null,
|
|
|
+ split: is_array($options['split'] ?? null) ? $options['split'] : null,
|
|
|
),
|
|
|
+
|
|
|
options: $options,
|
|
|
);
|
|
|
}
|
|
|
|
|
|
public function createOrderWithPix(
|
|
|
- Payment $payment,
|
|
|
- array $items,
|
|
|
+ Payment $payment,
|
|
|
+ array $items,
|
|
|
CustomerRequestData $customer,
|
|
|
- OrderPixData $pix,
|
|
|
- array $options = []
|
|
|
+ OrderPixData $pix,
|
|
|
+ array $options = []
|
|
|
): array {
|
|
|
return $this->createOrder(
|
|
|
- payment: $payment,
|
|
|
- items: $items,
|
|
|
+ payment: $payment,
|
|
|
+ items: $items,
|
|
|
customer: $customer,
|
|
|
+
|
|
|
paymentMethod: OrderRequestData::pixPaymentMethod(
|
|
|
- pix: $pix,
|
|
|
+ pix: $pix,
|
|
|
split: is_array($options['split'] ?? null) ? $options['split'] : null,
|
|
|
),
|
|
|
+
|
|
|
options: $options,
|
|
|
);
|
|
|
}
|
|
|
|
|
|
public function createOrder(
|
|
|
- Payment $payment,
|
|
|
- array $items,
|
|
|
+ Payment $payment,
|
|
|
+ array $items,
|
|
|
CustomerRequestData $customer,
|
|
|
- OrderPaymentData $paymentMethod,
|
|
|
- array $options = []
|
|
|
+ OrderPaymentData $paymentMethod,
|
|
|
+ array $options = []
|
|
|
): array {
|
|
|
$metadata = array_merge([
|
|
|
'payment_id' => (string) $payment->id,
|
|
|
@@ -136,26 +144,28 @@ class PagarmePaymentService
|
|
|
], $options['metadata'] ?? []);
|
|
|
|
|
|
$requestData = new OrderRequestData(
|
|
|
- code: $this->ensurePaymentCode($payment),
|
|
|
- items: $items,
|
|
|
- payments: [$paymentMethod],
|
|
|
- metadata: $metadata,
|
|
|
- customer: $customer,
|
|
|
+ code: $payment->ensureGatewayCode(),
|
|
|
+ items: $items,
|
|
|
+ payments: [$paymentMethod],
|
|
|
+ metadata: $metadata,
|
|
|
+ customer: $customer,
|
|
|
customerId: $options['customer_id'] ?? null,
|
|
|
- closed: $options['closed'] ?? true,
|
|
|
- channel: $options['channel'] ?? null,
|
|
|
+ closed: $options['closed'] ?? true,
|
|
|
+ channel: $options['channel'] ?? null,
|
|
|
);
|
|
|
|
|
|
$order = OrderResponseData::fromArray($this->pagarmeRequest(
|
|
|
- method: 'POST',
|
|
|
- path: '/orders',
|
|
|
- payload: $requestData,
|
|
|
+ method: 'POST',
|
|
|
+ path: '/orders',
|
|
|
+ payload: $requestData,
|
|
|
idempotencyKey: $this->idempotencyKey($payment),
|
|
|
- errorMessage: 'Erro ao criar pedido de pagamento no Pagar.me.',
|
|
|
+ errorMessage: 'Erro ao criar pedido de pagamento no Pagar.me.',
|
|
|
));
|
|
|
|
|
|
$order->requireId();
|
|
|
|
|
|
+ $this->saveExternalCustomerId($payment, $order);
|
|
|
+
|
|
|
return $order->toArray();
|
|
|
}
|
|
|
|
|
|
@@ -164,15 +174,19 @@ class PagarmePaymentService
|
|
|
public function applyGatewayResponseToPayment(Payment $payment, array $orderResponse): Payment
|
|
|
{
|
|
|
$order = OrderResponseData::fromArray($orderResponse);
|
|
|
- $newStatus = $order->paymentStatus();
|
|
|
- $failureCode = null;
|
|
|
+
|
|
|
+ $newStatus = $order->paymentStatus();
|
|
|
+ $failureCode = null;
|
|
|
$failureMessage = null;
|
|
|
|
|
|
if ($newStatus === PaymentStatusEnum::FAILED) {
|
|
|
- $failureCode = $order->failureCode();
|
|
|
+ $failureCode = $order->failureCode();
|
|
|
$failureMessage = $order->failureMessage();
|
|
|
}
|
|
|
|
|
|
+ $gatewayFeeCents = $order->lastTransaction()?->cost ?? 0;
|
|
|
+ $gatewayFee = $gatewayFeeCents > 0 ? round($gatewayFeeCents / 100, 2) : 0;
|
|
|
+
|
|
|
$payment->forceFill([
|
|
|
'gateway_provider' => 'pagarme',
|
|
|
'gateway_entity_reference' => $order->gatewayEntityReference(),
|
|
|
@@ -183,6 +197,7 @@ class PagarmePaymentService
|
|
|
'paid_at' => $order->paidAt(),
|
|
|
'authorized_at' => $order->authorizedAt(),
|
|
|
'gateway_payload' => $orderResponse,
|
|
|
+ 'gateway_fee_amount' => $gatewayFee,
|
|
|
'failure_code' => $failureCode,
|
|
|
'failure_message' => $failureMessage,
|
|
|
])->save();
|
|
|
@@ -204,37 +219,10 @@ 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;
|
|
|
- $user = $client->user()->first(['id', 'name', 'email', 'phone']);
|
|
|
+ $client = $schedule->client;
|
|
|
+ $user = $client->user()->first(['id', 'name', 'email', 'phone']);
|
|
|
$address = Address::with(['city.state', 'state'])->find($schedule->address_id);
|
|
|
|
|
|
foreach ([
|
|
|
@@ -256,8 +244,8 @@ class PagarmePaymentService
|
|
|
$phone = $this->buildPhonePayload($user->phone)
|
|
|
?: $this->buildPhonePayload($options['phone'] ?? null);
|
|
|
|
|
|
- $state = $address->state?->code ?? $address->city?->state?->code;
|
|
|
- $city = $address->city?->name;
|
|
|
+ $state = $address->state?->code ?? $address->city?->state?->code;
|
|
|
+ $city = $address->city?->name;
|
|
|
$zipCode = $this->digits($address->zip_code);
|
|
|
|
|
|
$line1 = implode(', ', array_filter([
|
|
|
@@ -280,11 +268,11 @@ class PagarmePaymentService
|
|
|
}
|
|
|
|
|
|
$customerAddress = new CustomerAddressRequestData(
|
|
|
- line1: $line1,
|
|
|
- line2: $address->complement ?: $address->instructions,
|
|
|
+ line1: $line1,
|
|
|
+ line2: $address->complement ?: $address->instructions,
|
|
|
zipCode: $zipCode,
|
|
|
- city: $city,
|
|
|
- state: $state,
|
|
|
+ city: $city,
|
|
|
+ state: $state,
|
|
|
country: 'BR',
|
|
|
);
|
|
|
|
|
|
@@ -294,24 +282,37 @@ class PagarmePaymentService
|
|
|
$customerPhones = new CustomerPhonesRequestData(
|
|
|
mobilePhone: new CustomerPhoneData(
|
|
|
countryCode: $phone['country_code'],
|
|
|
- areaCode: $phone['area_code'],
|
|
|
- number: $phone['number'],
|
|
|
+ areaCode: $phone['area_code'],
|
|
|
+ number: $phone['number'],
|
|
|
),
|
|
|
);
|
|
|
}
|
|
|
|
|
|
return new CustomerRequestData(
|
|
|
- name: $user->name,
|
|
|
- email: $user->email,
|
|
|
- document: $document,
|
|
|
- type: strlen($document) === 14 ? 'company' : 'individual',
|
|
|
+ name: $user->name,
|
|
|
+ email: $user->email,
|
|
|
+ document: $document,
|
|
|
+ type: strlen($document) === 14 ? 'company' : 'individual',
|
|
|
documentType: strlen($document) === 14 ? 'CNPJ' : 'CPF',
|
|
|
- code: "client-{$client->id}",
|
|
|
- address: $customerAddress,
|
|
|
- phones: $customerPhones,
|
|
|
+ code: $client->ensureGatewayCode(),
|
|
|
+ address: $customerAddress,
|
|
|
+ phones: $customerPhones,
|
|
|
);
|
|
|
}
|
|
|
|
|
|
+ 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);
|
|
|
@@ -339,19 +340,31 @@ class PagarmePaymentService
|
|
|
|
|
|
$split = OrderRequestData::splitFromTransfers($transfers);
|
|
|
|
|
|
- $platformFee = (float) ($payment->platform_fee_amount ?? 0);
|
|
|
+ $platformRecipientId = config('services.pagarme.platform_recipient_id');
|
|
|
+
|
|
|
+ if (empty($platformRecipientId)) {
|
|
|
+ return $split;
|
|
|
+ }
|
|
|
+
|
|
|
+ $orderAmountCents = OrderRequestData::amountInCents((float) $payment->gross_amount);
|
|
|
+
|
|
|
+ $providerTotalCents = array_sum(array_map(
|
|
|
+ static fn (OrderSplitData $s) => $s->amount,
|
|
|
+ $split,
|
|
|
+ ));
|
|
|
|
|
|
- if ($platformFee > 0) {
|
|
|
- $platformRecipientId = config('services.pagarme.platform_recipient_id');
|
|
|
+ $platformAmountCents = $orderAmountCents - $providerTotalCents;
|
|
|
|
|
|
+ if ($platformAmountCents > 0) {
|
|
|
$split[] = new OrderSplitData(
|
|
|
- amount: OrderRequestData::amountInCents($platformFee),
|
|
|
+ amount: $platformAmountCents,
|
|
|
recipientId: $platformRecipientId,
|
|
|
- type: 'flat',
|
|
|
+ type: 'flat',
|
|
|
+
|
|
|
options: new OrderSplitOptionsData(
|
|
|
chargeProcessingFee: true,
|
|
|
- chargeRemainderFee: true,
|
|
|
- liable: true,
|
|
|
+ chargeRemainderFee: true,
|
|
|
+ liable: true,
|
|
|
),
|
|
|
);
|
|
|
}
|
|
|
@@ -359,28 +372,54 @@ 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}";
|
|
|
+ if (! empty($payment->idempotency_key)) {
|
|
|
+ return $payment->idempotency_key;
|
|
|
+ }
|
|
|
+
|
|
|
+ $key = 'order-'.(string) \Illuminate\Support\Str::uuid();
|
|
|
+
|
|
|
+ $payment->forceFill(['idempotency_key' => $key])->save();
|
|
|
+
|
|
|
+ return $key;
|
|
|
}
|
|
|
|
|
|
- private function ensurePaymentCode(Payment $payment): string
|
|
|
+ // salva o gateway_customer_id do Pagar.me no Client apos criacao de ordem
|
|
|
+
|
|
|
+ private function saveExternalCustomerId(Payment $payment, OrderResponseData $order): void
|
|
|
{
|
|
|
- if (! empty($payment->gateway_code)) {
|
|
|
- return $payment->gateway_code;
|
|
|
+ $customerId = $order->customer?->id;
|
|
|
+ $customerCode = $order->customer?->code;
|
|
|
+
|
|
|
+ if (! $customerId && ! $customerCode) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ $client = Client::find($payment->client_id);
|
|
|
+
|
|
|
+ if (! $client) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ $updated = false;
|
|
|
+
|
|
|
+ if (! $client->gateway_customer_id && $customerId) {
|
|
|
+ $client->gateway_customer_id = $customerId;
|
|
|
+
|
|
|
+ $updated = true;
|
|
|
}
|
|
|
|
|
|
- $code = 'payment-'.(string) Str::uuid();
|
|
|
+ if (! $client->gateway_customer_code && $customerCode) {
|
|
|
+ $client->gateway_customer_code = $customerCode;
|
|
|
|
|
|
- $payment->forceFill(['gateway_code' => $code])->save();
|
|
|
+ $updated = true;
|
|
|
+ }
|
|
|
|
|
|
- return $code;
|
|
|
+ if ($updated) {
|
|
|
+ $client->save();
|
|
|
+ }
|
|
|
}
|
|
|
}
|