Compare commits

...

5 Commits

15 changed files with 145 additions and 22 deletions

View File

@@ -12,9 +12,9 @@ class CatalogController extends Controller
public function index(string $tenant): JsonResponse
{
$featuredGroups = FeaturedGroup::where('tenant_codigo', $tenant)
->with(['featuredVariants' => function ($query) {
->with(['groupItems' => function ($query) {
$query->orderBy('order');
}, 'featuredVariants.variant.product', 'featuredVariants.variant.attachments', 'featuredVariants.variant.product.attachments'])
}, 'groupItems.variant.product', 'groupItems.variant.attachments', 'groupItems.variant.product.attachments'])
->orderBy('group_order')
->get();

View File

@@ -32,7 +32,7 @@ class FeaturedGroupController extends Controller
public function show(string $tenant, FeaturedGroup $featuredGroup): FeaturedGroupResource
{
abort_if($featuredGroup->tenant_codigo !== $tenant, 404);
return new FeaturedGroupResource($featuredGroup->load('featuredVariants'));
return new FeaturedGroupResource($featuredGroup->load('groupItems'));
}
public function update(UpdateFeaturedGroupRequest $request, string $tenant, FeaturedGroup $featuredGroup): FeaturedGroupResource

View File

@@ -3,7 +3,7 @@
namespace App\Domains\Catalog\Controllers;
use App\Domains\Catalog\Models\FeaturedGroup;
use App\Domains\Catalog\Models\FeaturedVariant;
use App\Domains\Catalog\Models\GroupItem;
use App\Domains\Catalog\Requests\StoreFeaturedVariantRequest;
use App\Domains\Catalog\Requests\UpdateFeaturedVariantRequest;
use App\Domains\Catalog\Resources\FeaturedVariantResource;
@@ -16,24 +16,24 @@ class FeaturedVariantController extends Controller
public function index(string $tenant, FeaturedGroup $featuredGroup): AnonymousResourceCollection
{
abort_if($featuredGroup->tenant_codigo !== $tenant, 404);
return FeaturedVariantResource::collection($featuredGroup->featuredVariants);
return FeaturedVariantResource::collection($featuredGroup->groupItems);
}
public function store(StoreFeaturedVariantRequest $request, string $tenant, FeaturedGroup $featuredGroup): FeaturedVariantResource
{
abort_if($featuredGroup->tenant_codigo !== $tenant, 404);
$variant = $featuredGroup->featuredVariants()->create($request->validated());
$variant = $featuredGroup->groupItems()->create($request->validated());
return new FeaturedVariantResource($variant);
}
public function update(UpdateFeaturedVariantRequest $request, string $tenant, FeaturedGroup $featuredGroup, FeaturedVariant $featuredVariant): FeaturedVariantResource
public function update(UpdateFeaturedVariantRequest $request, string $tenant, FeaturedGroup $featuredGroup, GroupItem $featuredVariant): FeaturedVariantResource
{
abort_if($featuredGroup->tenant_codigo !== $tenant || $featuredVariant->featured_group_id !== $featuredGroup->id, 404);
$featuredVariant->update($request->validated());
return new FeaturedVariantResource($featuredVariant);
}
public function destroy(string $tenant, FeaturedGroup $featuredGroup, FeaturedVariant $featuredVariant): JsonResponse
public function destroy(string $tenant, FeaturedGroup $featuredGroup, GroupItem $featuredVariant): JsonResponse
{
abort_if($featuredGroup->tenant_codigo !== $tenant || $featuredVariant->featured_group_id !== $featuredGroup->id, 404);
$featuredVariant->delete();

View File

@@ -14,8 +14,8 @@ class FeaturedGroup extends Model
'group_order',
];
public function featuredVariants(): HasMany
public function groupItems(): HasMany
{
return $this->hasMany(FeaturedVariant::class);
return $this->hasMany(GroupItem::class);
}
}

View File

