PayGate

Go Live

Everything you need to do before accepting real payments.

Go Live Checklist

Before you start accepting real payments, complete this checklist to ensure a smooth launch.

Pre-Launch Checklist

Account Setup

  • Complete business verification - Submit required documents in the Dashboard
  • Add bank account for payouts - Set up where you'll receive funds
  • Set up team members - Invite team with appropriate permissions
  • Configure notification settings - Set up email alerts for important events

API Integration

  • Switch to live API keys - Replace sk_test_ with sk_live_
  • Update base URL - Use api.paygate.com.gh instead of sandbox
  • Verify webhook endpoints - Ensure production URLs are configured
  • Test with real transaction - Make a small real payment

Security

  • Secure API keys - Never expose keys in client-side code
  • Implement webhook signature verification - Verify all webhook signatures
  • Enable HTTPS - All endpoints must use HTTPS
  • Set up monitoring - Monitor for errors and unusual activity

Error Handling

  • Handle all error types - Test error scenarios
  • Implement retry logic - For transient failures
  • Set up alerting - Get notified of failures
  • Create user-friendly error messages - Map error codes to helpful messages

Step-by-Step Guide

Complete Business Verification

Before you can accept live payments, verify your business:

  1. Go to Settings > Business in your Dashboard
  2. Upload required documents:
    • Business registration certificate
    • Tax identification number (TIN)
    • ID of business owner
    • Bank statement or utility bill
  3. Wait for verification (usually 1-2 business days)

You can continue development in test mode while verification is pending.

Switch to Live API Keys

Replace your test keys with live keys:

// Before (Test)
const paygate = new PayGate('sk_test_...')

// After (Live)
const paygate = new PayGate(process.env.PAYGATE_SECRET_KEY)
// Where PAYGATE_SECRET_KEY is sk_live_...

Important: Use environment variables, never hardcode keys:

# .env.production
PAYGATE_SECRET_KEY=sk_live_...
PAYGATE_WEBHOOK_SECRET=whsec_...

Update Webhook Endpoints

Create production webhook endpoints:

  1. Go to Settings > Webhooks
  2. Add your production endpoint URL
  3. Select events to receive
  4. Copy the new webhook secret
  5. Update your server configuration
// Use production webhook secret
const endpointSecret = process.env.PAYGATE_WEBHOOK_SECRET

Verify Webhook Signatures

Always verify signatures in production:

app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-paygate-signature']

  let event
  try {
    event = Webhook.constructEvent(
      req.body,
      signature,
      process.env.PAYGATE_WEBHOOK_SECRET
    )
  } catch (err) {
    // Log the error for debugging
    console.error('Webhook signature verification failed:', err.message)
    return res.status(400).send('Invalid signature')
  }

  // Process the event
  handleWebhookEvent(event)

  res.json({ received: true })
})

Never skip signature verification in production. This prevents spoofed webhooks.

Test with a Real Transaction

Make a small real payment to verify everything works:

  1. Use your live API key
  2. Use a real phone number
  3. Process a small amount (e.g., GHS 1.00)
  4. Verify:
    • Payment prompt is received
    • Authorization works
    • Webhook is delivered
    • Funds appear in your dashboard
// Test with minimum amount
const payment = await paygate.payments.create({
  amount: 100, // GHS 1.00 - minimum amount
  currency: 'GHS',
  payment_method: 'mobile_money',
  phone: 'YOUR_REAL_PHONE',
  description: 'Integration test'
})

Set Up Monitoring

Monitor your integration for issues:

// Log all API errors
paygate.on('error', (error) => {
  logger.error('PayGate API Error', {
    type: error.type,
    code: error.code,
    message: error.message,
    requestId: error.requestId
  })

  // Alert on critical errors
  if (error.type === 'api_error') {
    alertOps('PayGate API Error', error)
  }
})

// Track webhook delivery
app.post('/webhooks', async (req, res) => {
  const event = Webhook.constructEvent(...)

  try {
    await handleWebhookEvent(event)
    logger.info('Webhook processed', { eventId: event.id, type: event.type })
  } catch (error) {
    logger.error('Webhook processing failed', {
      eventId: event.id,
      type: event.type,
      error: error.message
    })
  }

  res.json({ received: true })
})

Security Best Practices

Protect API Keys

// DON'T: Hardcode keys
const paygate = new PayGate('sk_live_abc123...')

