Bonaventure OgetoBy Bonaventure Ogeto|

Building a Payments Support Chatbot on Paystack Data

Architecture: 1) Authenticate the user first — verify their email or phone number before the chatbot can access their payment data. 2) Define customer-scoped tools: get_customer_transactions(email), get_subscription_status(subscription_code), get_transaction_by_reference(ref). 3) Pass the verified customer email + tools to the LLM. 4) LLM can only see that customer's data — not other customers. 5) Chatbot is read-only. Any refund or cancellation request goes to a human support agent queue.

Support Chatbot Architecture

// Customer-scoped tools — all queries filter by verified customer email
function buildCustomerTools(verifiedEmail) {
  return [
    {
      name: 'get_my_transactions',
      description: 'Get recent transactions for this customer.',
      input_schema: {
        type: 'object',
        properties: {
          status: { type: 'string', enum: ['success', 'failed', 'pending'] },
          limit: { type: 'number' },
        },
      },
    },
    {
      name: 'get_transaction_by_reference',
      description: 'Look up a specific transaction by reference code.',
      input_schema: {
        type: 'object',
        properties: { reference: { type: 'string' } },
        required: ['reference'],
      },
    },
    {
      name: 'get_subscription_status',
      description: 'Check the status of a subscription.',
      input_schema: {
        type: 'object',
        properties: { subscription_code: { type: 'string' } },
        required: ['subscription_code'],
      },
    },
    {
      name: 'request_human_agent',
      description: 'Escalate to a human support agent for refunds, cancellations, or complex issues.',
      input_schema: {
        type: 'object',
        properties: { reason: { type: 'string' } },
        required: ['reason'],
      },
    },
  ];
}

// Tool execution — always enforces the verified email scope
async function executeSupportTool(toolName, input, verifiedEmail) {
  var headers = { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY };

  if (toolName === 'get_my_transactions') {
    var params = 'customer=' + encodeURIComponent(verifiedEmail) + '&perPage=' + (input.limit || 10);
    if (input.status) params += '&status=' + input.status;
    var res = await fetch('https://api.paystack.co/transaction?' + params, { headers });
    return (await res.json()).data;
  }

  if (toolName === 'get_transaction_by_reference') {
    var res = await fetch('https://api.paystack.co/transaction/verify/' + input.reference, { headers });
    var data = (await res.json()).data;
    // Security: only return if this transaction belongs to the verified customer
    if (data?.customer?.email !== verifiedEmail) {
      return { error: 'Transaction not found for your account' };
    }
    return data;
  }

  if (toolName === 'request_human_agent') {
    await db.supportQueue.insert({ customerEmail: verifiedEmail, reason: input.reason, ts: new Date() });
    return { status: 'queued', message: 'A human agent will contact you within 2 business hours' };
  }
}

Learn More

See using an LLM to explain failed payments for the failure code explanation pattern to include in your chatbot system prompt.

Sign up for the McTaba newsletter

Key Takeaways

  • Authenticate the customer before the chatbot can access any Paystack data — no anonymous payment lookups.
  • Scope all tool calls to the verified customer's email — never allow cross-customer data access.
  • Keep the chatbot read-only — refunds and cancellations go to a human queue.
  • Give the LLM plain-English failure code explanations as a reference in the system prompt.
  • Log every chatbot session with the customer identifier, questions asked, and tools called.

Frequently Asked Questions

How do I authenticate the customer before giving the chatbot access to their data?
Use your existing session authentication. If the customer is logged in to your application, their verified email is in the session — pass it as the scope for the chatbot. If the chatbot is on a public widget, require the customer to enter their email and send an OTP to verify it before opening the chat session. Never trust a customer-supplied email without verification.
What should the chatbot do when it cannot find the transaction?
Return an honest response: "I could not find a transaction with that reference on your account." Then offer the request_human_agent option. Never let the LLM speculate about why a transaction is missing — it can produce plausible but wrong explanations. Missing transactions warrant a human check.
Should I include refund capabilities in the chatbot?
No — not as an automated action. A confused or manipulated LLM can refund the wrong transaction. Build a refund request tool that queues the request for human review. The chatbot can confirm the refund request was received and provide a timeline, but a human approves and executes the actual refund via Paystack dashboard or API.

Ready to build real-world apps?

Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.

Apply to the McTaba Marathon