@@ -2,13 +2,12 @@
namespace App\Domains\Catalog\Models;
use App\Domains\Catalog\Models\ProductVariant;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class FeaturedVariant extends Model
class GroupItem extends Model
{
protected $table = 'featured_variants';
protected $table = 'group_items';
protected $fillable = [
'featured_group_id',

View File

@@ -4,7 +4,7 @@ namespace App\Domains\Catalog\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Domains\Catalog\Models\FeaturedVariant;
use App\Domains\Catalog\Models\GroupItem;
class CatalogFeaturedGroupResource extends JsonResource
{
@@ -15,9 +15,9 @@ class CatalogFeaturedGroupResource extends JsonResource
'title' => $this->group_name,
'layout' => $this->product_layout,
'group_order' => $this->group_order,
'items' => $this->whenLoaded('featuredVariants', function () use ($request) {
return $this->featuredVariants->map(function (FeaturedVariant $featuredVariant) use ($request) {
$variant = $featuredVariant->variant;
'items' => $this->whenLoaded('groupItems', function () use ($request) {
return $this->groupItems->map(function (GroupItem $groupItem) use ($request) {
$variant = $groupItem->variant;
$variantResource = (new ProductVariantResource($variant))->toArray($request);
if ($this->product_layout === 'row' || $this->product_layout === 'column_with_cart' || $this->product_layout === 'vertical_with_cart') {

View File

@@ -15,7 +15,7 @@ class FeaturedGroupResource extends JsonResource
'group_name' => $this->group_name,
'product_layout' => $this->product_layout,
'group_order' => $this->group_order,
'featured_variants' => FeaturedVariantResource::collection($this->whenLoaded('featuredVariants')),
'featured_variants' => FeaturedVariantResource::collection($this->whenLoaded('groupItems')),
];
}
}

View File

@@ -54,7 +54,7 @@ class TelepagosWebhookService
$dni = substr($cuit, 2, -1);
$compra = Purchase::where('tenant_codigo', $tenantCodigo)
->whereRaw("REPLACE(dni, '.', '') = ?", [$dni])
->where('transfer_payer_dni', $dni)
->whereIn('status', [Purchase::STATUS_CREATED, Purchase::STATUS_PENDING_PAYMENT])
->where('payment_method', 'transfer')
->where('total', $amount)

View File

@@ -68,10 +68,17 @@ class PurchaseController extends Controller
$method = $request->validated('method');
$totalAmount = $compra->calculateCurrentTotalAmount();
$compra->update([
$purchaseUpdate = [
'payment_method' => $method,
'status' => Purchase::STATUS_PENDING_PAYMENT,
'total' => $totalAmount,
]);
];
if ($method === 'transfer') {
$purchaseUpdate['transfer_payer_dni'] = preg_replace('/\D+/', '', (string) $request->validated('transfer_payer_dni'));
}
$compra->update($purchaseUpdate);
if ($method === 'transfer') {
$telepagosService = new TelepagosIntegrationService;

View File

@@ -20,6 +20,7 @@ use Illuminate\Database\Eloquent\Relations\HasOne;
'payment_method',
'total',
'dni',
'transfer_payer_dni',
'telefono',
'nombre_apellido',
'email',

View File

@@ -19,6 +19,11 @@ class PaymentIntentRequest extends FormRequest
{
return [
'method' => ['required', 'string', Rule::in(['qr', 'transfer'])],
'transfer_payer_dni' => [
Rule::requiredIf(fn (): bool => $this->input('method') === 'transfer'),
'string',
'regex:/^\d{7,8}$/',
],
];
}
}

View File

@@ -44,6 +44,7 @@ class PurchaseResource extends JsonResource
'status' => $this->status,
'payment_method' => $this->payment_method,
'dni' => $this->dni,
'transfer_payer_dni' => $this->transfer_payer_dni,
'telefono' => $this->telefono,
'nombre_apellido' => $this->nombre_apellido,
'email' => $this->email,

View File

@@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('compras', function (Blueprint $table) {
$table->string('transfer_payer_dni', 8)->nullable()->after('dni');
});
}
public function down(): void
{
Schema::table('compras', function (Blueprint $table) {
$table->dropColumn('transfer_payer_dni');
});
}
};

View File

@@ -0,0 +1,23 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::rename('featured_variants', 'group_items');
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::rename('group_items', 'featured_variants');
}
};

View File

@@ -33,6 +33,70 @@ class TelepagosWebhookTest extends TestCase
Cache::flush();
}
public function test_transfer_payment_intent_requires_a_valid_transfer_payer_dni(): void
{
$tenant = $this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
$user = User::factory()->create();
$variant = $this->createVariantForTenant('sonder', 10, '50.00');
$purchase = $this->createPendingTransferPurchase($tenant, $user->id, $variant->id, 1, '12345678');
$this->actingAs($user, 'sanctum')
->postJson("/api/tenants/sonder/compras/{$purchase->id}/payment-intent", [
'method' => 'transfer',
])
->assertUnprocessable()
->assertJsonValidationErrors(['transfer_payer_dni']);
$this->actingAs($user, 'sanctum')
->postJson("/api/tenants/sonder/compras/{$purchase->id}/payment-intent", [
'method' => 'transfer',
'transfer_payer_dni' => '12.345.678',
])
->assertUnprocessable()
->assertJsonValidationErrors(['transfer_payer_dni']);
}
public function test_transfer_payment_intent_persists_transfer_payer_dni_without_replacing_customer_dni(): void
{
$tenant = $this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
$this->configureTelepagosIntegration($tenant);
$user = User::factory()->create();
$variant = $this->createVariantForTenant('sonder', 10, '50.00');
$purchase = $this->createPendingTransferPurchase($tenant, $user->id, $variant->id, 1, '12345678');
$purchase->update(['status' => Purchase::STATUS_CREATED]);
Http::fake([
'https://api.telepagos.com.ar/v2/auth/token' => Http::response([
'status' => 'ok',
'token' => 'test-token',
'expires_at' => now()->addHour()->toIso8601String(),
]),
'https://api.telepagos.com.ar/v2/account/info' => Http::response([
'status' => 'ok',
'holder' => 'Telepagos Test',
'cvu' => '0000003100000000000001',
'alias' => 'telepagos.test',
'entity' => 'Telepagos S.A.',
]),
]);
$this->actingAs($user, 'sanctum')
->postJson("/api/tenants/sonder/compras/{$purchase->id}/payment-intent", [
'method' => 'transfer',
'transfer_payer_dni' => '23456789',
])
->assertOk()
->assertJsonPath('transfer_data.alias', 'telepagos.test');
$this->assertDatabaseHas('compras', [
'id' => $purchase->id,
'dni' => '87654321',
'transfer_payer_dni' => '23456789',
'payment_method' => 'transfer',
'status' => Purchase::STATUS_PENDING_PAYMENT,
]);
}
public function test_transfer_webhook_matches_pending_purchase_by_dni_and_total_amount(): void
{
$tenant = $this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
@@ -209,7 +273,7 @@ class TelepagosWebhookTest extends TestCase
$purchase = $checkoutService->startCheckout($tenant, $userId, [
'cart_id' => $cart->id,
'dni' => $dni,
'dni' => '87654321',
'telefono' => '+54 9 341 555-4321',
'nombre_apellido' => 'Juan Perez',
'email' => 'juan.perez@example.com',
@@ -217,6 +281,7 @@ class TelepagosWebhookTest extends TestCase
$purchase->update([
'payment_method' => 'transfer',
'transfer_payer_dni' => $dni,
]);
$checkoutService->completePurchase($purchase);