Files
shopit-back/app/Domains/Purchase/Controllers/PurchaseController.php
ncoronel ff9d7728e8 Implement localization for API responses and error messages
- Added localization support for API responses in English and Spanish.
- Introduced a middleware to set the API locale based on the Accept-Language header.
- Updated various exception messages and validation responses to use localized strings.
- Created new language files for English and Spanish translations.
- Refactored existing code to replace hardcoded messages with localized strings.
- Added tests to verify localization functionality and response correctness.
2026-07-28 10:19:39 -03:00

244 lines
8.6 KiB
PHP

<?php
namespace App\Domains\Purchase\Controllers;
use App\Domains\Integration\Services\TelepagosIntegrationService;
use App\Domains\Purchase\Models\Purchase;
use App\Domains\Purchase\Models\PurchaseItem;
use App\Domains\Purchase\Requests\PaymentIntentRequest;
use App\Domains\Purchase\Requests\StartCheckoutRequest;
use App\Domains\Purchase\Requests\UpdatePurchaseCustomerRequest;
use App\Domains\Purchase\Requests\UpdatePurchaseItemQuantityRequest;
use App\Domains\Purchase\Resources\PurchaseResource;
use App\Domains\Purchase\Services\CheckoutService;
use App\Domains\Tenant\Models\Tenant;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class PurchaseController extends Controller
{
public function index(Request $request, Tenant $tenant): JsonResponse
{
return PurchaseResource::collection(
Purchase::query()
->where('tenant_codigo', $tenant->codigo)
->where('user_id', $request->user()->id)
->when($request->query('status'), function ($query, $status) {
$query->where('status', $status);
})
->latest()
->paginateFromRequest()
)->response();
}
public function startCheckout(
StartCheckoutRequest $request,
Tenant $tenant,
CheckoutService $checkoutService,
): JsonResponse {
$data = $request->validated();
$purchase = $checkoutService->startCheckout(
$tenant,
$request->user()->id,
$data
);
return PurchaseResource::make($purchase)->response()->setStatusCode(201);
}
public function show(Request $request, Tenant $tenant, Purchase $compra): PurchaseResource
{
$compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);
$compra->loadMissing('items');
$compra->items->load('imageAttachment');
return PurchaseResource::make($compra);
}
public function updateCustomerData(
UpdatePurchaseCustomerRequest $request,
Tenant $tenant,
Purchase $compra,
CheckoutService $checkoutService,
): PurchaseResource {
$compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);
return PurchaseResource::make(
$checkoutService->updateCustomerData($compra, $request->validated()),
);
}
public function updateItemQuantity(
UpdatePurchaseItemQuantityRequest $request,
Tenant $tenant,
Purchase $compra,
PurchaseItem $item,
CheckoutService $checkoutService,
): PurchaseResource {
$compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);
return PurchaseResource::make(
$checkoutService->updateItemQuantity(
$compra,
$item,
(int) $request->validated('quantity'),
),
);
}
public function prepareItemEditing(
Request $request,
Tenant $tenant,
Purchase $compra,
CheckoutService $checkoutService,
): PurchaseResource {
$compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);
return PurchaseResource::make(
$checkoutService->prepareItemEditing($compra),
);
}
public function paymentIntent(PaymentIntentRequest $request, Tenant $tenant, Purchase $compra): JsonResponse
{
$compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);
$method = $request->validated('method');
$totalAmount = $compra->calculateCurrentTotalAmount();
$purchaseUpdate = [
'payment_method' => $method,
'status' => Purchase::STATUS_PENDING_PAYMENT,
'expires_at' => now()->addMinutes(
max(1, (int) config("purchase.payment_expiration_minutes.{$method}", 30))
),
'total' => $totalAmount,
];
if ($method === 'transfer') {
$purchaseUpdate['transfer_payer_dni'] = preg_replace('/\D+/', '', (string) $request->validated('transfer_payer_dni'));
}
$updated = Purchase::query()
->whereKey($compra->getKey())
->whereIn('status', [Purchase::STATUS_CREATED, Purchase::STATUS_PENDING_PAYMENT])
->where(function ($query): void {
$query->whereNull('expires_at')
->orWhere('expires_at', '>', now());
})
->update($purchaseUpdate);
if ($updated === 0) {
throw ValidationException::withMessages([
'purchase' => __('api.purchase.not_available_for_payment'),
]);
}
$compra->refresh();
if ($method === 'transfer') {
$telepagosService = new TelepagosIntegrationService;
$telepagosService->forTenant($tenant->codigo);
try {
$accountInfo = $telepagosService->getAccountInfo();
return response()->json([
'payment_method' => 'transfer',
'transfer_data' => [
'titular' => $accountInfo['holder'] ?? null,
'cvu' => $accountInfo['cvu'] ?? null,
'alias' => $accountInfo['alias'] ?? null,
'cuit' => $accountInfo['cuit'] ?? null,
'entidad' => $accountInfo['entity'] ?? 'Telepagos S.A.',
],
]);
} catch (\Exception $e) {
Log::error('Unable to retrieve TelePagos account information.', [
'purchase_id' => $compra->id,
'exception' => $e,
]);
return response()->json([
'code' => 'integration.account_info_failed',
'message' => __('api.integration.account_info_failed'),
], 500);
}
}
if ($method === 'qr') {
$telepagosService = new TelepagosIntegrationService;
$telepagosService->forTenant($tenant->codigo);
try {
Log::info("Generating QR for purchase ID: {$compra->id}, amount: {$totalAmount}");
$qrResponse = $telepagosService->generateQr(
$totalAmount,
'Compra',
"Compra #{$compra->id}"
);
$telepagosQr = $compra->telepagosQr()->create([
'qr_order_id' => (string) ($qrResponse['qr_order_id'] ?? ''),
'qr_code' => $qrResponse['qr_code'] ?? '',
]);
} catch (\Exception $e) {
Log::error('Unable to generate TelePagos QR code.', [
'purchase_id' => $compra->id,
'exception' => $e,
]);
return response()->json([
'code' => 'integration.qr_generation_failed',
'message' => __('api.integration.qr_generation_failed'),
], 500);
}
return response()->json([
'payment_method' => 'qr',
'qr_data' => [
'qr_code' => $telepagosQr->qr_code,
'qr_order_id' => $telepagosQr->qr_order_id,
],
]);
}
return response()->json([
'code' => 'integration.invalid_payment_method',
'message' => __('api.integration.invalid_payment_method'),
], 400);
}
public function complete(Request $request, Tenant $tenant, Purchase $compra, CheckoutService $checkoutService): PurchaseResource
{
$compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);
return PurchaseResource::make(
$checkoutService->completePurchase($compra)
);
}
public function cancel(Request $request, Tenant $tenant, Purchase $compra, CheckoutService $checkoutService): PurchaseResource
{
$compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);
return PurchaseResource::make(
$checkoutService->cancelPurchase($compra)
);
}
protected function resolveScopedPurchase(Tenant $tenant, int $userId, Purchase $purchase): Purchase
{
if ($purchase->tenant_codigo !== $tenant->codigo || $purchase->user_id !== $userId) {
throw new NotFoundHttpException(__('api.errors.not_found'));
}
return $purchase;
}
}