feat: add total field to purchases and update related logic for payment processing
This commit is contained in:
246
tests/Feature/Integration/TelepagosWebhookTest.php
Normal file
246
tests/Feature/Integration/TelepagosWebhookTest.php
Normal file
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Integration;
|
||||
|
||||
use App\Domains\Auth\Models\User;
|
||||
use App\Domains\Cart\Models\Cart;
|
||||
use App\Domains\Catalog\Models\Product;
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use App\Domains\Integration\Models\Integration;
|
||||
use App\Domains\Integration\Models\TenantIntegration;
|
||||
use App\Domains\Purchase\Models\Purchase;
|
||||
use App\Domains\Purchase\Services\CheckoutService;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Tests\TestCase;
|
||||
|
||||
class TelepagosWebhookTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
config(['services.integrations.secret' => 'base64:' . base64_encode(random_bytes(32))]);
|
||||
Cache::flush();
|
||||
}
|
||||
|
||||
public function test_transfer_webhook_matches_pending_purchase_by_dni_and_total_amount(): void
|
||||
{
|
||||
$tenant = $this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
|
||||
$this->configureTelepagosIntegration($tenant);
|
||||
|
||||
$matchingUser = User::factory()->create([
|
||||
'email' => 'buyer@example.com',
|
||||
]);
|
||||
$newerUser = User::factory()->create([
|
||||
'email' => 'buyer-2@example.com',
|
||||
]);
|
||||
|
||||
$variant = $this->createVariantForTenant('sonder', 10, '50.00');
|
||||
|
||||
$matchingPurchase = $this->createPendingTransferPurchase(
|
||||
$tenant,
|
||||
$matchingUser->id,
|
||||
$variant->id,
|
||||
1,
|
||||
'12345678'
|
||||
);
|
||||
|
||||
$newerPurchase = $this->createPendingTransferPurchase(
|
||||
$tenant,
|
||||
$newerUser->id,
|
||||
$variant->id,
|
||||
2,
|
||||
'12345678'
|
||||
);
|
||||
|
||||
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/payment/cashin/6351' => Http::response([
|
||||
'status' => 'ok',
|
||||
'data' => [
|
||||
'amount' => 50,
|
||||
'operation_id' => 1,
|
||||
'transaction_id' => 'tx-123',
|
||||
'buyer' => [
|
||||
'cuit' => '20123456789',
|
||||
],
|
||||
],
|
||||
]),
|
||||
]);
|
||||
|
||||
$this->postJson('/api/webhooks/telepagos/sonder', [
|
||||
'id' => '6351',
|
||||
])->assertOk()->assertJsonPath('status', 'success');
|
||||
|
||||
$this->assertDatabaseHas('compras', [
|
||||
'id' => $matchingPurchase->id,
|
||||
'status' => 'paid',
|
||||
'payment_status' => 'approved',
|
||||
'payment_method' => 'transfer',
|
||||
'total' => 50,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('compras', [
|
||||
'id' => $newerPurchase->id,
|
||||
'status' => 'pending',
|
||||
'payment_status' => 'pending',
|
||||
'payment_method' => 'transfer',
|
||||
'total' => 100,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('telepagos_payments', [
|
||||
'compra_id' => $matchingPurchase->id,
|
||||
'amount' => 50,
|
||||
'operation_id' => 1,
|
||||
'transaction_id' => 'tx-123',
|
||||
]);
|
||||
|
||||
$this->assertDatabaseMissing('telepagos_payments', [
|
||||
'compra_id' => $newerPurchase->id,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('compra_items', [
|
||||
'compra_id' => $matchingPurchase->id,
|
||||
'producto_variante_id' => $variant->id,
|
||||
'cantidad' => 1,
|
||||
'total' => 50,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseMissing('compra_items', [
|
||||
'compra_id' => $newerPurchase->id,
|
||||
]);
|
||||
|
||||
$this->assertSoftDeleted('carritos', [
|
||||
'id' => $matchingPurchase->cart_id,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createPendingTransferPurchase(
|
||||
Tenant $tenant,
|
||||
int $userId,
|
||||
int $variantId,
|
||||
int $quantity,
|
||||
string $dni,
|
||||
): Purchase {
|
||||
$cart = Cart::query()->create([
|
||||
'tenant_codigo' => $tenant->codigo,
|
||||
'user_id' => $userId,
|
||||
'status' => 'active',
|
||||
]);
|
||||
|
||||
$cart->addItem($variantId, $quantity);
|
||||
|
||||
/** @var CheckoutService $checkoutService */
|
||||
$checkoutService = app(CheckoutService::class);
|
||||
|
||||
$purchase = $checkoutService->startCheckout($tenant, $userId, [
|
||||
'cart_id' => $cart->id,
|
||||
'dni' => $dni,
|
||||
'telefono' => '+54 9 341 555-4321',
|
||||
'nombre_apellido' => 'Juan Perez',
|
||||
'email' => 'juan.perez@example.com',
|
||||
]);
|
||||
|
||||
$purchase->update([
|
||||
'payment_method' => 'transfer',
|
||||
]);
|
||||
|
||||
return $purchase->fresh();
|
||||
}
|
||||
|
||||
private function configureTelepagosIntegration(Tenant $tenant): void
|
||||
{
|
||||
Integration::create([
|
||||
'integration_code' => 'telepagos_homo',
|
||||
'name' => 'Telepagos',
|
||||
'url' => 'https://api.telepagos.com.ar',
|
||||
'integration_data_schema' => [
|
||||
'username' => 'required|string',
|
||||
'password' => 'required|string',
|
||||
],
|
||||
]);
|
||||
|
||||
TenantIntegration::create([
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'integration_code' => 'telepagos_homo',
|
||||
'integration_data' => [
|
||||
'username' => 'user123',
|
||||
'password' => 'pass123',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
private function createVariantForTenant(
|
||||
string $tenantCode,
|
||||
int $stock,
|
||||
string $price,
|
||||
string $slugPrefix = 'shirt',
|
||||
): ProductVariant {
|
||||
$category = \App\Domains\Catalog\Models\Category::query()->create([
|
||||
'tenant_code' => $tenantCode,
|
||||
'nombre' => "{$slugPrefix} category {$tenantCode}",
|
||||
]);
|
||||
|
||||
$product = Product::query()->create([
|
||||
'tenant_codigo' => $tenantCode,
|
||||
'categoria_id' => $category->id,
|
||||
'slug' => "{$slugPrefix}-{$tenantCode}-".Product::query()->count(),
|
||||
'nombre' => ucfirst($slugPrefix)." {$tenantCode}",
|
||||
'descripcion' => 'Test product',
|
||||
'precio' => $price,
|
||||
]);
|
||||
|
||||
return ProductVariant::query()->create([
|
||||
'producto_id' => $product->id,
|
||||
'slug' => "{$slugPrefix}-variant-".ProductVariant::query()->count(),
|
||||
'nombre' => ucfirst($slugPrefix).' Variant',
|
||||
'stock' => $stock,
|
||||
'descripcion' => 'Test variant',
|
||||
'precio' => $price,
|
||||
])->load('product');
|
||||
}
|
||||
|
||||
private function createTenant(string $codigo, string $nombre, string $dominio): Tenant
|
||||
{
|
||||
$hdrKey = (string) \Illuminate\Support\Str::uuid();
|
||||
$ftrKey = (string) \Illuminate\Support\Str::uuid();
|
||||
|
||||
$headerAttachment = \App\Domains\Attachable\Models\Attachment::create([
|
||||
'key' => $hdrKey,
|
||||
'path' => 'tenants/' . $hdrKey . '.png',
|
||||
'filename' => 'logo_header.png',
|
||||
'type' => \App\Domains\Attachable\Enums\AttachmentType::Image,
|
||||
'mime_type' => 'image/png',
|
||||
]);
|
||||
$footerAttachment = \App\Domains\Attachable\Models\Attachment::create([
|
||||
'key' => $ftrKey,
|
||||
'path' => 'tenants/' . $ftrKey . '.png',
|
||||
'filename' => 'logo_footer.png',
|
||||
'type' => \App\Domains\Attachable\Enums\AttachmentType::Image,
|
||||
'mime_type' => 'image/png',
|
||||
]);
|
||||
|
||||
return Tenant::create([
|
||||
'codigo' => $codigo,
|
||||
'nombre' => $nombre,
|
||||
'dominio' => $dominio,
|
||||
'primary_color' => '#111111',
|
||||
'secondary_color' => '#222222',
|
||||
'danger_color' => '#333333',
|
||||
'success_color' => '#28a745',
|
||||
'header_bg_color' => '#444444',
|
||||
'footer_bg_color' => '#444444',
|
||||
'header_logo_id' => $headerAttachment->id,
|
||||
'footer_logo_id' => $footerAttachment->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,8 @@ class StorePurchaseTest extends TestCase
|
||||
$response->assertJsonPath('data.email', 'juan.perez@example.com');
|
||||
$response->assertJsonPath('data.tenant_codigo', 'sonder');
|
||||
$response->assertJsonPath('data.items', []);
|
||||
$response->assertJsonPath('data.total', '0.00');
|
||||
$response->assertJsonPath('data.subtotal', '100.00');
|
||||
$response->assertJsonPath('data.total', '100.00');
|
||||
|
||||
$purchaseId = $response->json('data.id');
|
||||
|
||||
@@ -88,6 +89,7 @@ class StorePurchaseTest extends TestCase
|
||||
'telefono' => '+54 9 341 555-4321',
|
||||
'nombre_apellido' => 'Juan Perez',
|
||||
'email' => 'juan.perez@example.com',
|
||||
'total' => 100,
|
||||
]);
|
||||
$this->assertDatabaseMissing('compra_items', [
|
||||
'compra_id' => $purchaseId,
|
||||
@@ -97,10 +99,6 @@ class StorePurchaseTest extends TestCase
|
||||
'status' => 'active',
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
$this->assertDatabaseMissing('carritos', [
|
||||
'id' => $cartId,
|
||||
'deleted_at' => null,
|
||||
]);
|
||||
$this->assertDatabaseHas('carrito_items', [
|
||||
'cart_id' => $cartId,
|
||||
'producto_variante_id' => $variant->id,
|
||||
|
||||
Reference in New Issue
Block a user