// DON'T: Use keys in client-side code
// <script>const key = 'sk_live_...'</script>

// DO: Use environment variables
const paygate = new PayGate(process.env.PAYGATE_SECRET_KEY)

// DO: Keep keys out of version control
// .gitignore
// .env
// .env.local
// .env.production

Webhook Security

// Always verify signatures
const event = Webhook.constructEvent(payload, signature, secret)

// Use HTTPS endpoints only
// PayGate won't deliver to HTTP in live mode

// Return 200 quickly
app.post('/webhooks', (req, res) => {
  res.json({ received: true }) // Acknowledge immediately
  processEventAsync(event) // Process in background
})

Data Security

// Don't log sensitive data
console.log(payment) // DON'T log full payment objects

console.log({
  id: payment.id,
  status: payment.status,
  amount: payment.amount
}) // DO: log only what you need

// Mask phone numbers in logs
const maskedPhone = phone.replace(/(\d{3})\d{4}(\d{3})/, '$1****$2')

Error Handling

Handle All Error Types

try {
  const payment = await paygate.payments.create(params)
} catch (error) {
  if (error instanceof PayGateError) {
    switch (error.type) {
      case 'authentication_error':
        // Check API key
        logger.error('Authentication failed')
        throw new Error('Payment service configuration error')

      case 'invalid_request_error':
        // Validation failed
        throw new Error(getUserFriendlyMessage(error.code))

      case 'payment_error':
        // Payment-specific error
        throw new Error(getPaymentErrorMessage(error.code))

      case 'rate_limit_error':
        // Too many requests
        await sleep(60000)
        return retry()

      case 'api_error':
        // Server error
        logger.error('PayGate server error', { error })
        throw new Error('Payment service temporarily unavailable')
    }
  }

  // Network or unknown error
  logger.error('Unknown payment error', { error })
  throw new Error('Unable to process payment. Please try again.')
}

User-Friendly Error Messages

const errorMessages = {
  // Payment errors
  'insufficient_funds': 'Your mobile money balance is too low. Please top up and try again.',
  'phone_unreachable': 'We couldn\'t reach your phone. Make sure it\'s on and has network.',
  'timeout': 'The payment timed out. Please try again.',
  'transaction_declined': 'The payment was declined. Please check your PIN and try again.',
  'daily_limit_exceeded': 'You\'ve reached your daily limit. Try again tomorrow.',

  // Validation errors
  'invalid_phone': 'Please enter a valid Ghana phone number.',
  'amount_too_small': 'The minimum payment amount is GHS 1.00.',
  'amount_too_large': 'The amount exceeds the maximum allowed.',

  // Generic
  'default': 'Something went wrong. Please try again or contact support.'
}

function getUserFriendlyMessage(code) {
  return errorMessages[code] || errorMessages.default
}

Monitoring & Alerts

What to Monitor

MetricAlert Threshold
Payment success rateBelow 95%
Average response timeAbove 5 seconds
Webhook delivery rateBelow 99%
Error rateAbove 1%

Example Monitoring Setup

import { metrics } from './monitoring'

// Track payment outcomes
async function createPayment(params) {
  const start = Date.now()

  try {
    const payment = await paygate.payments.create(params)
    metrics.increment('payments.created')
    metrics.timing('payments.latency', Date.now() - start)
    return payment
  } catch (error) {
    metrics.increment('payments.failed', { code: error.code })
    throw error
  }
}

// Track webhook processing
app.post('/webhooks', async (req, res) => {
  metrics.increment('webhooks.received')

  try {
    const event = Webhook.constructEvent(...)
    await processEvent(event)
    metrics.increment('webhooks.processed')
  } catch (error) {
    metrics.increment('webhooks.failed')
  }

  res.json({ received: true })
})

Post-Launch

First Week

  • Monitor error rates closely
  • Check webhook delivery in Dashboard
  • Verify payouts are processing
  • Review customer feedback

Ongoing

  • Review API logs weekly
  • Update SDKs regularly
  • Monitor for new features
  • Keep webhook endpoints healthy

Support

If you encounter issues:

  1. Check the API Status Page for outages
  2. Review your Dashboard for error details
  3. Search our Documentation for solutions
  4. Contact Support at support@paygate.com.gh

Congratulations! Once you've completed this checklist, you're ready to accept real payments. Welcome to PayGate!