PHPackages                             jamesmosq/pasarelas-pago-simulador - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. jamesmosq/pasarelas-pago-simulador

ActiveLibrary[Payment Processing](/categories/payments)

jamesmosq/pasarelas-pago-simulador
==================================

Simulador de pasarelas de pago para Laravel — prueba Wompi, PayU y pasarelas genéricas en local sin credenciales reales

00PHP

Since May 31Pushed 1w agoCompare

[ Source](https://github.com/jamesmosq/pasarelas-pago-simulador-demo)[ Packagist](https://packagist.org/packages/jamesmosq/pasarelas-pago-simulador)[ RSS](/packages/jamesmosq-pasarelas-pago-simulador/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependenciesVersions (1)Used By (0)

Payment Simulator
=================

[](#payment-simulator)

> El Mailtrap de los pagos. Paquete Laravel para simular Wompi, PayU y pasarelas genéricas en local con webhooks reales, números mágicos y panel de administración.

---

Instalación
-----------

[](#instalación)

```
composer require jamesmosq/pasarelas-pago-simulador --dev
```

El paquete se registra automáticamente vía Laravel Package Auto-Discovery. Publica la config y corre las migraciones:

```
php artisan vendor:publish --tag=payment-simulator-config
php artisan migrate
php artisan payment:seed-products   # opcional: 6 productos de prueba
```

**Requisitos:** PHP 8.2+, Laravel 11+, cualquier driver de base de datos, queue driver `database` o `redis`.

---

Configuración básica
--------------------

[](#configuración-básica)

Añade a tu `.env`:

```
PAYMENT_DRIVER=simulator
PAYMENT_WEBHOOK_URL=https://tu-app.test/webhooks/payment
PAYMENT_MERCHANT_NAME="Mi Comercio"
```

El paquete está **bloqueado en producción**. Si `APP_ENV` no es `local` o `testing`, el panel y el checkout devuelven `403` aunque `PAYMENT_DRIVER=simulator` esté configurado.

---

Uso básico
----------

[](#uso-básico)

```
use JamesMosq\PaymentSimulator\Facades\PaymentSimulator;

$checkoutUrl = PaymentSimulator::createPayment([
    'reference'      => 'ORDER-' . $order->id,
    'amount'         => $order->total_in_cents,   // centavos
    'return_url'     => route('orders.show', $order),
    'webhook_url'    => route('webhooks.payment'),
    'customer_email' => $user->email,
    'payment_method' => 'CARD', // CARD | PSE | NEQUI | DAVIPLATA | EFECTY | BALOTO
]);

return redirect($checkoutUrl);
```

El usuario paga en el checkout del simulador y regresa a `return_url`. Tu webhook recibe el payload exacto de Wompi o PayU.

---

Tarjetas mágicas
----------------

[](#tarjetas-mágicas)

NúmeroMarcaResultadoRazón de rechazo4242424242424242VisaAPPROVED—4111111111111111VisaAPPROVED—5555555555554444MastercardAPPROVED—378282246310005AmexAPPROVED—4000000000000002VisaDECLINEDINSUFFICIENT\_FUNDS4000000000000069VisaDECLINEDEXPIRED\_CARD4000000000000119VisaDECLINEDPROCESSING\_ERROR4000000000009995VisaDECLINED (permanente)STOLEN\_CARD4000000000000127VisaDECLINEDSECURITY\_VIOLATION4000000000000341VisaDECLINEDLIMIT\_EXCEEDED5200828282828210MastercardDECLINEDINSUFFICIENT\_FUNDS4000000000003220Visa3DS — autenticado—4000000000003063Visa3DS — fallidoAUTHENTICATION\_FAILED4000000000003238Visa3DS — frictionless—4000000000000259VisaAPPROVED + chargebackChargeback automático a los 30sCualquier otro número: **APPROVED** por defecto.

---

Cédulas / NITs mágicos (PSE)
----------------------------

[](#cédulas--nits-mágicos-pse)

DocumentoResultadoRazón1234567890APPROVED—9001234567APPROVED—9876543210DECLINEDACCOUNT\_NOT\_FOUND1111111111PENDINGBANK\_NOT\_AVAILABLE2222222222DECLINEDINSUFFICIENT\_FUNDS3333333333DECLINEDBLOCKED\_ACCOUNTCualquier otro documento: **APPROVED** por defecto.

---

Teléfonos mágicos (NEQUI / Daviplata)
-------------------------------------

[](#teléfonos-mágicos-nequi--daviplata)

TeléfonoResultadoDelayRazón3001234567APPROVED5s—3009999999DECLINED3sUSER\_REJECTED3008888888DECLINED120sTIMEOUT3007777777DECLINED3sINSUFFICIENT\_FUNDS3006666666DECLINED3sMOBILE\_NOT\_ACTIVECualquier otro teléfono: **APPROVED** en ~8s por defecto.

---

Métodos de pago
---------------

[](#métodos-de-pago)

MétodoDescripciónMonto mínimoCARDTarjeta crédito/débito con cuotas y 3DS$1.000 COPPSEDébito bancario (resolución asíncrona)$1.000 COPNEQUIBilletera digital (polling SSE)$1.000 COPDAVIPLATABilletera digital (polling SSE)$1.000 COPEFECTYEfectivo (referencia generada)$2.000 COPBALOTOBancolombia Collect$2.000 COP---

Cuotas
------

[](#cuotas)

Por defecto: `1, 2, 3, 6, 12, 24, 36`. Tasa simulada: 1.8% mensual.

```
PaymentSimulator::createPayment([
    'reference'    => 'ORDER-1',
    'amount'       => 600000,
    'installments' => 6,
    'return_url'   => '...',
]);
```

---

Tokenización
------------

[](#tokenización)

```
// 1. Guardar tarjeta al pagar
PaymentSimulator::createPayment([
    'reference' => 'ORDER-1',
    'amount'    => 50000,
    'return_url' => '...',
    'metadata'  => ['save_card' => true],
]);

// 2. Recuperar el token
$token = PaymentSimulator::getStatus('ORDER-1')['token'];
// "sim_tok_xxxxxxxxxxxxxxxxxxxx"

// 3. Cobros futuros sin checkout
PaymentSimulator::chargeToken($token, [
    'reference' => 'ORDER-2',
    'amount'    => 50000,
    'return_url' => '...',
]);
```

---

Pre-autorización y captura
--------------------------

[](#pre-autorización-y-captura)

```
$url = PaymentSimulator::authorize([
    'reference'  => 'HOTEL-1',
    'amount'     => 200000,
    'return_url' => '...',
]);

PaymentSimulator::capture('HOTEL-1');           // Captura total
PaymentSimulator::capture('HOTEL-1', 150000);   // Captura parcial
```

Las pre-autorizaciones no capturadas expiran automáticamente (scheduler diario).

---

Void vs Refund
--------------

[](#void-vs-refund)

AcciónCuándoResultadoVoidMismo día, antes de liquidarVOIDEDRefundDespués de liquidación (≥1 día)REFUNDED```
PaymentSimulator::void('ORDER-1');
PaymentSimulator::refund('ORDER-1');
PaymentSimulator::refund('ORDER-1', 30000);   // parcial
```

---

Webhooks
--------

[](#webhooks)

Los payloads replican exactamente el formato de cada pasarela:

**Wompi:**

```
{
  "event": "transaction.updated",
  "data": {
    "transaction": {
      "id": "uuid",
      "reference": "ORDER-1",
      "status": "APPROVED",
      "amount_in_cents": 50000
    }
  },
  "environment": "test",
  "signature": { "checksum": "sha256hex" }
}
```

**PayU:**

```
{
  "state_pol": "4",
  "reference_sale": "ORDER-1",
  "value": "500.00",
  "currency": "COP",
  "sign": "md5hash"
}
```

**Reintentos automáticos** cuando tu endpoint responde con un código no-2xx:

IntentoDelay15 minutos215 minutos31 hora46 horas524 horas---

Panel de administración
-----------------------

[](#panel-de-administración)

Disponible en `http://tu-app.test/payment-simulator` (solo en entorno local).

SecciónURLTransacciones`/payment-simulator`Inspector`/payment-simulator/transactions/{id}`Links de pago`/payment-simulator/payment-links`Tokens`/payment-simulator/tokens`Productos`/payment-simulator/products`Reportes CSV`/payment-simulator/reports`Estadísticas`/payment-simulator/stats`El inspector permite aprobar, rechazar, anular, reembolsar, capturar, forzar chargeback, reenviar webhook y ver el audit log en tiempo real.

---

Comandos Artisan
----------------

[](#comandos-artisan)

**Acciones manuales:**

```
php artisan payment:approve {reference}
php artisan payment:reject {reference} {--reason=PROCESSING_ERROR}
php artisan payment:void {reference}
php artisan payment:refund {reference} {--amount=}
php artisan payment:capture {reference} {--amount=}
php artisan payment:chargeback {reference} {--reason=FRAUD_DISPUTE}
php artisan payment:charge-token {token} --amount= --reference=
```

**Schedulers** (se registran automáticamente vía ServiceProvider):

```
php artisan payment:expire-pending        # cada minuto
php artisan payment:settle-transactions   # cada hora
php artisan payment:expire-pre-auths      # diario
php artisan payment:retry-webhooks        # cada 5 minutos
```

**Mantenimiento:**

```
php artisan payment:list {--status=} {--gateway=} {--limit=20}
php artisan payment:clear {--days=7} {--force}
php artisan payment:seed-products
php artisan payment:reset-fraud
```

---

Testing
-------

[](#testing)

### Setup

[](#setup)

```
use JamesMosq\PaymentSimulator\Facades\PaymentSimulator;
use JamesMosq\PaymentSimulator\Testing\InteractsWithPaymentSimulator;

class OrderPaymentTest extends TestCase
{
    use RefreshDatabase;
    use InteractsWithPaymentSimulator; // resetea estado entre tests

    protected function setUp(): void
    {
        parent::setUp();
        Http::fake(['*' => Http::response('OK', 200)]);
        Queue::fake();
    }
}
```

### Forzar resultados

[](#forzar-resultados)

```
$this->approveNextPayment();                         // todos aprobados
$this->declineNextPayment('INSUFFICIENT_FUNDS');      // todos rechazados

PaymentSimulator::approveReference('ORDER-42');       // solo esa referencia
PaymentSimulator::rejectReference('ORDER-43', 'EXPIRED_CARD');
PaymentSimulator::reset();                            // limpiar estado
```

### Aserciones

[](#aserciones)

```
// Estado de la transacción
PaymentSimulator::assertApproved('ORDER-1');
PaymentSimulator::assertDeclined('ORDER-1');
PaymentSimulator::assertDeclinedWith('ORDER-1', 'INSUFFICIENT_FUNDS');
PaymentSimulator::assertAuthorized('ORDER-1');
PaymentSimulator::assertCaptured('ORDER-1');
PaymentSimulator::assertRefunded('ORDER-1');
PaymentSimulator::assertVoided('ORDER-1');
PaymentSimulator::assertChargeback('ORDER-1');
PaymentSimulator::assertExpired('ORDER-1');

// Webhooks
PaymentSimulator::assertWebhookSent('ORDER-1');
PaymentSimulator::assertWebhookNotSent('ORDER-1');
PaymentSimulator::assertWebhookAttempts('ORDER-1', 2);
PaymentSimulator::assertWebhookPayloadContains('ORDER-1', [
    'data.transaction.status' => 'APPROVED',
]);

// 3DS, cuotas, tokens, email
PaymentSimulator::assertThreeDsAuthenticated('ORDER-1');
PaymentSimulator::assertInstallments('ORDER-1', 6);
PaymentSimulator::assertCardTokenCreated('ORDER-1');
PaymentSimulator::assertReceiptEmailSent('ORDER-1');

// Audit log
PaymentSimulator::assertEventLogged('ORDER-1', 'status_changed');
PaymentSimulator::assertActorWas('ORDER-1', 'artisan');

// Inspección
$tx     = PaymentSimulator::find('ORDER-1');
$events = PaymentSimulator::events('ORDER-1');
```

### Ejemplo de test completo

[](#ejemplo-de-test-completo)

```
public function test_order_is_confirmed_after_payment(): void
{
    $this->approveNextPayment();
    $order = Order::factory()->create();

    $this->post('/orders/' . $order->id . '/pay');

    PaymentSimulator::assertApproved('ORDER-' . $order->id);
    PaymentSimulator::assertWebhookSent('ORDER-' . $order->id);
    $this->assertDatabaseHas('orders', ['id' => $order->id, 'status' => 'paid']);
}

public function test_order_stays_pending_on_declined_payment(): void
{
    $this->declineNextPayment('INSUFFICIENT_FUNDS');
    $order = Order::factory()->create();

    $this->post('/orders/' . $order->id . '/pay');

    PaymentSimulator::assertDeclinedWith('ORDER-' . $order->id, 'INSUFFICIENT_FUNDS');
    $this->assertDatabaseHas('orders', ['id' => $order->id, 'status' => 'pending']);
}
```

---

Variables de entorno
--------------------

[](#variables-de-entorno)

VariableDefaultDescripción`PAYMENT_DRIVER``simulator`Driver: `simulator`, `wompi`, `payu``PAYMENT_SIMULATOR_URL``/payment-simulator`URL base del panel`PAYMENT_WEBHOOK_URL``null`URL por defecto para webhooks`PAYMENT_WEBHOOK_DELAY``0`Delay artificial antes de enviar (segundos)`PAYMENT_WEBHOOK_RETRY``true`Habilitar reintentos automáticos`PAYMENT_AUTO_RESOLVE``null`Resolución automática: `approved`/`declined``PAYMENT_LINK_EXPIRY``1800`Vida del link de checkout en segundos`PAYMENT_PSE_DELAY``3`Segundos de procesamiento PSE simulado`PAYMENT_NEQUI_TIMEOUT``120`Timeout NEQUI en segundos`PAYMENT_SETTLEMENT_DELAY``1`Días hasta liquidación (habilita refunds)`PAYMENT_CAPTURE_DEADLINE``7`Días límite para capturar pre-auths`PAYMENT_SEND_RECEIPT``true`Enviar email de comprobante`PAYMENT_MERCHANT_NAME``"Mi Comercio (Simulado)"`Nombre en el checkout`PAYMENT_FRAUD_CARD_MAX``3`Intentos fallidos antes de bloquear tarjeta`PAYMENT_FRAUD_IP_MAX``5`Intentos fallidos antes de bloquear IP`WOMPI_PUBLIC_KEY`—Clave pública Wompi (producción)`WOMPI_PRIVATE_KEY`—Clave privada Wompi`WOMPI_INTEGRITY_KEY`—Clave de integridad Wompi`PAYU_MERCHANT_ID`—ID de comercio PayU`PAYU_API_KEY`—API Key PayU`PAYU_API_LOGIN`—API Login PayU---

Cambiar a producción
--------------------

[](#cambiar-a-producción)

Una sola línea en `.env`:

```
# Desarrollo
PAYMENT_DRIVER=simulator

# Producción con Wompi
PAYMENT_DRIVER=wompi
WOMPI_PUBLIC_KEY=pub_prod_xxx
WOMPI_PRIVATE_KEY=prv_prod_xxx
WOMPI_INTEGRITY_KEY=xxx

# Producción con PayU
PAYMENT_DRIVER=payu
PAYU_MERCHANT_ID=xxx
PAYU_API_KEY=xxx
PAYU_API_LOGIN=xxx
```

El código de tu aplicación no cambia — `PaymentSimulator::createPayment([...])` funciona igual con cualquier driver gracias al contrato `PaymentGatewayContract`.

---

Seguridad
---------

[](#seguridad)

- El panel solo es accesible con `APP_ENV=local` o `APP_ENV=testing`. Cualquier otro entorno retorna 403.
- Los números mágicos de tarjeta son identificadores de test, no PANs reales.
- Valida siempre la firma del webhook en tu handler (SHA-256 para Wompi, MD5 para PayU).

---

Licencia
--------

[](#licencia)

MIT

###  Health Score

20

—

LowBetter than 13% of packages

Maintenance64

Regular maintenance activity

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/74a95e129c336b3281ccc4fe2caf06a27f944567490b52c86f05d00a6202c169?d=identicon)[jamesmosq](/maintainers/jamesmosq)

---

Top Contributors

[![jamesmosquera](https://avatars.githubusercontent.com/u/9124363?v=4)](https://github.com/jamesmosquera "jamesmosquera (3 commits)")

### Embed Badge

![Health badge](/badges/jamesmosq-pasarelas-pago-simulador/health.svg)

```
[![Health](https://phpackages.com/badges/jamesmosq-pasarelas-pago-simulador/health.svg)](https://phpackages.com/packages/jamesmosq-pasarelas-pago-simulador)
```

###  Alternatives

[omnipay/coinbase

Coinbase driver for the Omnipay payment processing library

18570.2k1](/packages/omnipay-coinbase)[yenepay/php-sdk

YenePay SDK for PHP

112.7k](/packages/yenepay-php-sdk)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
