<!-- ZenPays documentation · https://docs.zenpayz.com/docs/guides/testing -->

# Testing

Learn how to test your ZenPays integration.

## Test Environment

Use test API keys for development:

<Tabs groupId="language">
  <TabItem value="javascript" label="JavaScript" default>

```typescript
const zenpays = new ZenPays({
  apiKey: process.env.ZENPAYS_TEST_API_KEY!, // zp_test_xxx
  baseUrl: 'https://sandbox.zenpays.com',
})
```

  </TabItem>
  <TabItem value="python" label="Python">

```python
import os

zenpays = ZenPays(
    api_key=os.environ["ZENPAYS_TEST_API_KEY"],  # zp_test_xxx
    base_url="https://sandbox.zenpays.com",
)
```

  </TabItem>
</Tabs>

## Test Payment Methods

Use these test values in the sandbox:

### UPI

| VPA | Scenario |
|-----|----------|
| `success@upi` | Successful payment |
| `failure@upi` | Payment failed |
| `timeout@upi` | Payment timeout |

### Net Banking

| Bank Code | Scenario |
|-----------|----------|
| `TEST_BANK_001` | Successful payment |
| `TEST_BANK_002` | Payment declined |

## Mocking the SDK

<Tabs groupId="language">
  <TabItem value="javascript" label="JavaScript" default>

### Using Vitest

```typescript
import { describe, expect, it, vi } from 'vitest'
import { ZenPays } from '@zenxdigitalholdings/zenpays'

vi.mock('@zenxdigitalholdings/zenpays', () => ({
  ZenPays: vi.fn().mockImplementation(() => ({
    payments: {
      createPaymentIntent: vi.fn().mockResolvedValue({
        intentId: 'pi_test_xxx',
        status: 'pending',
      }),
    },
  })),
}))

describe('payment flow', () => {
  it('should create a payment intent', async () => {
    const zenpays = new ZenPays({ apiKey: 'test' })

    const intent = await zenpays.payments.createPaymentIntent({
      amount: 1000,
      currency: 'USD',
    })

    expect(intent.intentId).toBe('pi_test_xxx')
  })
})
```

### Using Jest

```typescript
import { ZenPays } from '@zenxdigitalholdings/zenpays'

jest.mock('@zenxdigitalholdings/zenpays')

const mockZenPays = ZenPays as jest.MockedClass<typeof ZenPays>

describe('payment flow', () => {
  beforeEach(() => {
    mockZenPays.mockImplementation(() => ({
      payments: {
        createPaymentIntent: jest.fn().mockResolvedValue({
          intentId: 'pi_test_xxx',
          status: 'pending',
        }),
      },
    } as any))
  })

  it('should create a payment intent', async () => {
    const zenpays = new ZenPays({ apiKey: 'test' })
    const intent = await zenpays.payments.createPaymentIntent({
      amount: 1000,
      currency: 'USD',
    })

    expect(intent.intentId).toBe('pi_test_xxx')
  })
})
```

  </TabItem>
  <TabItem value="python" label="Python">

### Using unittest.mock

```python
from unittest.mock import Mock, patch
import pytest
from zenpays import ZenPays

def test_create_payment_intent():
    with patch('zenpays.ZenPays') as MockZenPays:
        # Setup mock
        mock_instance = MockZenPays.return_value
        mock_instance.payments.create_payment_intent.return_value = {
            "intent_id": "pi_test_xxx",
            "status": "pending",
        }

        # Test code
        zenpays = ZenPays(api_key="test")
        intent = zenpays.payments.create_payment_intent({
            "amount": 1000,
            "currency": "USD",
        })

        assert intent["intent_id"] == "pi_test_xxx"
```

### Using pytest-mock

```python
import pytest
from zenpays import ZenPays

def test_create_payment_intent(mocker):
    # Mock the ZenPays class
    mock_zenpays = mocker.Mock(spec=ZenPays)
    mock_zenpays.payments.create_payment_intent.return_value = {
        "intent_id": "pi_test_xxx",
        "status": "pending",
    }

    # Test code
    intent = mock_zenpays.payments.create_payment_intent({
        "amount": 1000,
        "currency": "USD",
    })

    assert intent["intent_id"] == "pi_test_xxx"
```

  </TabItem>
</Tabs>

## Integration Tests

Test against the sandbox API:

