feat: add total field to purchases and update related logic for payment processing

This commit is contained in:
2026-07-07 10:16:28 -03:00
parent 362e888e46
commit 7b2a741884
9 changed files with 377 additions and 56 deletions

View File

@@ -158,7 +158,7 @@ class TelepagosIntegrationService extends BaseIntegrationService
* @return array
* @throws Exception
*/
public function getCashinDetails(int $cashinId): array
public function getCashinDetails(string $cashinId): array
{
$response = $this->sendRequest('get', "/v2/payment/cashin/{$cashinId}");

View File

@@ -2,10 +2,10 @@
namespace App\Domains\Integration\Services;
use App\Domains\Purchase\Services\CheckoutService;
use App\Domains\Purchase\Models\Purchase;
use App\Domains\Purchase\Models\TelepagosPayment;
use App\Domains\Purchase\Models\TelepagosQr;
use App\Domains\Purchase\Services\CheckoutService;
use App\Domains\Tenant\Models\Tenant;
use Exception;
use Illuminate\Support\Facades\Log;
@@ -27,18 +27,15 @@ class TelepagosWebhookService
public function handleWebhook(string $tenantCodigo, string $cashinId): void
{
$tenant = Tenant::where('codigo', $tenantCodigo)->firstOrFail();
$telepagosService = new TelepagosIntegrationService();
$telepagosService->forTenant($tenant);
$telepagosService->forTenant($tenant->codigo);
try {
$details = $telepagosService->getCashinDetails($cashinId);
// Extract the qr_order_id from the response
// The exact path depends on Telepagos response structure, assuming it's inside 'data'
// Need to handle potential nested structures based on actual API
$qrOrderId = $details['data']['qr_order_id'] ?? $details['qr_order_id'] ?? null;
$amount = $details['data']['amount'] ?? $details['amount'] ?? 0;
$amount = $this->normalizeAmount($details['data']['amount'] ?? $details['amount'] ?? 0);
$operationId = $details['data']['operation_id'] ?? $details['operation_id'] ?? null;
$transferenciaOperationIds = [1, 3, 11];
@@ -46,59 +43,51 @@ class TelepagosWebhookService
$compra = null;
if (in_array((int)$operationId, $transferenciaOperationIds, true)) {
if (in_array((int) $operationId, $transferenciaOperationIds, true)) {
$cuit = $details['data']['buyer']['cuit'] ?? $details['buyer']['cuit'] ?? null;
if (!$cuit) {
if (! $cuit) {
Log::warning("Telepagos webhook: CUIT not found for Transferencia cashin {$cashinId}");
return;
}
// Extraer el DNI del cuit (sacando los primeros 2 y el último dígito)
$dni = substr($cuit, 2, -1);
// Buscar la compra pendiente, con método transferencia, más reciente
$compras = Purchase::where('tenant_codigo', $tenantCodigo)
$compra = Purchase::where('tenant_codigo', $tenantCodigo)
->where('dni', $dni)
->where('status', 'pendiente')
->where('payment_method', 'transferencia')
->where('status', 'pending')
->where('payment_method', 'transfer')
->where('total', $amount)
->latest()
->get();
foreach ($compras as $c) {
if ($c->getTotalAmount() == $amount) {
$compra = $c;
break;
}
}
if (!$compra) {
->first();
if (! $compra) {
Log::warning("Telepagos webhook: No matching purchase found for DNI {$dni} and amount {$amount} for cashin {$cashinId}");
return;
}
} elseif (in_array((int)$operationId, $qrOperationIds, true)) {
if (!$qrOrderId) {
} elseif (in_array((int) $operationId, $qrOperationIds, true)) {
if (! $qrOrderId) {
Log::warning("Telepagos webhook: qr_order_id not found for QR cashin {$cashinId}");
return;
}
$telepagosQr = TelepagosQr::where('qr_order_id', $qrOrderId)->first();
if (!$telepagosQr) {
if (! $telepagosQr) {
Log::warning("Telepagos webhook: QR {$qrOrderId} not found in database for cashin {$cashinId}");
return;
}
$compra = $telepagosQr->compra;
if (!$compra) {
if (! $compra) {
Log::warning("Telepagos webhook: Purchase not found for QR {$qrOrderId}");
return;
}
$totalAmount = $compra->getTotalAmount();
if ($amount != $totalAmount) {
$totalAmount = $this->normalizeAmount($compra->getTotalAmount());
if ($amount !== $totalAmount) {
Log::warning("Telepagos webhook: Amount mismatch. Cashin amount: {$amount}, Purchase amount: {$totalAmount}");
return;
}
@@ -107,7 +96,6 @@ class TelepagosWebhookService
return;
}
// Create the TelepagosPayment and finalize the purchase in a transaction
$paymentData = [
'compra_id' => $compra->id,
'cuit_buyer' => $details['data']['cuit_buyer'] ?? $details['cuit_buyer'] ?? null,
@@ -128,10 +116,14 @@ class TelepagosWebhookService
});
Log::info("Telepagos webhook: Successfully processed cashin {$cashinId} for purchase {$compra->id}");
} catch (Exception $e) {
Log::error("Telepagos webhook error: " . $e->getMessage());
throw $e;
}
}
protected function normalizeAmount(mixed $amount): string
{
return number_format((float) $amount, 2, '.', '');
}
}

View File

@@ -55,9 +55,11 @@ class PurchaseController extends Controller
{
$compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);
$method = $request->validated('method');
$totalAmount = $compra->calculateCurrentTotalAmount();
$compra->update([
'payment_method' => $method,
'total' => $totalAmount,
]);
if ($method === 'transfer') {
@@ -89,9 +91,9 @@ class PurchaseController extends Controller
$telepagosService->forTenant($tenant->codigo);
try {
Log::info("Generating QR for purchase ID: {$compra->id}, amount: {$compra->getTotalAmount()}");
Log::info("Generating QR for purchase ID: {$compra->id}, amount: {$totalAmount}");
$qrResponse = $telepagosService->generateQr(
$compra->getTotalAmount(),
$totalAmount,
'Compra',
"Compra #{$compra->id}"
);

View File

@@ -18,6 +18,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
'status',
'payment_status',
'payment_method',
'total',
'dni',
'telefono',
'nombre_apellido',
@@ -34,6 +35,7 @@ class Purchase extends Model
return [
'cart_id' => 'integer',
'user_id' => 'integer',
'total' => 'decimal:2',
];
}
@@ -87,13 +89,26 @@ class Purchase extends Model
public function getTotalAmount(): float
{
if ($this->total !== null) {
return (float) $this->total;
}
return $this->calculateCurrentTotalAmount();
}
public function calculateCurrentTotalAmount(): float
{
if ($this->relationLoaded('items') && $this->getRelation('items')->isNotEmpty()) {
return (float) $this->getRelation('items')->sum('total');
}
if ($this->items()->exists()) {
return (float) $this->items()->sum('total');
}
$cart = $this->relationLoaded('cart')
? $this->getRelation('cart')
: $this->cart()->first();
: $this->cart()->with('items.variant.product')->first();
if (! $cart) {
return 0.0;
@@ -105,8 +120,8 @@ class Purchase extends Model
public function finalize(): void
{
$this->update([
'status' => 'pagado', // Or the equivalent final status in your system
'payment_status' => 'paid',
'status' => 'paid',
'payment_status' => 'approved',
]);
}
}

View File

@@ -19,15 +19,19 @@ class PurchaseResource extends JsonResource
? $this->resource->getRelation('items')
: collect();
$subtotal = $items->reduce(
fn (float $carry, $item): float => $carry + ((float) $item->precio_unitario * $item->cantidad),
0.0,
);
$subtotal = $items->isNotEmpty()
? $items->reduce(
fn (float $carry, $item): float => $carry + ((float) $item->precio_unitario * $item->cantidad),
0.0,
)
: (float) ($this->total ?? 0);
$total = $items->reduce(
fn (float $carry, $item): float => $carry + (float) $item->total,
0.0,
);
$total = $items->isNotEmpty()
? $items->reduce(
fn (float $carry, $item): float => $carry + (float) $item->total,
0.0,
)
: (float) ($this->total ?? 0);
return [
'id' => $this->id,

View File

@@ -29,6 +29,10 @@ class CheckoutService
]);
}
$cartItems->load('variant.product');
$cart->setRelation('items', $cartItems);
$totalAmount = $cart->getTotalAmount();
/** @var Purchase $purchase */
$purchase = Purchase::query()->updateOrCreate(
[
@@ -41,6 +45,7 @@ class CheckoutService
...$purchaseData,
'payment_status' => 'pending',
'payment_method' => null,
'total' => $totalAmount,
]
);