S
ShadhinPay Docs

Security Best Practices

Secure your ShadhinPay integration

Security Best Practices

Follow these guidelines to keep your payment integration secure.

API Key Security

Never Expose Secret Keys

Your Secret Key should never appear in:

  • Client-side JavaScript
  • Mobile app source code
  • Git repositories
  • Browser network requests
  • Log files

Use Environment Variables

Correct
// Server-side only
const secretKey = process.env.SHADHINPAY_SECRET_KEY;
Wrong
// Never do this!
const secretKey = 'sk_live_abc123...';

Separate Keys by Environment

.env.development
SHADHINPAY_CLIENT_ID=cl_test_xxx
SHADHINPAY_SECRET_KEY=sk_test_xxx
.env.production
SHADHINPAY_CLIENT_ID=cl_live_xxx
SHADHINPAY_SECRET_KEY=sk_live_xxx

Webhook Security

Always Verify Signatures

Never process webhooks without verifying the signature:

function verifyWebhook(payload, signature, secretKey) {
  const crypto = require('crypto');

  const expectedSignature = crypto
    .createHmac('sha256', secretKey)
    .update(JSON.stringify(payload))
    .digest('hex');

  const providedSignature = signature.replace('sha256=', '');

  // Use timing-safe comparison
  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature),
    Buffer.from(providedSignature)
  );
}

Validate Webhook Data

Don't trust webhook data blindly:

app.post('/webhook', async (req, res) => {
  const { payment_id, amount } = req.body.data;

  // Verify with our API
  const payment = await shadhinpay.payments.get(payment_id);

  if (payment.amount !== amount) {
    console.error('Amount mismatch!');
    return res.status(400).send('Invalid data');
  }

  // Process...
});

Server Security

Use HTTPS Only

All communication with ShadhinPay must use HTTPS:

// Correct
const baseUrl = 'https://api.shadhinpay.com';

// Wrong - Never use HTTP
const baseUrl = 'http://api.shadhinpay.com';

Implement Rate Limiting

Protect your webhook endpoint:

const rateLimit = require('express-rate-limit');

const webhookLimiter = rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 100, // 100 requests per minute
});

app.post('/webhook', webhookLimiter, handleWebhook);

Validate IP Addresses (Optional)

Whitelist ShadhinPay's IP addresses:

const ALLOWED_IPS = [
  '103.xxx.xxx.xxx',
  '103.xxx.xxx.xxx',
  // Get current IPs from dashboard
];

function validateIP(req, res, next) {
  const ip = req.ip || req.connection.remoteAddress;

  if (!ALLOWED_IPS.includes(ip)) {
    return res.status(403).send('Forbidden');
  }

  next();
}

Data Protection

Minimize Data Storage

Only store what you need:

// Good - Store reference only
const order = {
  payment_id: 'SP_123',
  status: 'PAID',
  amount: 1000,
};

// Bad - Don't store sensitive payment details
const order = {
  payment_id: 'SP_123',
  customer_pin: '1234',     // Never store this!
  card_number: '4111...',   // Never store this!
};

Encrypt Sensitive Data

If you must store sensitive data:

const crypto = require('crypto');

function encrypt(text, key) {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
  const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);
  const tag = cipher.getAuthTag();

  return {
    iv: iv.toString('hex'),
    content: encrypted.toString('hex'),
    tag: tag.toString('hex'),
  };
}

Input Validation

Validate All Inputs

const { z } = require('zod');

const paymentSchema = z.object({
  amount: z.number().min(10).max(500000),
  currency: z.enum(['BDT']),
  customer_phone: z.string().regex(/^880\d{10}$/),
  customer_email: z.string().email().optional(),
});

function createPayment(data) {
  const validated = paymentSchema.parse(data);
  // Proceed with validated data
}

Sanitize User Input

const sanitizeHtml = require('sanitize-html');

function sanitizeInput(input) {
  return sanitizeHtml(input, {
    allowedTags: [],
    allowedAttributes: {},
  });
}

Logging & Monitoring

Log Security Events

function logSecurityEvent(type, details) {
  console.log(JSON.stringify({
    timestamp: new Date().toISOString(),
    type,
    details,
    // Don't log sensitive data
  }));
}

// Usage
logSecurityEvent('WEBHOOK_SIGNATURE_INVALID', {
  ip: req.ip,
  payment_id: req.body.payment_id,
});

Monitor for Anomalies

Set up alerts for:

  • Multiple failed signature verifications
  • Unusual payment amounts
  • Requests from unexpected IPs
  • High error rates

Security Checklist

  • Secret keys stored in environment variables
  • Webhook signatures verified
  • HTTPS used for all requests
  • Rate limiting implemented
  • Input validation in place
  • Sensitive data encrypted at rest
  • Security logging enabled
  • Keys rotated periodically
  • Access restricted by IP (optional)
  • PCI DSS compliance reviewed

On this page