<Tabs groupId="language">
  <TabItem value="javascript" label="JavaScript" default>

```typescript
import { ZenPays } from '@zenxdigitalholdings/zenpays'

describe('integration tests', () => {
  const zenpays = new ZenPays({
    apiKey: process.env.ZENPAYS_TEST_API_KEY!,
    baseUrl: 'https://sandbox.zenpays.com',
  })

  it('should create and confirm a payment', async () => {
    // Create intent
    const intent = await zenpays.payments.createPaymentIntent({
      amount: 1000,
      currency: 'USD',
    })

    expect(intent.intentId).toBeDefined()

    // Confirm payment
    const result = await zenpays.payments.confirmPayment(intent.intentId, {
      customerDetails: {
        name: 'Test User',
        email: 'test@example.com',
        address: { country: 'US' },
      },
      paymentMethodDetails: {
        type: 'upi',
        vpa: 'test@upi',
      },
    })

    expect(result.status).toBe('succeeded')
  })
})
```

  </TabItem>
  <TabItem value="python" label="Python">

```python
import os
import pytest
from zenpays import ZenPays

@pytest.fixture
def zenpays():
    return ZenPays(
        api_key=os.environ["ZENPAYS_TEST_API_KEY"],
        base_url="https://sandbox.zenpays.com",
    )

def test_create_and_confirm_payment(zenpays):
    # Create intent
    intent = zenpays.payments.create_payment_intent({
        "amount": 1000,
        "currency": "USD",
    })

    assert "intent_id" in intent

    # Confirm payment
    result = zenpays.payments.confirm_payment(intent["intent_id"], {
        "customer_details": {
            "name": "Test User",
            "email": "test@example.com",
            "address": {"country": "US"},
        },
        "payment_method_details": {
            "type": "upi",
            "vpa": "test@upi",
        },
    })

    assert result["status"] == "succeeded"
```

  </TabItem>
</Tabs>

## Custom Fetch for Testing

<Tabs groupId="language">
  <TabItem value="javascript" label="JavaScript" default>

Provide a custom fetch implementation:

```typescript
import { ZenPays } from '@zenxdigitalholdings/zenpays'

const mockFetch = vi.fn().mockResolvedValue({
  ok: true,
  json: () => Promise.resolve({
    success: true,
    data: { intentId: 'pi_xxx', status: 'pending' },
  }),
})

const zenpays = new ZenPays({
  apiKey: 'test',
  fetch: mockFetch,
})

// Now all requests go through mockFetch
await zenpays.payments.createPaymentIntent({ amount: 1000, currency: 'USD' })

expect(mockFetch).toHaveBeenCalledWith(
  expect.stringContaining('/payment-intent'),
  expect.objectContaining({ method: 'POST' })
)
```

  </TabItem>
  <TabItem value="python" label="Python">

Provide a custom session for testing:

```python
from unittest.mock import Mock
from zenpays import ZenPays
import requests

def test_with_custom_session():
    # Create a mock session
    mock_session = Mock(spec=requests.Session)
    mock_response = Mock()
    mock_response.json.return_value = {
        "success": True,
        "data": {"intent_id": "pi_xxx", "status": "pending"},
    }
    mock_response.raise_for_status.return_value = None
    mock_session.post.return_value = mock_response

    # Create ZenPays with custom session
    zenpays = ZenPays(api_key="test", session=mock_session)

    # Now all requests go through mock_session
    intent = zenpays.payments.create_payment_intent({"amount": 1000, "currency": "USD"})

    # Verify the call
    mock_session.post.assert_called_once()
    assert "payment-intent" in mock_session.post.call_args[0][0]
```

  </TabItem>
</Tabs>

## Testing Webhooks

Use a tool like ngrok for local webhook testing:

```bash
ngrok http 3000
```

Then register the ngrok URL as your webhook endpoint:

<Tabs groupId="language">
  <TabItem value="javascript" label="JavaScript" default>

```typescript
await zenpays.merchants.createWebhook({
  url: 'https://your-ngrok-url.ngrok.io/webhooks',
  events: ['payment.intent.succeeded'],
})
```

  </TabItem>
  <TabItem value="python" label="Python">

```python
zenpays.merchants.create_webhook({
    "url": "https://your-ngrok-url.ngrok.io/webhooks",
    "events": ["payment.intent.succeeded"],
})
```

  </TabItem>
</Tabs>
