zurri / PAYMENT_TEST.md
nexusbert's picture
push to space
4a285d2

Paystack Payment Integration - Test Guide

Overview

The platform now supports Paystack payments for wallet funding. Users can fund their wallets using Paystack's inline widget or redirect method.

Environment Variables Required

PAYSTACK_SECRET_KEY=sk_test_...  # Your Paystack secret key
PAYSTACK_PUBLIC_KEY=pk_test_...   # Your Paystack public key
PAYSTACK_CALLBACK_URL=http://localhost:3000/payment/callback  # Optional
FRONTEND_URL=http://localhost:3000  # Optional, for callback URL fallback
POINT_VALUE_USD=0.05  # 1 point = $0.05
NGN_PER_USD=750  # Exchange rate (adjust as needed)

API Endpoints

1. Initialize Payment (Fund Wallet)

POST /api/wallet/fund

Headers:

Authorization: Bearer <user_token>
Content-Type: application/json

Request Body:

{
  "amount": 1000  // Amount in Naira (NGN)
}

Response:

{
  "publicKey": "pk_test_...",
  "reference": "wallet_...",
  "authorization_url": "https://checkout.paystack.com/...",
  "access_code": "...",
  "payment": {
    "reference": "wallet_...",
    "amount": 1000,
    "currency": "NGN",
    "points": 26,
    "amountInDollars": "1.33"
  },
  "message": "Use authorization_url to redirect or use Paystack inline widget with access_code"
}

2. Verify Transaction (Manual Verification)

GET /api/wallet/verify/:reference

Headers:

Authorization: Bearer <user_token>

Response:

{
  "success": true,
  "message": "Payment verified and points added",
  "transaction": {
    "id": "...",
    "type": "purchase",
    "amount": 26,
    "balanceAfter": 26
  },
  "wallet": {
    "balance": 26,
    "balanceInDollars": 1.3
  }
}

3. Webhook (Paystack β†’ Server)

POST /api/wallet/webhook/paystack

Headers:

x-paystack-signature: <hmac_sha512_signature>

Note: This is called automatically by Paystack. No authentication needed (uses signature verification).

Frontend Integration Examples

Option 1: Using Paystack Inline Widget (Recommended)

<script src="https://js.paystack.co/v1/inline.js"></script>
// After calling POST /api/wallet/fund
async function fundWallet(amountInNaira) {
  const response = await fetch('/api/wallet/fund', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${userToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ amount: amountInNaira })
  });

  const data = await response.json();

  // Use Paystack inline widget
  const handler = PaystackPop.setup({
    key: data.publicKey,
    email: userEmail,
    amount: data.payment.amount * 100, // Convert to kobo
    ref: data.reference,
    metadata: {
      custom_fields: [
        {
          display_name: 'User ID',
          variable_name: 'userId',
          value: userId
        }
      ]
    },
    onClose: function() {
      // Handle payment cancellation
      console.log('Payment cancelled');
    },
    callback: function(response) {
      // Verify payment
      verifyPayment(response.reference);
    }
  });

  handler.openIframe();
}

async function verifyPayment(reference) {
  const response = await fetch(`/api/wallet/verify/${reference}`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${userToken}`
    }
  });

  const result = await response.json();
  if (result.success) {
    console.log('Payment successful!', result);
    // Update wallet balance in UI
    updateWalletBalance(result.wallet.balance);
  }
}

Option 2: Using Redirect URL

async function fundWalletRedirect(amountInNaira) {
  const response = await fetch('/api/wallet/fund', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${userToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ amount: amountInNaira })
  });

  const data = await response.json();
  
  // Redirect to Paystack checkout
  window.location.href = data.authorization_url;
}

// On callback page (after redirect)
const urlParams = new URLSearchParams(window.location.search);
const reference = urlParams.get('reference');

if (reference) {
  verifyPayment(reference);
}

Testing

Test Mode

Use Paystack test keys:

  • Public Key: pk_test_...
  • Secret Key: sk_test_...

Test Cards

  • Success: 4084084084084081
  • Decline: 5060666666666666666
  • Insufficient Funds: 5078607857785078

Test Flow

  1. Call POST /api/wallet/fund with amount (e.g., 1000 NGN)
  2. Use the returned publicKey and reference in Paystack widget
  3. Complete payment with test card
  4. Call GET /api/wallet/verify/:reference to verify and credit wallet
  5. Check wallet balance: GET /api/wallet

Notes

  • Idempotency: The system checks for duplicate transactions by reference. Calling verify multiple times won't double-credit.
  • Webhook: Paystack automatically calls the webhook endpoint. Both webhook and manual verification work independently.
  • Currency: Amounts are in Naira (NGN). The system converts to points automatically.
  • Points Calculation: Points = (NGN / NGN_PER_USD) / POINT_VALUE_USD