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

# Security Best Practices

This guide covers security best practices for integrating the ZenPays SDK into your application.

## API Key Security

### Environment Variables

Always store API keys in environment variables:

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

```typescript
// ✅ Good - using environment variables
const zenpays = new ZenPays({
  apiKey: process.env.ZENPAYS_API_KEY!,
})

// ❌ Bad - hardcoded API key
const zenpays = new ZenPays({
  apiKey: 'zp_live_abc123...',
})
```

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

```python
import os

# ✅ Good - using environment variables
zenpays = ZenPays(api_key=os.environ["ZENPAYS_API_KEY"])

# ❌ Bad - hardcoded API key
zenpays = ZenPays(api_key="zp_live_abc123...")
```

  </TabItem>
</Tabs>

## Network Security

### IP Whitelisting

Restrict API access to known IP addresses:

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

```typescript
// Add your server IPs
await zenpays.merchants.addIPToWhitelist('203.0.113.50', 'Production Server')
await zenpays.merchants.addIPToWhitelist('203.0.113.51', 'Backup Server')
```

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

```python
# Add your server IPs
zenpays.merchants.add_ip_to_whitelist("203.0.113.50", "Production Server")
zenpays.merchants.add_ip_to_whitelist("203.0.113.51", "Backup Server")
```

  </TabItem>
</Tabs>

### TLS/HTTPS

The SDK always uses HTTPS for API communication. Never disable TLS verification in production:

```typescript
// The SDK enforces HTTPS by default
const zenpays = new ZenPays({
  apiKey: process.env.ZENPAYS_API_KEY!,
  // baseUrl defaults to https://api.zenpayz.com
})
```

## Authentication Security

### Two-Factor Authentication

Enable 2FA for all team members with API access:

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

```typescript
// Setup 2FA
const setup = await zenpays.security.setup2FA()

// Store backup codes securely
console.log('Backup codes:', setup.backupCodes)

// Verify to complete setup
await zenpays.security.verify2FA({ code: '123456' })
```

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

```python
# Setup 2FA
setup = zenpays.security.setup_two_factor()

# Store backup codes securely
print(f"Backup codes: {setup['backup_codes']}")

# Verify to complete setup
zenpays.security.verify_two_factor({"code": "123456"})
```

  </TabItem>
</Tabs>

### Session Management

For user-facing applications, implement proper session handling:

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

```typescript
// Use short-lived sessions
const session = await authenticate(user)

// Invalidate sessions on logout
await zenpays.security.revokeSession(sessionId)
```

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

```python
# Use short-lived sessions
session = authenticate(user)

# Invalidate sessions on logout
zenpays.security.revoke_session(session_id)
```

  </TabItem>
</Tabs>

## Webhook Security

### Signature Verification

Always verify webhook signatures:

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

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

app.post('/webhooks/zenpays', (req, res) => {
  const signature = req.headers['x-zenpays-signature']
  const webhookSecret = process.env.ZENPAYS_WEBHOOK_SECRET!

  const isValid = verifyWebhookSignature(
    JSON.stringify(req.body),
    signature,
    webhookSecret
  )

  if (!isValid) {
    return res.status(401).send('Invalid signature')
  }

  // Process the webhook
  handleWebhook(req.body)
  res.status(200).send('OK')
})
```

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

```python
import os
from zenpays import verify_webhook_signature
from flask import Flask, request

@app.route('/webhooks/zenpays', methods=['POST'])
def webhook_handler():
    signature = request.headers.get('x-zenpays-signature')
    webhook_secret = os.environ["ZENPAYS_WEBHOOK_SECRET"]

    is_valid = verify_webhook_signature(
        request.get_data().decode('utf-8'),
        signature,
        webhook_secret
    )

    if not is_valid:
        return 'Invalid signature', 401

    # Process the webhook
    handle_webhook(request.get_json())
    return 'OK', 200
```

  </TabItem>
</Tabs>

### Replay Attack Prevention

Check webhook timestamps to prevent replay attacks:

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

```typescript
function handleWebhook(payload: WebhookPayload) {
  const timestamp = new Date(payload.timestamp)
  const now = new Date()
  const fiveMinutes = 5 * 60 * 1000

  if (now.getTime() - timestamp.getTime() > fiveMinutes) {
    throw new Error('Webhook too old - possible replay attack')
  }

  // Process the webhook...
}
```

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

```python
from datetime import datetime, timedelta

