<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Akinshola Samuel AKINDE</title>
    <description>The latest articles on DEV Community by Akinshola Samuel AKINDE (@thisishaykins).</description>
    <link>https://dev.to/thisishaykins</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F662501%2F231318df-6237-4408-b673-b824975374a5.png</url>
      <title>DEV Community: Akinshola Samuel AKINDE</title>
      <link>https://dev.to/thisishaykins</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thisishaykins"/>
    <language>en</language>
    <item>
      <title>Bridging Fintech and Agentic AI: Building a Paystack MCP Server in Python</title>
      <dc:creator>Akinshola Samuel AKINDE</dc:creator>
      <pubDate>Wed, 25 Mar 2026 05:09:06 +0000</pubDate>
      <link>https://dev.to/thisishaykins/bridging-fintech-and-agentic-ai-building-a-paystack-mcp-server-in-python-43f0</link>
      <guid>https://dev.to/thisishaykins/bridging-fintech-and-agentic-ai-building-a-paystack-mcp-server-in-python-43f0</guid>
      <description>&lt;p&gt;If you’ve been following the AI space lately, you’ve probably heard of the Model Context Protocol (MCP). It is rapidly becoming the gold standard for connecting Large Language Models (LLMs) to external tools and databases. &lt;/p&gt;

&lt;p&gt;We are seeing a massive surge in MCP servers for developer tools like GitHub, Postgres, and SQLite. But what about the Fintech space? It has remained largely untapped—until now.&lt;/p&gt;

&lt;p&gt;I’m excited to share a project I had published over 6months ago on GitHub: the &lt;strong&gt;Paystack MCP Server&lt;/strong&gt;. It’s a Python-based server that bridges the gap between &lt;strong&gt;Paystack&lt;/strong&gt; (Africa’s premier payment gateway) and agentic AI interfaces like Claude Desktop. &lt;/p&gt;

&lt;p&gt;Instead of writing manual cURL requests or digging through your Paystack dashboard, you can now just &lt;em&gt;ask&lt;/em&gt; your AI agent to verify transactions, fetch customer details, or process refunds. Let's dive into how it works and how you can run it locally!&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Capabilities
&lt;/h2&gt;

&lt;p&gt;The Paystack MCP Server wraps the Paystack API into "Tools" that LLMs can naturally understand and execute. Currently, the server exposes the following critical endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;verify_transaction&lt;/code&gt;&lt;/strong&gt;: Confirms the status of a payment using a reference code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;fetch_transaction&lt;/code&gt;&lt;/strong&gt;: Retrieves detailed metadata about a specific transaction ID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;create_customer&lt;/code&gt;&lt;/strong&gt;: Registers a new customer on your Paystack integration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;fetch_customer&lt;/code&gt;&lt;/strong&gt;: Retrieves customer details via email or ID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;list_transactions&lt;/code&gt;&lt;/strong&gt;: Fetches a history of recent transactions (with pagination support).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;initiate_refund&lt;/code&gt;&lt;/strong&gt;: Processes refunds for disputes or errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technical Deep Dive: How the Logic Works
&lt;/h2&gt;

&lt;p&gt;I built this project using Python and the official &lt;code&gt;mcp&lt;/code&gt; SDK. Right now, it leverages a synchronous architecture (using the good old &lt;code&gt;requests&lt;/code&gt; library) for solid reliability. &lt;/p&gt;

&lt;p&gt;At its core, the server uses the &lt;code&gt;FastMCP&lt;/code&gt; pattern to strip away unnecessary boilerplate. Here is how the flow works when you prompt your AI:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;You ask:&lt;/strong&gt; &lt;em&gt;"Check if transaction &lt;code&gt;T12345&lt;/code&gt; was successful."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Claude (MCP Client):&lt;/strong&gt; Sees that the &lt;code&gt;verify_transaction&lt;/code&gt; tool perfectly matches your intent.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;MCP Server:&lt;/strong&gt; Receives the tool call and routes it directly to our Python function.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Paystack API:&lt;/strong&gt; The function fires a &lt;code&gt;GET&lt;/code&gt; request to &lt;code&gt;https://api.paystack.co/transaction/verify/T12345&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Response:&lt;/strong&gt; The JSON data is cleaned up, formatted, and sent back to the LLM so it can reply to you in natural language.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Code Sample: Transaction Verification
&lt;/h3&gt;

