|
|
@@ -24,6 +24,7 @@ use App\Models\Payment;
|
|
|
use App\Models\PaymentSplit;
|
|
|
use App\Models\Schedule;
|
|
|
use App\Services\Pagarme\Concerns\FormatsPagarmeData;
|
|
|
+use App\Services\Pagarme\Concerns\MocksPagarmeRequests;
|
|
|
use App\Services\Pagarme\Concerns\SendsPagarmeRequests;
|
|
|
use Illuminate\Support\Facades\Log;
|
|
|
use Illuminate\Support\Str;
|
|
|
@@ -31,6 +32,7 @@ use Illuminate\Support\Str;
|
|
|
class PagarmePaymentService
|
|
|
{
|
|
|
use FormatsPagarmeData;
|
|
|
+ use MocksPagarmeRequests;
|
|
|
use SendsPagarmeRequests;
|
|
|
|
|
|
public function calculatePaymentAmounts(float $serviceAmount, string $paymentMethod, ?Schedule $schedule = null): array
|
|
|
@@ -220,6 +222,18 @@ class PagarmePaymentService
|
|
|
channel: $options['channel'] ?? null,
|
|
|
);
|
|
|
|
|
|
+ if ($this->shouldMockPagarmeRequest()) {
|
|
|
+ $order = OrderResponseData::fromArray(
|
|
|
+ $this->mockOrderResponse($payment, $requestData, $paymentMethod),
|
|
|
+ );
|
|
|
+
|
|
|
+ $order->requireId();
|
|
|
+
|
|
|
+ $this->saveExternalCustomerId($payment, $order);
|
|
|
+
|
|
|
+ return $order->toArray();
|
|
|
+ }
|
|
|
+
|
|
|
$order = OrderResponseData::fromArray($this->pagarmeRequest(
|
|
|
method: 'POST',
|
|
|
path: '/orders',
|
|
|
@@ -293,6 +307,90 @@ class PagarmePaymentService
|
|
|
|
|
|
//
|
|
|
|
|
|
+ private function mockOrderResponse(
|
|
|
+ Payment $payment, OrderRequestData $requestData, PaymentData $paymentMethod,
|
|
|
+ ): array {
|
|
|
+ $payload = $requestData->toArray();
|
|
|
+
|
|
|
+ $now = now()->toISOString();
|
|
|
+
|
|
|
+ $orderId = $this->mockPagarmeId('or', $payment->id);
|
|
|
+ $chargeId = $this->mockPagarmeId('ch', $payment->id);
|
|
|
+ $transactionId = $this->mockPagarmeId('tran', $payment->id);
|
|
|
+
|
|
|
+ $amount = array_sum(array_map(
|
|
|
+ static fn (array $item): int => ((int) ($item['amount'] ?? 0)) * ((int) ($item['quantity'] ?? 1)),
|
|
|
+ $payload['items'] ?? [],
|
|
|
+ ));
|
|
|
+
|
|
|
+ $isCreditCard = $paymentMethod->paymentMethod === 'credit_card';
|
|
|
+
|
|
|
+ $chargeStatus = $isCreditCard ? 'paid' : 'pending';
|
|
|
+ $transactionStatus = $isCreditCard ? 'captured' : 'waiting_payment';
|
|
|
+
|
|
|
+ $transaction = [
|
|
|
+ 'id' => $transactionId,
|
|
|
+ 'status' => $transactionStatus,
|
|
|
+ 'amount' => $amount,
|
|
|
+ 'cost' => 0,
|
|
|
+ 'created_at' => $now,
|
|
|
+
|
|
|
+ 'acquirer_message' => $isCreditCard ? 'Transacao mockada aprovada.' : null,
|
|
|
+
|
|
|
+ 'gateway_response' => [
|
|
|
+ 'code' => $isCreditCard ? '00' : 'mock_waiting_payment',
|
|
|
+ 'message' => 'Pagar.me mock local.',
|
|
|
+ ],
|
|
|
+ ];
|
|
|
+
|
|
|
+ if (! $isCreditCard) {
|
|
|
+ $transaction['qr_code'] = '00020101021226880014br.gov.bcb.pix2566mock.local/pix/'.$payment->id;
|
|
|
+ $transaction['qr_code_url'] = url("/mock/pix/{$payment->id}");
|
|
|
+ $transaction['expires_at'] = now()->addMinutes(30)->toISOString();
|
|
|
+ }
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'id' => $orderId,
|
|
|
+ 'code' => $requestData->code,
|
|
|
+ 'amount' => $amount,
|
|
|
+ 'currency' => 'BRL',
|
|
|
+ 'closed' => true,
|
|
|
+ 'status' => $chargeStatus,
|
|
|
+ 'items' => $payload['items'] ?? [],
|
|
|
+ 'customer' => $this->mockCustomerResponse($requestData),
|
|
|
+
|
|
|
+ 'charges' => [[
|
|
|
+ 'id' => $chargeId,
|
|
|
+ 'status' => $chargeStatus,
|
|
|
+ 'amount' => $amount,
|
|
|
+ 'currency' => 'BRL',
|
|
|
+ 'paid_at' => $isCreditCard ? $now : null,
|
|
|
+ 'created_at' => $now,
|
|
|
+ 'expires_at' => $isCreditCard ? null : now()->addMinutes(30)->toISOString(),
|
|
|
+
|
|
|
+ 'last_transaction' => $transaction,
|
|
|
+ ]],
|
|
|
+
|
|
|
+ 'checkouts' => [],
|
|
|
+ 'metadata' => array_merge($requestData->metadata, ['mocked' => true]),
|
|
|
+ 'created_at' => $now,
|
|
|
+ 'updated_at' => $now,
|
|
|
+ 'closed_at' => $now,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ private function mockCustomerResponse(OrderRequestData $requestData): array
|
|
|
+ {
|
|
|
+ $customer = $requestData->customer?->toArray() ?? [];
|
|
|
+
|
|
|
+ return array_merge($customer, [
|
|
|
+ 'id' => $requestData->customerId ?: $this->mockPagarmeId('cus', $customer['code'] ?? $requestData->code),
|
|
|
+ 'delinquent' => false,
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ //
|
|
|
+
|
|
|
private function buildCustomer(Schedule $schedule, array $options = []): CustomerRequestData
|
|
|
{
|
|
|
$client = $schedule->client;
|
|
|
@@ -388,6 +486,79 @@ class PagarmePaymentService
|
|
|
)];
|
|
|
}
|
|
|
|
|
|
+ private function buildPhonePayload(?string $phone): ?array
|
|
|
+ {
|
|
|
+ $digits = $this->digits($phone);
|
|
|
+
|
|
|
+ if (strlen($digits) < 10) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (str_starts_with($digits, '55')) {
|
|
|
+ $digits = substr($digits, 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'country_code' => '55',
|
|
|
+ 'area_code' => substr($digits, 0, 2),
|
|
|
+ 'number' => substr($digits, 2),
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ private function buildSplit(Payment $payment, array $options): array
|
|
|
+ {
|
|
|
+ $transfers = PaymentSplit::query()
|
|
|
+ ->where('payment_id', $payment->id)
|
|
|
+ ->get();
|
|
|
+
|
|
|
+ $split = OrderRequestData::splitFromTransfers($transfers);
|
|
|
+
|
|
|
+ $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 (SplitData $s) => $s->amount,
|
|
|
+ $split,
|
|
|
+ ));
|
|
|
+
|
|
|
+ $platformAmountCents = $orderAmountCents - $providerTotalCents;
|
|
|
+
|
|
|
+ if ($platformAmountCents > 0) {
|
|
|
+ $split[] = new SplitData(
|
|
|
+ amount: $platformAmountCents,
|
|
|
+ recipientId: $platformRecipientId,
|
|
|
+ type: 'flat',
|
|
|
+
|
|
|
+ options: new SplitOptionsData(
|
|
|
+ chargeProcessingFee: true,
|
|
|
+ chargeRemainderFee: true,
|
|
|
+ liable: true,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ return $split;
|
|
|
+ }
|
|
|
+
|
|
|
+ //
|
|
|
+
|
|
|
+ private function isMisleadingGatewayCode(mixed $code): bool
|
|
|
+ {
|
|
|
+ if ($code === null || $code === '') {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ $code = mb_strtolower((string) $code);
|
|
|
+
|
|
|
+ return preg_match('/^[1-2]\d{2}$/', $code) === 1
|
|
|
+ || in_array($code, ['00', '0', 'approved', 'success'], true);
|
|
|
+ }
|
|
|
+
|
|
|
private function logCreditCardOrderResult(Payment $payment, array $orderResponse, string $source): void
|
|
|
{
|
|
|
$order = OrderResponseData::fromArray($orderResponse);
|
|
|
@@ -446,82 +617,11 @@ class PagarmePaymentService
|
|
|
return $gatewayResponse;
|
|
|
}
|
|
|
|
|
|
- private function isMisleadingGatewayCode(mixed $code): bool
|
|
|
- {
|
|
|
- if ($code === null || $code === '') {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- $code = mb_strtolower((string) $code);
|
|
|
-
|
|
|
- return preg_match('/^[1-2]\d{2}$/', $code) === 1
|
|
|
- || in_array($code, ['00', '0', 'approved', 'success'], true);
|
|
|
- }
|
|
|
-
|
|
|
- private function buildPhonePayload(?string $phone): ?array
|
|
|
- {
|
|
|
- $digits = $this->digits($phone);
|
|
|
-
|
|
|
- if (strlen($digits) < 10) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- if (str_starts_with($digits, '55')) {
|
|
|
- $digits = substr($digits, 2);
|
|
|
- }
|
|
|
-
|
|
|
- return [
|
|
|
- 'country_code' => '55',
|
|
|
- 'area_code' => substr($digits, 0, 2),
|
|
|
- 'number' => substr($digits, 2),
|
|
|
- ];
|
|
|
- }
|
|
|
-
|
|
|
private function roundMoneyUp(float $amount): float
|
|
|
{
|
|
|
return ceil($amount * 100) / 100;
|
|
|
}
|
|
|
|
|
|
- private function buildSplit(Payment $payment, array $options): array
|
|
|
- {
|
|
|
- $transfers = PaymentSplit::query()
|
|
|
- ->where('payment_id', $payment->id)
|
|
|
- ->get();
|
|
|
-
|
|
|
- $split = OrderRequestData::splitFromTransfers($transfers);
|
|
|
-
|
|
|
- $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 (SplitData $s) => $s->amount,
|
|
|
- $split,
|
|
|
- ));
|
|
|
-
|
|
|
- $platformAmountCents = $orderAmountCents - $providerTotalCents;
|
|
|
-
|
|
|
- if ($platformAmountCents > 0) {
|
|
|
- $split[] = new SplitData(
|
|
|
- amount: $platformAmountCents,
|
|
|
- recipientId: $platformRecipientId,
|
|
|
- type: 'flat',
|
|
|
-
|
|
|
- options: new SplitOptionsData(
|
|
|
- chargeProcessingFee: true,
|
|
|
- chargeRemainderFee: true,
|
|
|
- liable: true,
|
|
|
- ),
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- return $split;
|
|
|
- }
|
|
|
-
|
|
|
// evita criacao duplicada de payment
|
|
|
|
|
|
private function idempotencyKey(Payment $payment): string
|