def handle_webhook(payload: dict):
    timestamp = datetime.fromisoformat(payload["timestamp"])
    now = datetime.utcnow()
    five_minutes = timedelta(minutes=5)

    if now - timestamp > five_minutes:
        raise ValueError("Webhook too old - possible replay attack")

    # Process the webhook...
```

  </TabItem>
</Tabs>

## Data Security

### PCI Compliance

Never handle raw card data on your servers. Use the checkout flow:

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

```typescript
// ✅ Good - using checkout link
const intent = await zenpays.payments.createPaymentIntent({
  amount: 5000,
  currency: 'USD',
})

// Redirect customer to secure checkout
const checkoutUrl = await zenpays.checkout.createCheckoutLink(intent.id)
```

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

```python
# ✅ Good - using checkout link
intent = zenpays.payments.create_payment_intent({
    "amount": 5000,
    "currency": "USD",
})

# Redirect customer to secure checkout
checkout_url = zenpays.checkout.create_checkout_link(intent["id"])
```

  </TabItem>
</Tabs>

### Sensitive Data Handling

Never log sensitive information:

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

```typescript
// ✅ Good - logging safe fields only
console.log(`Payment ${payment.id} - Amount: ${payment.amount}`)

// ❌ Bad - logging sensitive data
console.log('Payment details:', JSON.stringify(payment))
```

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

```python
# ✅ Good - logging safe fields only
print(f"Payment {payment['id']} - Amount: {payment['amount']}")

# ❌ Bad - logging sensitive data
import json
print(f"Payment details: {json.dumps(payment)}")
```

  </TabItem>
</Tabs>

## Error Handling

### Don't Expose Internal Errors

Show generic messages to users:

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

```typescript
try {
  await zenpays.payments.createPaymentIntent(data)
}
catch (error) {
  // Log detailed error for debugging
  console.error('Payment error:', error)

  // Show generic message to user
  throw new Error('Payment processing failed. Please try again.')
}
```

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

```python
try:
    zenpays.payments.create_payment_intent(data)
except Exception as error:
    # Log detailed error for debugging
    print(f"Payment error: {error}")

    # Show generic message to user
    raise Exception("Payment processing failed. Please try again.")
```

  </TabItem>
</Tabs>

## Security Monitoring

### Monitor Security Events

Regularly review security events:

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

```typescript
const events = await zenpays.security.listSecurityEvents({
  from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), // Last 7 days
  type: ['login_failed', 'api_key_used', 'suspicious_activity'],
})

events.data.forEach((event) => {
  console.log(`${event.type} at ${event.createdAt} from ${event.ipAddress}`)
})
```

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

```python
from datetime import datetime, timedelta

events = zenpays.security.list_security_events({
    "from_": datetime.now() - timedelta(days=7),  # Last 7 days
    "type": ["login_failed", "api_key_used", "suspicious_activity"],
})

for event in events["data"]:
    print(f"{event['type']} at {event['created_at']} from {event['ip_address']}")
```

  </TabItem>
</Tabs>

### Set Up Alerts

Configure webhooks for security events:

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

```typescript
await zenpays.merchants.createWebhook({
  url: 'https://your-app.com/security-alerts',
  events: [
    'security.suspicious_activity',
    'security.api_key_revoked',
    'security.login_failed',
  ],
})
```

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

```python
zenpays.merchants.create_webhook({
    "url": "https://your-app.com/security-alerts",
    "events": [
        "security.suspicious_activity",
        "security.api_key_revoked",
        "security.login_failed",
    ],
})
```

  </TabItem>
</Tabs>

## Checklist

Before going to production:

- [ ] API keys stored in environment variables
- [ ] API keys have minimal required scopes
- [ ] IP whitelisting enabled
- [ ] Webhook signature verification implemented
- [ ] 2FA enabled for all team members
- [ ] Security event monitoring configured
- [ ] No sensitive data in logs
- [ ] Error messages don't expose internal details

## Next Steps

- [Authentication](/docs/getting-started/authentication) - API key management
- [Webhooks](/docs/guides/webhooks) - Webhook setup and verification
- [Error Handling](/docs/guides/error-handling) - Handling errors securely