&lt;p&gt;Here is a quick look under the hood at how the verification logic is implemented (simplified for readability):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mcp.server.fastmcp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastMCP&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="c1"&gt;# Initialize the MCP Server
&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastMCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;paystack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.paystack.co&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;HEADERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PAYSTACK_SECRET_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;verify_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Verifies the status of a transaction using its reference.

    Args:
        reference: The unique transaction reference code.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/transaction/verify/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;  &lt;span class="c1"&gt;# Convert kobo to major unit
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Transaction &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Amount: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error verifying transaction: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installation &amp;amp; Setup
&lt;/h2&gt;

&lt;p&gt;Ready to try it out? You can run this server locally or spin it up via Docker.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.10+&lt;/li&gt;
&lt;li&gt;A Paystack Account (and your Secret Key)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;uv&lt;/code&gt; (highly recommended) or &lt;code&gt;pip&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Option 1: Local Development (via UV)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clone the repo:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   git clone https://github.com/thisishaykins/paystack-python-mcp
   &lt;span class="nb"&gt;cd &lt;/span&gt;paystack-python-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Set up your environment variables:&lt;/strong&gt;
Create a &lt;code&gt;.env&lt;/code&gt; file or export your key directly in the terminal:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PAYSTACK_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sk_test_..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run the Server:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   uv run paystack_mcp.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Option 2: Docker Deployment
&lt;/h3&gt;

&lt;p&gt;If you prefer isolated environments, Docker is the way to go.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dockerfile Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.10-slim&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# Copy source code&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Set entrypoint&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["python", "paystack_mcp.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run it:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; paystack-mcp &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;PAYSTACK_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sk_test_..."&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; paystack-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Note: That &lt;code&gt;-i&lt;/code&gt; flag is super important because MCP communicates over Stdio!)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating with Claude Desktop
&lt;/h2&gt;

&lt;p&gt;To get this working inside your Claude Desktop app, you just need to update your &lt;code&gt;claude_desktop_config.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where to find it:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS:&lt;/strong&gt; &lt;code&gt;~/Library/Application Support/Claude/claude_desktop_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows:&lt;/strong&gt; &lt;code&gt;%APPDATA%\Claude\claude_desktop_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Add this configuration:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"paystack"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uv"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"requests"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"/absolute/path/to/paystack-python-mcp/paystack_mcp.py"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"PAYSTACK_SECRET_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sk_test_YOUR_KEY_HERE"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Scenarios in Action
&lt;/h2&gt;

&lt;p&gt;Once you are hooked up, it feels like magic. Here is what you can do:&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 1: Quick Support Investigation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;You:&lt;/strong&gt; "I have a customer, &lt;code&gt;john@example.com&lt;/code&gt;, claiming he paid for the Premium Plan but it's not showing. Can you check his last 3 transactions?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude's Actions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Triggers &lt;code&gt;fetch_customer(email="john@example.com")&lt;/code&gt; to grab the Customer ID.&lt;/li&gt;
&lt;li&gt;Triggers &lt;code&gt;list_transactions(customer_id=123, per_page=3)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response:&lt;/strong&gt; &lt;em&gt;"I found John. His last 3 transactions were all 'Failed'. He attempted to pay 5000 NGN but the bank declined."&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Scenario 2: Processing Instant Refunds
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;You:&lt;/strong&gt; "Transaction &lt;code&gt;REF-998877&lt;/code&gt; was a duplicate charge. Please refund it immediately."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude's Actions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Triggers &lt;code&gt;verify_transaction(reference="REF-998877")&lt;/code&gt; to check the status.&lt;/li&gt;
&lt;li&gt;Triggers &lt;code&gt;initiate_refund(transaction="REF-998877")&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response:&lt;/strong&gt; &lt;em&gt;"Refund initiated successfully for transaction REF-998877."&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🔮 What's Next? Going Async
&lt;/h2&gt;

&lt;p&gt;This current version is robust, but I'm already working on a &lt;strong&gt;v2 release&lt;/strong&gt; to handle high-throughput workflows (imagine asking Claude to audit 50 transactions at once). &lt;strong&gt;Here's a tested branch:&lt;/strong&gt; &lt;a href="https://github.com/thisishaykins/paystack-python-mcp/tree/feature/v2-async-refactor" rel="noopener noreferrer"&gt;https://github.com/thisishaykins/paystack-python-mcp/tree/feature/v2-async-refactor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is what is on the immediate roadmap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Async/Await:&lt;/strong&gt; Swapping &lt;code&gt;requests&lt;/code&gt; for &lt;code&gt;httpx&lt;/code&gt; and &lt;code&gt;asyncio&lt;/code&gt; for non-blocking API calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhooks:&lt;/strong&gt; Allowing Paystack to push live updates back to the MCP server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bulk Operations:&lt;/strong&gt; Dedicated tools for managing bulk transfers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’d love for you to try it out, break it, and contribute! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check out the repo here:&lt;/strong&gt; &lt;a href="https://github.com/thisishaykins/paystack-python-mcp" rel="noopener noreferrer"&gt;github.com/thisishaykins/paystack-python-mcp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please drop a star ⭐ if you find it useful, and let me know in the comments what Fintech tools you'd like to see integrated next!&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>api</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Shipping Desktop Apps with PHP? Let’s Talk NativePHP for Startups</title>
      <dc:creator>Akinshola Samuel AKINDE</dc:creator>
      <pubDate>Tue, 17 Mar 2026 04:17:25 +0000</pubDate>
      <link>https://dev.to/thisishaykins/shipping-desktop-apps-with-php-lets-talk-nativephp-for-startups-2ke6</link>
      <guid>https://dev.to/thisishaykins/shipping-desktop-apps-with-php-lets-talk-nativephp-for-startups-2ke6</guid>
      <description>&lt;p&gt;&lt;strong&gt;Hey Guys... Haykins here 😊.&lt;/strong&gt; Are you a PHP lover tired of being told you can't build "real" desktop apps? Scratch that. Let’s talk about taking your Laravel skills and wrapping them into a powerful desktop experience without touching a line of Swift or C#. &lt;/p&gt;

&lt;p&gt;If you’re an early-stage startup founder or a solo-dev trying to ship fast, &lt;strong&gt;NativePHP&lt;/strong&gt; might just be the "ginger" your product needs. But is it ready for the big leagues? Let’s dive into the pros, the cons, and the real impact on your throughput.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Quick Gist of NativePHP&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;NativePHP isn’t a new language; it’s a framework that lets you use the PHP tools you already love (like Laravel) to build native-like desktop applications. It uses Electron or Tauri under the hood to bridge the gap between your web code and the user's OS.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;#1 The Pros: Why Your Startup Might Need This&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Developer Velocity (The Hustle):&lt;/strong&gt; You don't need to hire a separate macOS and Windows team. If you know PHP, you’re already a desktop developer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unified Codebase:&lt;/strong&gt; Share your logic, validation, and even your CSS between your web app and your desktop app. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low Barrier to Entry:&lt;/strong&gt; No need to learn complex memory management or UI kits like Cocoa. It’s just PHP and HTML/JS.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;#2 The Cons: The "Hold On A Minute" Section&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Binary Size:&lt;/strong&gt; Because it bundles a whole PHP runtime and Electron, your "Hello World" app might be a bit chunky (100MB+). &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alpha/Beta Status:&lt;/strong&gt; NativePHP is still fresh. You might hit some bugs that don't have a StackOverflow answer yet. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Overhead:&lt;/strong&gt; For high-intensity data processing, a native C++ or Rust app will always win.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;#3 Impact and Rate of Throughput&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;In a startup, &lt;strong&gt;throughput = features shipped&lt;/strong&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Impact:&lt;/strong&gt; You can turn a web-based SaaS into a desktop tool (with system tray icons and global shortcuts) in a weekend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Throughput:&lt;/strong&gt; Your "Time to Market" (TTM) drops significantly. Instead of 3 months for a cross-platform build, you're looking at maybe 2 weeks of tweaking.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Final 2kobo (thoughts 😊)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;NativePHP is a game-changer for the "ship-it" culture. It allows you to leverage your existing PHP expertise to enter new markets (desktop users) with minimal overhead. While it's still growing, the potential for rapid prototyping is insane.&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>nativephp</category>
      <category>learning</category>
    </item>
    <item>
      <title>How to configure multiple databases on individual domain names using a single Odoo instance? [A Multi-Tenant Odoo App]</title>
      <dc:creator>Akinshola Samuel AKINDE</dc:creator>
      <pubDate>Thu, 29 May 2025 15:25:31 +0000</pubDate>
      <link>https://dev.to/thisishaykins/how-to-configure-multiple-databases-on-individual-domain-names-using-a-single-odoo-instance-a-1mlg</link>
      <guid>https://dev.to/thisishaykins/how-to-configure-multiple-databases-on-individual-domain-names-using-a-single-odoo-instance-a-1mlg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Hey Guys... Haykins here 😊. Are you tired of managing multiple Odoo servers for different clients? scratch that. Let’s dockerize your hustle, make it scalable, and give each client their own domain — all on one Odoo engine. this also works out for B2B Startup adopting Odoo for their customers operational activities.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Quick Gist of what we are doing
&lt;/h2&gt;

&lt;p&gt;We will be developing an Odoo setup where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;And Odoo instance that serves multiple clients&lt;/li&gt;
&lt;li&gt;Each client has their own database&lt;/li&gt;
&lt;li&gt;Each database is accessible via a custom domain or subdomain like:

&lt;ul&gt;
&lt;li&gt;client-1.haykinsodoo.docker&lt;/li&gt;
&lt;li&gt;client-2.haykinsodoo.docker&lt;/li&gt;
&lt;li&gt;client-3.haykinsodoo.docker&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Lastly, using Docker, PostgreSQL, Traefik for SSL &amp;amp; routing, sprinkles of Naija developer ginger 😎, and my PC with internet. NEPA or not 😅.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Now, let's get into it...&lt;/p&gt;

&lt;h2&gt;
  
  
  #1 Project Structure
&lt;/h2&gt;

&lt;p&gt;let's get the project structure setup in this format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── docker-compose.yml
├── .env
├── odoo.conf
└── traefik/
    ├── traefik.yml
    └── acme.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  #2 Setting up Environment Variables
&lt;/h2&gt;

&lt;p&gt;we will be creating an .env file on the root folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ODOO_VERSION=18.0
ODOO_DB_USER=odoo
ODOO_DB_PASSWORD=password
POSTGRES_PASSWORD=password
DOMAINS=client-1.haykinsodoo.docker,client-2.haykinsodoo.docker,client-3.haykinsodoo.docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  #3 Setting up Docker Compose YAML
&lt;/h2&gt;

&lt;p&gt;Next, we will be setting up the &lt;code&gt;docker-compose.yml&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# version: "3.9"&lt;/span&gt;
&lt;span class="c1"&gt;# Odoo with Traefik and PostgreSQL&lt;/span&gt;
&lt;span class="c1"&gt;# This docker-compose file sets up Odoo with Traefik as a reverse proxy and PostgreSQL as the database.&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:15&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_USER=${ODOO_DB_USER}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=${POSTGRES_PASSWORD}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGDATA=/var/lib/postgresql/data/pgdata&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8432:5432&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db-data:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgresql_password&lt;/span&gt;

  &lt;span class="na"&gt;odoo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;odoo:${ODOO_VERSION}&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;HOST=db&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;USER=${ODOO_DB_USER}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PASSWORD=${ODOO_DB_PASSWORD}&lt;/span&gt;
    &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgresql_password&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8069:8069"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8072:8072"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;odoo-data:/var/lib/odoo&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./odoo.conf:/etc/odoo/odoo.conf&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./addons:/mnt/extra-addons&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.enable=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.odoo.rule=HostRegexp(`{subdomain:.+}.haykinsodoo.docker`)"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.odoo.entrypoints=web"&lt;/span&gt;
      &lt;span class="c1"&gt;# - "traefik.http.routers.odoo.entrypoints=websecure"&lt;/span&gt;
      &lt;span class="c1"&gt;# - "traefik.http.routers.odoo.tls=true"&lt;/span&gt;
      &lt;span class="c1"&gt;# - "traefik.http.routers.odoo.tls.certresolver=letsencrypt"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.services.odoo.loadbalancer.server.port=8069"&lt;/span&gt;

  &lt;span class="na"&gt;traefik&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik:v2.10&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--api.insecure=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--providers.docker=true&lt;/span&gt;
      &lt;span class="c1"&gt;# - --entrypoints.websecure.address=:443&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--entrypoints.web.address=:80&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--certificatesresolvers.letsencrypt.acme.httpchallenge=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--certificatesresolvers.letsencrypt.acme.email=admin@haykinsodoo.docker&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443:443"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:8080"&lt;/span&gt;  &lt;span class="c1"&gt;# Traefik dashboard port&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./traefik/traefik.yml:/traefik.yml&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./traefik/acme.json:/letsencrypt/acme.json&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;odoo-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgresql_password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;odoo_pg_pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  #4 Setting up the Odoo Configuration
&lt;/h2&gt;

&lt;p&gt;We continue with creatinf an &lt;code&gt;odoo.conf&lt;/code&gt; file and adding our configurations&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[options]&lt;/span&gt;
&lt;span class="py"&gt;addons_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/mnt/extra-addons&lt;/span&gt;
&lt;span class="py"&gt;admin_passwd&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
&lt;span class="py"&gt;db_host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
&lt;span class="py"&gt;db_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;5432&lt;/span&gt;
&lt;span class="py"&gt;db_user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;odoo&lt;/span&gt;
&lt;span class="py"&gt;db_password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;password&lt;/span&gt;
&lt;span class="py"&gt;dbfilter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;^%d$&lt;/span&gt;
&lt;span class="py"&gt;xmlrpc_interface&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0&lt;/span&gt;
&lt;span class="py"&gt;log_level&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;info&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;dbfilter = ^%d$&lt;/code&gt; simply means when the site: &lt;code&gt;client-1.haykinsodoo.docker&lt;/code&gt; is visited, Odoo will open client_1 as the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  #5 Setting and Configuring Traefik
&lt;/h2&gt;

&lt;p&gt;Let's create a &lt;code&gt;traefik/traefik.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;entryPoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:80"&lt;/span&gt;
  &lt;span class="na"&gt;websecure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:443"&lt;/span&gt;
  &lt;span class="na"&gt;traefik&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:8080"&lt;/span&gt;

&lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dashboard&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;insecure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;docker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;exposedByDefault&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, create &lt;code&gt;acme.json&lt;/code&gt; and set file permissions (access rights) to 600, so that only the file owner has read and write access, while no one else, including the group and others, has any access.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;traefik/acme.json &lt;span class="c"&gt;# create the file&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;600 traefik/acme.json &lt;span class="c"&gt;# change file permission&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  #5 Let's get everything up and running
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oshee! Our Odoo live... For us to access the different instances&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;client-1.haykinsodoo.docker&lt;/code&gt; will serve the client-1 database. &lt;strong&gt;(note: ensure to copy the master password which will be re-used for all other clients installations)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client-2.haykinsodoo.docker&lt;/code&gt; will serve the client-2 database.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client-3.haykinsodoo.docker&lt;/code&gt; will serve the client-3 database.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Note In Prod: Make sure those subdomains point to your VPS/public IP address (you can use your domain provider’s DNS settings to handle this).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Bonus Bonus Bonus: Create a DB Manually (Optional)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;odoo_container_id&amp;gt; /bin/bash

&lt;span class="c"&gt;# run the below code inside container&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;odoo &lt;span class="nt"&gt;-d&lt;/span&gt; client-1 &lt;span class="nt"&gt;--init&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;base &lt;span class="nt"&gt;--stop-after-init&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final 2kobo (thoughts 😊)
&lt;/h2&gt;

&lt;p&gt;This entire process and setup gives you the power to serve, scale, and secure multiple Odoo clients like a true Odoo Developer and/or DevOps engineer. It’s modular, clean, and cheap to maintain.&lt;/p&gt;

&lt;p&gt;So whether you’re a developer, startup founder, or building the next ERP-as-a-Service, this is your kick-off point. With this setup, you're running your own multi-tenant Odoo app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;visit my GitHub for the full code setup: &lt;a href="https://github.com/thisishaykins/multi-tenant-odoo-app" rel="noopener noreferrer"&gt;https://github.com/thisishaykins/multi-tenant-odoo-app&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🙌 Shoutout
&lt;/h2&gt;

&lt;p&gt;Written by a Dev for Devs, proudly from the land of jollof, generators, and pure tech vibes. 🇳🇬&lt;br&gt;
Built this with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My PC&lt;/li&gt;
&lt;li&gt;VS Code&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Odoo&lt;/li&gt;
&lt;li&gt;Postgres&lt;/li&gt;
&lt;li&gt;Traefik&lt;/li&gt;
&lt;li&gt;Naija jollof rice 🌶️&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drop your questions below or tag me on Twitter/X: &lt;a href="https://x.com/thisishaykins" rel="noopener noreferrer"&gt;@thisishaykins&lt;/a&gt; &lt;/p&gt;

</description>
      <category>odoo</category>
      <category>programming</category>
      <category>database</category>
      <category>odooerp</category>
    </item>
  </channel>
</rss>
