<?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: Afriex</title>
    <description>The latest articles on DEV Community by Afriex (@afriex).</description>
    <link>https://dev.to/afriex</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%2Forganization%2Fprofile_image%2F12790%2Fb22225a9-dfbf-4c5a-a15a-2d25dba8e294.jpeg</url>
      <title>DEV Community: Afriex</title>
      <link>https://dev.to/afriex</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/afriex"/>
    <language>en</language>
    <item>
      <title>Building Global Payment Products Without Owning Banking Infrastructure</title>
      <dc:creator>0xSonOfUri</dc:creator>
      <pubDate>Fri, 12 Jun 2026 23:46:55 +0000</pubDate>
      <link>https://dev.to/afriex/building-global-payment-products-without-owning-banking-infrastructure-2h76</link>
      <guid>https://dev.to/afriex/building-global-payment-products-without-owning-banking-infrastructure-2h76</guid>
      <description>&lt;p&gt;A decade ago, building a financial product required enormous resources.&lt;/p&gt;

&lt;p&gt;If you wanted to launch a payments company, you needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;banking relationships&lt;/li&gt;
&lt;li&gt;settlement infrastructure&lt;/li&gt;
&lt;li&gt;compliance operations&lt;/li&gt;
&lt;li&gt;payment processing agreements&lt;/li&gt;
&lt;li&gt;treasury management systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most startups, that was simply out of reach.&lt;/p&gt;

&lt;p&gt;Today, things look very different.&lt;/p&gt;

&lt;p&gt;A small team can build products that move money globally without owning a bank, operating payment rails, or maintaining treasury infrastructure.&lt;/p&gt;

&lt;p&gt;The secret isn't magic.&lt;/p&gt;

&lt;p&gt;It's infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Old Fintech Playbook
&lt;/h2&gt;

&lt;p&gt;Historically, launching a financial product meant becoming part of the financial system itself.&lt;/p&gt;

&lt;p&gt;That often involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;obtaining licenses&lt;/li&gt;
&lt;li&gt;negotiating banking partnerships&lt;/li&gt;
&lt;li&gt;integrating with payment processors&lt;/li&gt;
&lt;li&gt;maintaining settlement accounts&lt;/li&gt;
&lt;li&gt;building compliance operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The barriers were high.&lt;/p&gt;

&lt;p&gt;And for good reason.&lt;/p&gt;

&lt;p&gt;Moving money is complicated.&lt;/p&gt;




&lt;h2&gt;
  
  
  The New Fintech Stack
&lt;/h2&gt;

&lt;p&gt;Modern fintech companies increasingly build on top of specialized infrastructure providers.&lt;/p&gt;

&lt;p&gt;Instead of building everything themselves, they compose financial primitives.&lt;/p&gt;

&lt;p&gt;Think of it like cloud computing.&lt;/p&gt;

&lt;p&gt;Most startups don't build their own data centers.&lt;/p&gt;

&lt;p&gt;They build on AWS, Google Cloud, or Azure.&lt;/p&gt;

&lt;p&gt;Financial infrastructure is moving in a similar direction.&lt;/p&gt;

&lt;p&gt;Developers now have access to building blocks such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;virtual accounts&lt;/li&gt;
&lt;li&gt;wallet infrastructure&lt;/li&gt;
&lt;li&gt;payouts&lt;/li&gt;
&lt;li&gt;foreign exchange&lt;/li&gt;
&lt;li&gt;stablecoin settlement&lt;/li&gt;
&lt;li&gt;transaction monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;through APIs.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Shift from Banks to Building Blocks
&lt;/h2&gt;

&lt;p&gt;Consider a simple requirement:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Receive money from customers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Years ago, that might require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;direct banking integrations&lt;/li&gt;
&lt;li&gt;account provisioning systems&lt;/li&gt;
&lt;li&gt;settlement infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Today, an API can provision receiving accounts for you.&lt;/p&gt;

&lt;p&gt;The same pattern appears across the financial stack.&lt;/p&gt;

&lt;p&gt;Need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;receive funds?&lt;/li&gt;
&lt;li&gt;send payouts?&lt;/li&gt;
&lt;li&gt;manage wallets?&lt;/li&gt;
&lt;li&gt;support multiple currencies?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are infrastructure layers designed to handle those complexities.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Developers Actually Build
&lt;/h2&gt;

&lt;p&gt;The interesting thing is that most fintech startups are not building payment rails.&lt;/p&gt;

&lt;p&gt;They're building products.&lt;/p&gt;

&lt;p&gt;The rails already exist.&lt;/p&gt;

&lt;p&gt;The real value often comes from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;user experience&lt;/li&gt;
&lt;li&gt;automation&lt;/li&gt;
&lt;li&gt;workflows&lt;/li&gt;
&lt;li&gt;business logic&lt;/li&gt;
&lt;li&gt;distribution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not from rebuilding banking infrastructure.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;A freelancer platform doesn't need to become a bank.&lt;/p&gt;

&lt;p&gt;It needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;customer onboarding&lt;/li&gt;
&lt;li&gt;collections&lt;/li&gt;
&lt;li&gt;payouts&lt;/li&gt;
&lt;li&gt;reporting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Infrastructure providers handle the rest.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Role of Stablecoins
&lt;/h2&gt;

&lt;p&gt;Stablecoins are accelerating this trend.&lt;/p&gt;

&lt;p&gt;Many cross-border payment platforms now use stablecoins internally for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;treasury management&lt;/li&gt;
&lt;li&gt;liquidity movement&lt;/li&gt;
&lt;li&gt;settlement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;while customers continue interacting with familiar payment experiences.&lt;/p&gt;

&lt;p&gt;The customer sees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Send Money
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The infrastructure may involve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Collection
↓
Settlement
↓
Stablecoin Treasury
↓
Liquidity Partner
↓
Payout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complexity is hidden behind APIs and infrastructure layers.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Means for Builders
&lt;/h2&gt;

&lt;p&gt;The question is no longer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do I become a bank?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What can I build on top of financial infrastructure?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That opens the door to entirely new products.&lt;/p&gt;

&lt;p&gt;Developers can focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;marketplaces&lt;/li&gt;
&lt;li&gt;payroll systems&lt;/li&gt;
&lt;li&gt;creator platforms&lt;/li&gt;
&lt;li&gt;remittance products&lt;/li&gt;
&lt;li&gt;B2B payment workflows&lt;/li&gt;
&lt;li&gt;treasury automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;without spending years building foundational infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Infrastructure Is Becoming the Advantage
&lt;/h2&gt;

&lt;p&gt;The most successful fintech companies of the next decade may not be the ones that own the rails.&lt;/p&gt;

&lt;p&gt;They may be the ones that use the rails most effectively.&lt;/p&gt;

&lt;p&gt;Just as cloud providers abstracted away servers, modern financial infrastructure is abstracting away much of the complexity involved in moving money.&lt;/p&gt;

&lt;p&gt;That allows builders to focus on solving customer problems rather than rebuilding the financial system from scratch.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Building a global payment product used to require becoming part of the banking system.&lt;/p&gt;

&lt;p&gt;Today, developers can access many of the same capabilities through infrastructure providers and APIs.&lt;/p&gt;

&lt;p&gt;The opportunity has shifted.&lt;/p&gt;

&lt;p&gt;Less time spent building rails.&lt;/p&gt;

&lt;p&gt;More time spent building products.&lt;/p&gt;

&lt;p&gt;And that might be one of the most important changes happening in fintech today.&lt;/p&gt;

</description>
      <category>fintech</category>
      <category>payment</category>
      <category>banking</category>
    </item>
    <item>
      <title>Accept Payments in Minutes with Afriex Checkout Sessions</title>
      <dc:creator>Victory Lucky</dc:creator>
      <pubDate>Sat, 30 May 2026 16:48:17 +0000</pubDate>
      <link>https://dev.to/afriex/accept-payments-in-minutes-with-afriex-checkout-sessions-3j1m</link>
      <guid>https://dev.to/afriex/accept-payments-in-minutes-with-afriex-checkout-sessions-3j1m</guid>
      <description>&lt;p&gt;Most payment integrations follow the same painful path: you build a checkout UI, set up bank account display logic, handle Mobile Money prompts, manage session timers, poll for confirmation, and somehow do all of this correctly across every edge case. It takes days, sometimes weeks — and that's before you think about testing.&lt;/p&gt;

&lt;p&gt;Afriex Checkout Sessions changes that. With a single API call, you get a fully hosted payment page that handles everything: payment method selection, bank transfer details, Mobile Money authorization, session expiration, and redirect on completion. You stay focused on your product; Afriex handles the payment flow.&lt;/p&gt;

&lt;p&gt;This guide walks you through a complete integration from scratch — using either the official TypeScript SDK or the REST API directly.&lt;/p&gt;




&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;The flow is four steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Your server&lt;/strong&gt; creates a checkout session via the SDK or REST API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Afriex&lt;/strong&gt; returns a &lt;code&gt;checkoutUrl&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You redirect&lt;/strong&gt; the customer to that URL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After payment&lt;/strong&gt;, Afriex redirects the customer back to your app and fires a webhook to your server&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's the entire integration surface. No payment UI to build. No polling loop. No bank account display to manage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you start, you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;a href="https://business.afriex.com" rel="noopener noreferrer"&gt;Afriex Business account&lt;/a&gt; with API access&lt;/li&gt;
&lt;li&gt;Your API key from the &lt;a href="https://business.afriex.com/developer" rel="noopener noreferrer"&gt;Afriex dashboard&lt;/a&gt; (Developer tab)&lt;/li&gt;
&lt;li&gt;Node.js 20+ if you're using the SDK, or any server-side runtime for the REST API&lt;/li&gt;
&lt;li&gt;An HTTPS redirect URL where customers will land after payment&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Sandbox first.&lt;/strong&gt; Checkout Sessions is currently available for end-to-end testing in Afriex's sandbox environment. Production rollout is happening soon — use sandbox to validate your integration now so you're ready to flip the switch.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Using the SDK (Node.js / TypeScript)
&lt;/h3&gt;

&lt;p&gt;Install the official Afriex SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @afriex/sdk
&lt;span class="c"&gt;# or&lt;/span&gt;
pnpm add @afriex/sdk
&lt;span class="c"&gt;# or&lt;/span&gt;
yarn add @afriex/sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then initialize it once — typically in a shared module or service file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AfriexSDK&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@afriex/sdk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;afriex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AfriexSDK&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AFRIEX_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;staging&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or "production"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK maps &lt;code&gt;staging&lt;/code&gt; to &lt;code&gt;https://sandbox.api.afriex.com&lt;/code&gt; and &lt;code&gt;production&lt;/code&gt; to &lt;code&gt;https://api.afriex.com&lt;/code&gt; — no need to manage base URLs yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enable retries for production resilience:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;afriex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AfriexSDK&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AFRIEX_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;retryConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;maxRetries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;retryDelay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// milliseconds&lt;/span&gt;
    &lt;span class="na"&gt;retryableStatusCodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;408&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;502&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using the REST API directly
&lt;/h3&gt;

&lt;p&gt;Set your base URL depending on your environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sandbox:    https://sandbox.api.afriex.com
Production: https://api.afriex.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pass your API key on every request via the &lt;code&gt;x-api-key&lt;/code&gt; header.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Create a Checkout Session
&lt;/h2&gt;

&lt;h3&gt;
  
  
  SDK (Node.js / TypeScript)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;afriex&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./afriex-client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// your initialized SDK instance&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createCheckoutSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;afriex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSession&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amountInKobo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// e.g. 500000 for ₦5,000&lt;/span&gt;
    &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NGN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;merchantReference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// must be unique per session&lt;/span&gt;
    &lt;span class="na"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://yourapp.com/checkout/return&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;channels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VIRTUAL_BANK_ACCOUNT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MOBILE_MONEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// E.164 format: +2348100000000&lt;/span&gt;
      &lt;span class="na"&gt;countryCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NG&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkoutUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK returns &lt;code&gt;{ checkoutUrl: string }&lt;/code&gt; — redirect your customer there immediately.&lt;/p&gt;




&lt;h3&gt;
  
  
  REST API
&lt;/h3&gt;

&lt;h4&gt;
  
  
  cURL
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://sandbox.api.afriex.com/api/v1/checkout-session &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-api-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "amount": 500000,
    "currency": "NGN",
    "merchantReference": "order-2026-05-30-001",
    "redirectUrl": "https://yourapp.com/checkout/return",
    "channels": ["VIRTUAL_BANK_ACCOUNT", "MOBILE_MONEY"],
    "customer": {
      "name": "Amarachi Okafor",
      "email": "amara@example.com",
      "phone": "+2348100000000",
      "countryCode": "NG"
    },
    "metadata": {
      "orderId": "ORD-9912",
      "plan": "pro"
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Node.js (fetch)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createCheckoutSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://sandbox.api.afriex.com/api/v1/checkout-session&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AFRIEX_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amountInKobo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NGN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;merchantReference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://yourapp.com/checkout/return&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;channels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VIRTUAL_BANK_ACCOUNT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MOBILE_MONEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;countryCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NG&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkoutUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Python (requests)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_checkout_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&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;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://sandbox.api.afriex.com/api/v1/checkout-session&lt;/span&gt;&lt;span class="sh"&gt;"&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="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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-api-key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AFRIEX_API_KEY&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;json&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;amount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order&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_in_kobo&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;currency&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;NGN&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;merchantReference&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&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;redirectUrl&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;https://yourapp.com/checkout/return&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;channels&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;VIRTUAL_BANK_ACCOUNT&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;MOBILE_MONEY&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;customer&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer&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;name&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;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer&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;email&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;phone&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer&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;phone&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;countryCode&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;NG&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="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&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="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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;checkoutUrl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Response
&lt;/h4&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;"data"&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;"checkoutUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://sandbox.pay.afriex.com/pay/eyJhbGciOiJI..."&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;p&gt;Redirect your customer to &lt;code&gt;checkoutUrl&lt;/code&gt; immediately. The session has an expiration timer, so don't hold on to the URL for later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Request Reference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Required Fields
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;amount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;Amount in &lt;strong&gt;minor currency units&lt;/strong&gt; (kobo for NGN, cents for USD). Minimum: &lt;code&gt;100&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;currency&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;ISO 4217 currency code, uppercase (e.g. &lt;code&gt;NGN&lt;/code&gt;, &lt;code&gt;GHS&lt;/code&gt;). Must be enabled for checkout on your account.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;merchantReference&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Your unique identifier for this session. Used to match webhooks and reconcile transactions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;redirectUrl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;HTTPS URL the customer is sent to after the checkout flow completes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;customer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;object&lt;/td&gt;
&lt;td&gt;Customer details. See below.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Customer Object
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Customer's full name.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;email&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Customer's email address.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;phone&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Phone number in E.164 format (e.g. &lt;code&gt;+2348100000000&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;countryCode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;ISO 3166-1 alpha-2 country code (e.g. &lt;code&gt;NG&lt;/code&gt;, &lt;code&gt;GH&lt;/code&gt;). Case-insensitive.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Optional Fields
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;channels&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;array&lt;/td&gt;
&lt;td&gt;&lt;code&gt;["VIRTUAL_BANK_ACCOUNT"]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Payment methods to offer. Options: &lt;code&gt;VIRTUAL_BANK_ACCOUNT&lt;/code&gt;, &lt;code&gt;MOBILE_MONEY&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;metadata&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;object&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Flat key/value pairs (strings only) you want attached to the session for your own reference.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Step 2: The Customer Completes Payment
&lt;/h2&gt;

&lt;p&gt;Once redirected, the customer lands on the Afriex-hosted checkout page. Depending on the &lt;code&gt;channels&lt;/code&gt; you enabled, they can:&lt;/p&gt;

&lt;h3&gt;
  
  
  Pay by Bank Transfer
&lt;/h3&gt;

&lt;p&gt;The customer sees a one-time virtual bank account — account name, number, and bank — with the exact amount to transfer. They open their banking app, send the transfer, and return to the checkout page. Afriex detects the inbound transfer and confirms payment automatically.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Exact amount required.&lt;/strong&gt; The customer must transfer the precise amount shown. Sending a different amount will cause delays and may require a manual reconciliation or refund.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Pay by Mobile Money
&lt;/h3&gt;

&lt;p&gt;The customer selects their network (e.g. MTN, Airtel), enters their account name and phone number, and taps &lt;strong&gt;Pay now&lt;/strong&gt;. Afriex pushes an authorization prompt to their phone. Once they approve it, the checkout page updates automatically.&lt;/p&gt;

&lt;p&gt;Both paths end in one of two outcomes: a &lt;strong&gt;success screen&lt;/strong&gt; (auto-redirects to your &lt;code&gt;redirectUrl&lt;/code&gt;) or a &lt;strong&gt;failure screen&lt;/strong&gt; (with guidance to retry or contact support).&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Handle the Redirect
&lt;/h2&gt;

&lt;p&gt;When the customer lands back on your &lt;code&gt;redirectUrl&lt;/code&gt;, show them an appropriate confirmation or error screen. Use the &lt;code&gt;merchantReference&lt;/code&gt; you generated to look up the order state in your system.&lt;/p&gt;

&lt;p&gt;A typical return handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Express.js example&lt;/span&gt;
&lt;span class="nx"&gt;app&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/checkout/return&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// The merchantReference you passed when creating the session&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Look up your order by reference — don't trust query params for payment status&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;paid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/orders/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/confirmation`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Not yet confirmed — webhook may still be in flight&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/orders/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/pending`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't use the redirect to confirm payment.&lt;/strong&gt; The redirect tells you the customer finished the checkout flow, not that payment succeeded. Always use the webhook (see Step 4) as the authoritative signal.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 4: Listen for the Webhook
&lt;/h2&gt;

&lt;p&gt;When Afriex confirms payment, it fires a &lt;code&gt;CHECKOUT_SESSION.CREATED&lt;/code&gt; webhook to the callback URL configured in your Afriex dashboard. This is the source of truth for marking an order as paid.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Express.js webhook handler example&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/webhooks/afriex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CHECKOUT_SESSION.CREATED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;merchantReference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Mark order as paid using your merchantReference&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;markPaid&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;merchantReference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OK&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make your webhook handler idempotent — Afriex may retry delivery if your endpoint doesn't respond with a &lt;code&gt;200&lt;/code&gt;. Using &lt;code&gt;merchantReference&lt;/code&gt; as your idempotency key is the right approach.&lt;/p&gt;




&lt;h2&gt;
  
  
  Beyond the Basic Flow: Advanced Use Cases
&lt;/h2&gt;

&lt;p&gt;The checkout session API supports a few patterns beyond the standard redirect flow that are worth knowing about.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delayed Payment Collection
&lt;/h3&gt;

&lt;p&gt;You don't have to redirect customers immediately after creating a session. You can create the session on your server, store the &lt;code&gt;checkoutUrl&lt;/code&gt;, and send it to the customer later — via email, SMS, or a payment link in an invoice. This is useful for billing flows where payment isn't expected at the point of order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create the session when the invoice is generated&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;afriex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSession&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;invoiceDetails&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Store checkoutUrl against the invoice&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;paymentUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkoutUrl&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Send it later when the invoice is dispatched&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your invoice is ready&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Pay here: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep session expiration in mind — if the payment link will be sent days later, build in a check for whether the session is still valid before sending.&lt;/p&gt;

&lt;h3&gt;
  
  
  Embedded Checkout (Iframe / Webview)
&lt;/h3&gt;

&lt;p&gt;Rather than fully leaving your app, you can open the &lt;code&gt;checkoutUrl&lt;/code&gt; inside an iframe (web) or a webview (mobile). This keeps the customer inside your product's shell while Afriex handles the payment UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Web: iframe embed --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://sandbox.pay.afriex.com/pay/YOUR_SESSION_TOKEN"&lt;/span&gt;
  &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt;
  &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"600"&lt;/span&gt;
  &lt;span class="na"&gt;frameborder=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
  &lt;span class="na"&gt;allow=&lt;/span&gt;&lt;span class="s"&gt;"payment"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// React Native: WebView embed&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;WebView&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-native-webview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WebView&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkoutUrl&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
  &lt;span class="nx"&gt;onNavigationStateChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Detect redirect back to your redirectUrl&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://yourapp.com/checkout/return&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OrderConfirmation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The webhook still fires regardless of how the customer accesses the checkout — embedded or full redirect.&lt;/p&gt;




&lt;h2&gt;
  
  
  Important Notes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Amounts are always in minor units
&lt;/h3&gt;

&lt;p&gt;Afriex uses the smallest denomination of each currency — no decimals in the &lt;code&gt;amount&lt;/code&gt; field:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Currency&lt;/th&gt;
&lt;th&gt;Major unit&lt;/th&gt;
&lt;th&gt;Minor unit&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NGN&lt;/td&gt;
&lt;td&gt;₦1&lt;/td&gt;
&lt;td&gt;1 kobo&lt;/td&gt;
&lt;td&gt;₦5,000 → &lt;code&gt;500000&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GHS&lt;/td&gt;
&lt;td&gt;₵1&lt;/td&gt;
&lt;td&gt;1 pesewa&lt;/td&gt;
&lt;td&gt;₵100 → &lt;code&gt;10000&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USD&lt;/td&gt;
&lt;td&gt;$1&lt;/td&gt;
&lt;td&gt;1 cent&lt;/td&gt;
&lt;td&gt;$10 → &lt;code&gt;1000&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The minimum &lt;code&gt;amount&lt;/code&gt; is &lt;code&gt;100&lt;/code&gt; (equivalent to 1 major unit).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;merchantReference&lt;/code&gt; must be unique
&lt;/h3&gt;

&lt;p&gt;Each session needs a distinct &lt;code&gt;merchantReference&lt;/code&gt;. Re-using a reference from a previous session will result in an error. Use your internal order ID, a UUID, or any other identifier you control.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sessions expire
&lt;/h3&gt;

&lt;p&gt;Each checkout session has an expiration timer visible to the customer. If the session expires before payment completes, the customer will need a new session. Build a recovery path — for example, let customers re-initiate checkout from your order page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phone numbers must be E.164
&lt;/h3&gt;

&lt;p&gt;The customer's &lt;code&gt;phone&lt;/code&gt; must be in E.164 format: &lt;code&gt;+&lt;/code&gt; followed by the country code and subscriber number, no spaces or punctuation. For example, a Nigerian number would be &lt;code&gt;+2348100000000&lt;/code&gt;, not &lt;code&gt;08100000000&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing in Sandbox
&lt;/h2&gt;

&lt;p&gt;All of the above works end-to-end in sandbox today:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use base URL &lt;code&gt;https://sandbox.api.afriex.com&lt;/code&gt; (or set &lt;code&gt;environment: "staging"&lt;/code&gt; in the SDK)&lt;/li&gt;
&lt;li&gt;Use your sandbox API key from the Afriex dashboard&lt;/li&gt;
&lt;li&gt;Successful checkout sessions redirect to &lt;code&gt;https://sandbox.pay.afriex.com/pay/{token}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Webhooks fire to your configured callback URL in sandbox as well&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Test the full round-trip — create a session, go through the hosted checkout, and verify your webhook handler receives and processes the event — before switching to production credentials.&lt;/p&gt;




&lt;h2&gt;
  
  
  Error Responses
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;400&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Invalid request — check required fields, amount minimum, and currency format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;401&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unauthorized — verify your &lt;code&gt;x-api-key&lt;/code&gt; header&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;500&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Server error — retry with exponential backoff&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://afriex-sdk-docs.vercel.app/getting-started" rel="noopener noreferrer"&gt;Afriex SDK Documentation&lt;/a&gt;&lt;/strong&gt; — Full SDK reference covering installation, configuration, and all available modules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://afriex-sdk-docs.vercel.app/api/checkout" rel="noopener noreferrer"&gt;SDK Checkout Reference&lt;/a&gt;&lt;/strong&gt; — SDK-specific parameters and examples for the Checkout API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.afriex.com/api-reference/endpoint/checkout-sessions/user-flow-guide" rel="noopener noreferrer"&gt;User Flow Guide&lt;/a&gt;&lt;/strong&gt; — See the full customer-facing checkout experience, screen by screen&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.afriex.com/api-reference/endpoint/webhooks/introduction" rel="noopener noreferrer"&gt;Webhooks&lt;/a&gt;&lt;/strong&gt; — Configure your callback URL and understand the full webhook payload&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.afriex.com/api-reference/endpoint/checkout-sessions/create" rel="noopener noreferrer"&gt;REST API Reference&lt;/a&gt;&lt;/strong&gt; — Full OpenAPI spec for the Create Checkout Session endpoint&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Afriex Checkout Sessions is currently available in sandbox. Production rollout is expected in the coming weeks — get your integration ready today.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>afriex</category>
      <category>stripe</category>
      <category>fintech</category>
      <category>crossborder</category>
    </item>
    <item>
      <title>What Happens After You Click “Send Money”? Lessons from the Afriex Utila Partnership</title>
      <dc:creator>0xSonOfUri</dc:creator>
      <pubDate>Fri, 29 May 2026 15:42:42 +0000</pubDate>
      <link>https://dev.to/afriex/what-happens-after-you-click-send-money-lessons-from-the-afriex-x-utila-partnership-3o1d</link>
      <guid>https://dev.to/afriex/what-happens-after-you-click-send-money-lessons-from-the-afriex-x-utila-partnership-3o1d</guid>
      <description>&lt;p&gt;Most people think cross-border payments are simple.&lt;/p&gt;

&lt;p&gt;You open an app.&lt;/p&gt;

&lt;p&gt;Enter an amount.&lt;/p&gt;

&lt;p&gt;Choose a recipient.&lt;/p&gt;

&lt;p&gt;Click send.&lt;/p&gt;

&lt;p&gt;A few moments later, the money arrives.&lt;/p&gt;

&lt;p&gt;Simple.&lt;/p&gt;

&lt;p&gt;At least on the surface.&lt;/p&gt;

&lt;p&gt;What most people never see is the infrastructure required to make that experience possible at scale.&lt;/p&gt;

&lt;p&gt;Recently, Utila published a case study about its partnership with Afriex, a company operating cross-border payment corridors across Africa. While the announcement focused on infrastructure, it revealed something much more interesting:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The real challenge in cross-border payments isn't sending money. It's managing liquidity, treasury operations, and settlement across multiple markets simultaneously.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that's where stablecoins are quietly becoming critical infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Hidden Side of Cross-Border Payments
&lt;/h2&gt;

&lt;p&gt;When users think about a transfer, they typically imagine something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sender
   ↓
Payment
   ↓
Recipient
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reality looks closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sender
   ↓
Collection Rail
   ↓
Treasury Layer
   ↓
Liquidity Partner
   ↓
FX Conversion
   ↓
Settlement Layer
   ↓
Local Payout Partner
   ↓
Recipient
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every additional country introduces more complexity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;different currencies&lt;/li&gt;
&lt;li&gt;different banking systems&lt;/li&gt;
&lt;li&gt;different payout partners&lt;/li&gt;
&lt;li&gt;different liquidity requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now imagine operating this across more than 20 African corridors.&lt;/p&gt;

&lt;p&gt;The challenge quickly becomes operational.&lt;/p&gt;




&lt;h2&gt;
  
  
  Afriex's Challenge
&lt;/h2&gt;

&lt;p&gt;According to Utila's case study, Afriex needed the ability to support multiple stablecoins across multiple blockchain networks while settling with payout partners throughout Africa.&lt;/p&gt;

&lt;p&gt;The problem wasn't simply storing assets.&lt;/p&gt;

&lt;p&gt;It was managing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;USDC&lt;/li&gt;
&lt;li&gt;USDT&lt;/li&gt;
&lt;li&gt;multiple blockchain networks&lt;/li&gt;
&lt;li&gt;treasury movements&lt;/li&gt;
&lt;li&gt;partner settlements&lt;/li&gt;
&lt;li&gt;growing B2B customer operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;all from a unified operational layer.&lt;/p&gt;

&lt;p&gt;Without consolidation, these workflows often require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multiple exchanges&lt;/li&gt;
&lt;li&gt;multiple wallets&lt;/li&gt;
&lt;li&gt;manual treasury processes&lt;/li&gt;
&lt;li&gt;fragmented operational visibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As transaction volume grows, that model becomes increasingly difficult to manage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Stablecoins Matter
&lt;/h2&gt;

&lt;p&gt;When most people hear "stablecoins," they think about crypto.&lt;/p&gt;

&lt;p&gt;But infrastructure teams increasingly think about them differently.&lt;/p&gt;

&lt;p&gt;They think about settlement.&lt;/p&gt;

&lt;p&gt;Stablecoins offer something traditional payment systems often struggle to provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;global interoperability&lt;/li&gt;
&lt;li&gt;24/7 settlement&lt;/li&gt;
&lt;li&gt;programmable transfers&lt;/li&gt;
&lt;li&gt;asset portability across networks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For payment companies, stablecoins are increasingly functioning as operational rails rather than speculative assets.&lt;/p&gt;

&lt;p&gt;That's a significant shift.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Infrastructure Layer
&lt;/h2&gt;

&lt;p&gt;One detail from the case study stood out.&lt;/p&gt;

&lt;p&gt;Afriex needed to support:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;any stablecoin on any chain&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;for partner settlements across African markets.&lt;/p&gt;

&lt;p&gt;That sounds simple.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;Different counterparties may require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;different assets&lt;/li&gt;
&lt;li&gt;different networks&lt;/li&gt;
&lt;li&gt;different settlement preferences&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Supporting that flexibility often introduces substantial operational complexity.&lt;/p&gt;

&lt;p&gt;According to the case study, Utila provided a single platform that enabled:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multi-asset support&lt;/li&gt;
&lt;li&gt;multi-chain support&lt;/li&gt;
&lt;li&gt;treasury operations&lt;/li&gt;
&lt;li&gt;wallet provisioning&lt;/li&gt;
&lt;li&gt;settlement workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;from one operational layer.&lt;/p&gt;

&lt;p&gt;The result was less fragmentation and greater operational control.&lt;/p&gt;




&lt;h2&gt;
  
  
  Beyond Treasury: Wallet Infrastructure
&lt;/h2&gt;

&lt;p&gt;Another interesting outcome was wallet provisioning.&lt;/p&gt;

&lt;p&gt;As Afriex expanded its B2B platform, business customers needed dedicated wallet infrastructure.&lt;/p&gt;

&lt;p&gt;Rather than treating wallets as an internal treasury tool, wallets became part of the customer-facing product experience.&lt;/p&gt;

&lt;p&gt;Businesses could receive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dedicated wallet addresses&lt;/li&gt;
&lt;li&gt;preferred stablecoin support&lt;/li&gt;
&lt;li&gt;preferred network support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;while Afriex managed the infrastructure centrally.&lt;/p&gt;

&lt;p&gt;This highlights an important trend:&lt;/p&gt;

&lt;p&gt;Stablecoin infrastructure is increasingly becoming product infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Trend
&lt;/h2&gt;

&lt;p&gt;The Afriex × Utila story reflects a broader industry movement.&lt;/p&gt;

&lt;p&gt;Stablecoins are no longer only consumer products.&lt;/p&gt;

&lt;p&gt;They are becoming:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;treasury infrastructure&lt;/li&gt;
&lt;li&gt;settlement infrastructure&lt;/li&gt;
&lt;li&gt;liquidity infrastructure&lt;/li&gt;
&lt;li&gt;payment infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Across the payments industry, companies are increasingly evaluating stablecoins as an alternative settlement layer because of their speed, global accessibility, and operational flexibility.&lt;/p&gt;

&lt;p&gt;In many ways, stablecoins are evolving into the backend infrastructure powering modern money movement.&lt;/p&gt;

&lt;p&gt;Users may never see them.&lt;/p&gt;

&lt;p&gt;But they increasingly benefit from them.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Lesson
&lt;/h2&gt;

&lt;p&gt;The most interesting takeaway from the Afriex × Utila partnership isn't that a fintech adopted stablecoins.&lt;/p&gt;

&lt;p&gt;It's that operational complexity becomes the biggest challenge as payment systems scale.&lt;/p&gt;

&lt;p&gt;Building a payment product is one thing.&lt;/p&gt;

&lt;p&gt;Operating liquidity, settlements, treasury, and customer infrastructure across dozens of markets is another challenge entirely.&lt;/p&gt;

&lt;p&gt;The companies that succeed are often the ones that find ways to simplify that complexity.&lt;/p&gt;

&lt;p&gt;That's what makes this case study interesting.&lt;/p&gt;

&lt;p&gt;It's a reminder that behind every successful payment experience is an enormous amount of infrastructure working quietly in the background.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Cross-border payments are often discussed from the perspective of user experience.&lt;/p&gt;

&lt;p&gt;Faster transfers.&lt;/p&gt;

&lt;p&gt;Lower fees.&lt;/p&gt;

&lt;p&gt;Better access.&lt;/p&gt;

&lt;p&gt;But infrastructure tells a different story.&lt;/p&gt;

&lt;p&gt;The real challenge is coordinating liquidity, settlements, treasury operations, and customer assets across a fragmented financial landscape.&lt;/p&gt;

&lt;p&gt;The Afriex × Utila partnership offers a glimpse into how modern payment companies are approaching that challenge.&lt;/p&gt;

&lt;p&gt;And increasingly, stablecoins are becoming part of the answer.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>cryptocurrency</category>
      <category>infrastructure</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>What Happens When AI Agents Can Access Payment Infrastructure? Exploring OpenClaw + Afriex MCP</title>
      <dc:creator>0xSonOfUri</dc:creator>
      <pubDate>Fri, 29 May 2026 15:24:25 +0000</pubDate>
      <link>https://dev.to/afriex/what-happens-when-ai-agents-can-access-payment-infrastructure-exploring-openclaw-afriex-mcp-381e</link>
      <guid>https://dev.to/afriex/what-happens-when-ai-agents-can-access-payment-infrastructure-exploring-openclaw-afriex-mcp-381e</guid>
      <description>&lt;p&gt;For years, we've built APIs for developers.&lt;/p&gt;

&lt;p&gt;Every payment gateway, banking platform, fintech API, and infrastructure provider has been designed around a simple assumption:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A human developer writes the code that interacts with the API.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But what happens when AI agents become the users of those APIs?&lt;/p&gt;

&lt;p&gt;That's the question I've been thinking about while exploring OpenClaw and Afriex MCP.&lt;/p&gt;

&lt;p&gt;Not from the perspective of replacing developers.&lt;/p&gt;

&lt;p&gt;But from the perspective of enabling AI systems to interact with infrastructure in meaningful ways.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Shift From Code Generation to Infrastructure Interaction
&lt;/h2&gt;

&lt;p&gt;Most developers have already experienced AI-assisted coding.&lt;/p&gt;

&lt;p&gt;We use tools like Cursor to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generate components&lt;/li&gt;
&lt;li&gt;write tests&lt;/li&gt;
&lt;li&gt;explain code&lt;/li&gt;
&lt;li&gt;scaffold projects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But that's still fundamentally code generation.&lt;/p&gt;

&lt;p&gt;The AI helps create software.&lt;/p&gt;

&lt;p&gt;The developer remains the bridge between the software and the infrastructure.&lt;/p&gt;

&lt;p&gt;The next evolution is different.&lt;/p&gt;

&lt;p&gt;The AI doesn't just generate code.&lt;/p&gt;

&lt;p&gt;The AI interacts with infrastructure directly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enter OpenClaw
&lt;/h2&gt;

&lt;p&gt;OpenClaw is an open framework for building AI agents capable of interacting with tools and external systems.&lt;/p&gt;

&lt;p&gt;Instead of simply responding to prompts, agents can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;perform actions&lt;/li&gt;
&lt;li&gt;execute workflows&lt;/li&gt;
&lt;li&gt;use tools&lt;/li&gt;
&lt;li&gt;interact with services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This transforms the AI from a conversational assistant into something much more operational.&lt;/p&gt;

&lt;p&gt;The interesting question becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What tools should these agents have access to?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that's where infrastructure enters the picture.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enter Afriex MCP
&lt;/h2&gt;

&lt;p&gt;Afriex MCP exposes Afriex capabilities through the Model Context Protocol.&lt;/p&gt;

&lt;p&gt;This means AI-enabled tools and agents can interact with payment infrastructure through structured tooling.&lt;/p&gt;

&lt;p&gt;Instead of building everything manually first, an agent can understand and interact with financial primitives such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;balances&lt;/li&gt;
&lt;li&gt;virtual accounts&lt;/li&gt;
&lt;li&gt;transactions&lt;/li&gt;
&lt;li&gt;payment workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;through MCP-enabled interfaces.&lt;/p&gt;

&lt;p&gt;On their own, OpenClaw and Afriex MCP are interesting.&lt;/p&gt;

&lt;p&gt;Together, they become much more compelling.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Could an Infrastructure-Aware Agent Do?
&lt;/h2&gt;

&lt;p&gt;Imagine an AI agent with access to payment infrastructure.&lt;/p&gt;

&lt;p&gt;Not in a hypothetical science-fiction future.&lt;/p&gt;

&lt;p&gt;Today.&lt;/p&gt;




&lt;h2&gt;
  
  
  Balance Monitoring
&lt;/h2&gt;

&lt;p&gt;An agent could continuously monitor balances across accounts.&lt;/p&gt;

&lt;p&gt;When thresholds are reached, it could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;notify teams&lt;/li&gt;
&lt;li&gt;trigger workflows&lt;/li&gt;
&lt;li&gt;generate reports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;without manual intervention.&lt;/p&gt;




&lt;h2&gt;
  
  
  Payment Operations
&lt;/h2&gt;

&lt;p&gt;An agent could generate receiving instructions for customers.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Create a virtual account for Customer X.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent retrieves the required information and returns payment instructions immediately.&lt;/p&gt;




&lt;h2&gt;
  
  
  Transaction Monitoring
&lt;/h2&gt;

&lt;p&gt;Instead of manually checking dashboards, agents could monitor transaction events and surface only what requires human attention.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;failed transactions&lt;/li&gt;
&lt;li&gt;delayed settlements&lt;/li&gt;
&lt;li&gt;unusual activity&lt;/li&gt;
&lt;li&gt;reconciliation issues&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Developer Workflows
&lt;/h2&gt;

&lt;p&gt;This is the area that excites me most.&lt;/p&gt;

&lt;p&gt;Imagine asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Generate a payment integration using Afriex.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead of simply generating generic code, the agent understands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the infrastructure&lt;/li&gt;
&lt;li&gt;available tools&lt;/li&gt;
&lt;li&gt;workflow patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and builds with real context.&lt;/p&gt;

&lt;p&gt;The difference is subtle.&lt;/p&gt;

&lt;p&gt;But significant.&lt;/p&gt;




&lt;h2&gt;
  
  
  APIs Were Built for Developers
&lt;/h2&gt;

&lt;p&gt;One idea keeps coming back to me.&lt;/p&gt;

&lt;p&gt;For decades, we've designed APIs around human developers.&lt;/p&gt;

&lt;p&gt;Documentation.&lt;br&gt;
SDKs.&lt;br&gt;
Authentication.&lt;br&gt;
Request-response patterns.&lt;/p&gt;

&lt;p&gt;Everything assumes a human sits in the middle.&lt;/p&gt;

&lt;p&gt;But MCP introduces a different model.&lt;/p&gt;

&lt;p&gt;Infrastructure becomes accessible not only to developers, but also to AI systems.&lt;/p&gt;

&lt;p&gt;That changes how we think about integrations.&lt;/p&gt;

&lt;p&gt;It changes how we think about tooling.&lt;/p&gt;

&lt;p&gt;And eventually, it may change how software gets built.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Fintech Is Particularly Interesting
&lt;/h2&gt;

&lt;p&gt;Many industries can benefit from infrastructure-aware agents.&lt;/p&gt;

&lt;p&gt;Fintech stands out because financial workflows are already highly structured.&lt;/p&gt;

&lt;p&gt;Consider how much time teams spend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;monitoring transactions&lt;/li&gt;
&lt;li&gt;checking balances&lt;/li&gt;
&lt;li&gt;reconciling payments&lt;/li&gt;
&lt;li&gt;handling operational workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are precisely the kinds of activities agents can assist with.&lt;/p&gt;

&lt;p&gt;Not replacing humans.&lt;/p&gt;

&lt;p&gt;Augmenting them.&lt;/p&gt;

&lt;p&gt;Making teams more efficient.&lt;/p&gt;

&lt;p&gt;Reducing operational friction.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Picture
&lt;/h2&gt;

&lt;p&gt;I don't think the most interesting outcome is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI writes code faster.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think the more interesting outcome is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI understands and interacts with infrastructure.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a much bigger shift.&lt;/p&gt;

&lt;p&gt;Because once agents can interact with infrastructure safely and predictably, entirely new workflows become possible.&lt;/p&gt;

&lt;p&gt;OpenClaw provides the agent layer.&lt;/p&gt;

&lt;p&gt;Afriex MCP provides the infrastructure layer.&lt;/p&gt;

&lt;p&gt;Together they offer a glimpse into what AI-native financial systems might look like.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;We're still early.&lt;/p&gt;

&lt;p&gt;Most of these patterns are only beginning to emerge.&lt;/p&gt;

&lt;p&gt;But one thing feels increasingly clear:&lt;/p&gt;

&lt;p&gt;The future isn't just AI-assisted development.&lt;/p&gt;

&lt;p&gt;It's infrastructure-aware AI.&lt;/p&gt;

&lt;p&gt;And as developers, it's worth paying attention to what becomes possible when agents can interact with the systems we've spent years building.&lt;/p&gt;

&lt;p&gt;OpenClaw and Afriex MCP are an interesting place to start exploring that future.&lt;/p&gt;




</description>
      <category>mcp</category>
      <category>openclaw</category>
      <category>afriex</category>
      <category>fintech</category>
    </item>
    <item>
      <title>Afriex vs Flutterwave vs Paystack: Which One Should You Choose?</title>
      <dc:creator>Victory Lucky</dc:creator>
      <pubDate>Mon, 25 May 2026 12:56:13 +0000</pubDate>
      <link>https://dev.to/afriex/afriex-vs-flutterwave-vs-paystack-which-one-should-you-use-for-payouts-and-disbursements-20jc</link>
      <guid>https://dev.to/afriex/afriex-vs-flutterwave-vs-paystack-which-one-should-you-use-for-payouts-and-disbursements-20jc</guid>
      <description>&lt;p&gt;If you are building something that collects payments from customers, Paystack and Flutterwave are both solid choices and you probably already know that. This article is not about that.&lt;/p&gt;

&lt;p&gt;This is about the other direction: sending money out. Paying a freelancer in Nairobi from a USD balance. Disbursing contractor fees to someone in Accra via MTN Mobile Money. Running payroll across five African countries without building a separate integration for each one. That is a different problem, and it needs a different tool.&lt;/p&gt;

&lt;p&gt;The honest answer is that Paystack and Flutterwave were built collection-first. They handle disbursements, but with limits that matter once you are operating across borders. Afriex was built disbursement-first, for African corridors specifically. The comparison is not that one is better overall — it is that they solve different problems, and picking the wrong one for outbound payments means hitting walls you did not expect.&lt;/p&gt;

&lt;p&gt;Here is how they actually compare.&lt;/p&gt;




&lt;h2&gt;
  
  
  What we are comparing
&lt;/h2&gt;

&lt;p&gt;Three things matter for outbound disbursement:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coverage&lt;/strong&gt; — which countries and payment channels can you actually pay out to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Developer experience&lt;/strong&gt; — how the API is structured, what you need to do before money can move, how status updates work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fit for the use case&lt;/strong&gt; — whether the tool was designed for this job or adapted to it.&lt;/p&gt;

&lt;p&gt;We are not comparing collection features, pricing pages, or dashboard UX. Those are separate decisions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Coverage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Paystack
&lt;/h3&gt;

&lt;p&gt;Paystack is currently available for companies based in Nigeria, South Africa, Ghana, Kenya, and Côte d'Ivoire, supporting NGN, GHS, ZAR, KES, and USD (USD only in Kenya and Nigeria).&lt;/p&gt;

&lt;p&gt;For outbound transfers, Paystack supports bank account payouts within those same markets. It primarily settles in local currencies — if someone pays in USD via a foreign card, funds are often converted to NGN or another local currency. USD payout to a recipient abroad is not a native flow.&lt;/p&gt;

&lt;p&gt;If your recipients are Nigerian businesses with local NGN bank accounts and you are already using Paystack for collections, the transfers API works and settlement is fast. The moment your disbursement needs go outside Nigeria, Ghana, Kenya, or South Africa — or you need to pay in USD without conversion — Paystack starts to show its limits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flutterwave
&lt;/h3&gt;

&lt;p&gt;Flutterwave is supported in Nigeria, Ghana, Kenya, South Africa, Uganda, Tanzania, the United Kingdom, the United States, and Europe. It processes payouts in 30+ currencies and has broader mobile money coverage than Paystack across East and West Africa.&lt;/p&gt;

&lt;p&gt;Flutterwave is the wider net of the two, and for businesses that need to pay out in markets like Uganda and Tanzania where Paystack does not operate, it is the more practical choice. The transfers API is well-documented and the mobile money coverage is real.&lt;/p&gt;

&lt;p&gt;The gap is that Flutterwave is still fundamentally a collection platform that also does transfers. The payout flow works, but the API was not designed with multi-country disbursement as its primary job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Afriex
&lt;/h3&gt;

&lt;p&gt;Afriex covers &lt;a href="https://docs.afriex.com/api-reference/supported-currencies" rel="noopener noreferrer"&gt;31 African countries&lt;/a&gt; with payout rails across bank accounts, mobile money (MTN MoMo, M-Pesa, Airtel Money, and others), and SWIFT. Beyond Africa, USD SWIFT payouts reach 100 countries globally, including the US, UK, all of Europe, India, China, Canada, and more. Payment channels include bank transfers, mobile money, SWIFT, UPI (India), Interac (Canada), WeChat Pay and Alipay (China), and crypto (USDC/USDT).&lt;/p&gt;

&lt;p&gt;The coverage table for African countries specifically:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Country&lt;/th&gt;
&lt;th&gt;Payout Rails&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nigeria&lt;/td&gt;
&lt;td&gt;Bank Account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kenya&lt;/td&gt;
&lt;td&gt;Bank Account, Mobile Money, Paybill/Till&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ghana&lt;/td&gt;
&lt;td&gt;Bank Account, Mobile Money&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uganda&lt;/td&gt;
&lt;td&gt;Bank Account, Mobile Money&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tanzania&lt;/td&gt;
&lt;td&gt;Mobile Money&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cameroon&lt;/td&gt;
&lt;td&gt;Bank Account, Mobile Money&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Côte d'Ivoire&lt;/td&gt;
&lt;td&gt;Bank Account, Mobile Money&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ethiopia&lt;/td&gt;
&lt;td&gt;Bank Account, Mobile Money&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Egypt&lt;/td&gt;
&lt;td&gt;Bank Account, Mobile Money&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;South Africa&lt;/td&gt;
&lt;td&gt;Bank Account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rwanda&lt;/td&gt;
&lt;td&gt;Bank Account, Mobile Money&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senegal&lt;/td&gt;
&lt;td&gt;Bank Account, Mobile Money&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+ 19 more African countries&lt;/td&gt;
&lt;td&gt;Mobile Money&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The difference is not just the number of countries. It is that mobile money is a first-class channel in the Afriex API, not an add-on. In Kenya, you can pay to a bank account, an M-Pesa wallet, or a Paybill/Till number from the same integration. In Ghana, bank and MTN MoMo are both available. For many recipients in these markets, mobile money is their primary financial account, not a fallback.&lt;/p&gt;




&lt;h2&gt;
  
  
  Developer experience
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How Paystack transfers work
&lt;/h3&gt;

&lt;p&gt;Paystack's transfers API follows a straightforward pattern: create a transfer recipient (stores the bank account details), then initiate a transfer to that recipient. The API is clean and the docs are good. The limitation is that it is scoped to Paystack's supported markets, so there is no concept of channel selection — bank account is the primary output.&lt;/p&gt;

&lt;p&gt;For a developer building payroll or contractor payouts strictly within Nigeria, the API is simple and it works. For anything involving mobile money payouts or recipients outside Paystack's five markets, you are building a separate integration.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Flutterwave transfers work
&lt;/h3&gt;

&lt;p&gt;Flutterwave's transfer API accepts bank account details directly without a separate recipient creation step, which makes it slightly lighter for one-off payouts. Mobile money transfers are supported for several African markets. The documentation covers the major corridors well.&lt;/p&gt;

&lt;p&gt;The gap developers hit in production is reliability. Flutterwave offers broad African coverage across 34 countries with extensive mobile money integration but has more variable API reliability than Paystack or Stripe. For a payroll system where every transfer matters, variable reliability in production is a real constraint to account for.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Afriex transfers work
&lt;/h3&gt;

&lt;p&gt;The Afriex API uses a three-step model: create a customer, attach a payment method, create a transaction. It is slightly more upfront setup than Flutterwave's direct transfer approach, but the structure is deliberate — the payment method record stores verified account details that can be reused across multiple payouts without re-entering them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /api/v1/customer       → returns customerId
POST /api/v1/payment-method → returns paymentMethodId  
POST /api/v1/transaction    → uses both IDs, returns transactionId
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a payroll use case, this is actually the right model. You register an employee once, attach their bank account or mobile wallet once, and then every subsequent payout is a single transaction call with the stored IDs. Repeat disbursements to the same recipient require no additional setup.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;meta.idempotencyKey&lt;/code&gt; field on every transaction prevents duplicates if a job retries after a network failure. The &lt;code&gt;reference&lt;/code&gt; field maps every transaction back to your internal records. Both are required, which enforces good practice.&lt;/p&gt;

&lt;p&gt;Transaction types map cleanly to the three outbound flows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;WITHDRAW&lt;/code&gt; — send money from your Afriex wallet to a recipient's account&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DEPOSIT&lt;/code&gt; — pull money from a customer's account into your wallet&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SWAP&lt;/code&gt; — convert between currencies in your Afriex wallet without touching any external account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For disbursement specifically, &lt;code&gt;WITHDRAW&lt;/code&gt; is the one you use.&lt;/p&gt;




&lt;h2&gt;
  
  
  Status updates and webhooks
&lt;/h2&gt;

&lt;p&gt;This is where the tools diverge most practically for anyone building a reliable disbursement system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paystack and Flutterwave
&lt;/h3&gt;

&lt;p&gt;Both support webhooks for transfer status updates. The status vocabulary is standard: pending, success, failed. For domestic transfers that settle quickly, this is sufficient. For cross-border payouts that can sit in intermediate states for longer — especially in corridors with compliance review steps — a simple success/failed model leaves you with less visibility than you need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Afriex
&lt;/h3&gt;

&lt;p&gt;The Afriex webhook system fires on &lt;code&gt;TRANSACTION.CREATED&lt;/code&gt; and &lt;code&gt;TRANSACTION.UPDATED&lt;/code&gt;. &lt;br&gt;
Here's the full status vocabulary:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;What it means for your system&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PENDING&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Received, not yet processed — do nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PROCESSING&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Actively moving — do nothing, wait&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;COMPLETED&lt;/code&gt; / &lt;code&gt;SUCCESS&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Settled — notify recipient, update your records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IN_REVIEW&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Under compliance review — notify the user, do not mark as failed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RETRY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Network is retrying automatically — do nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FAILED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Terminal failure — alert user, allow retry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;REJECTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rejected after review — alert user with reason&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;REFUNDED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Funds returned — update your records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UNKNOWN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Indeterminate — alert your team to investigate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;IN_REVIEW&lt;/code&gt; and &lt;code&gt;RETRY&lt;/code&gt; statuses are the ones that matter most for cross-border corridors. &lt;code&gt;IN_REVIEW&lt;/code&gt; reflects a real compliance hold that African payment networks produce — it is not a failure state, and treating it as one causes support noise for payouts that are actually in progress. &lt;code&gt;RETRY&lt;/code&gt; means the network is handling it without any action needed from your code.&lt;/p&gt;

&lt;p&gt;Webhooks are signed with RSA-SHA256 and verified against your public key from the dashboard. Afriex retries delivery up to 12 times with exponential backoff from 30 seconds to 16 hours, which means a temporary outage on your end does not mean lost status updates.&lt;/p&gt;

&lt;p&gt;The full webhook implementation guide is covered in the &lt;a href="https://dev.to/afriex/how-to-handle-afriex-webhooks-the-right-way"&gt;Afriex webhook article&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to use each one
&lt;/h2&gt;

&lt;p&gt;This is the honest summary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Paystack transfers if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your recipients are in Nigeria, Ghana, Kenya, South Africa, or Côte d'Ivoire only&lt;/li&gt;
&lt;li&gt;You are already using Paystack for collection and want a single integration&lt;/li&gt;
&lt;li&gt;Your payout volume is primarily NGN bank transfers with fast local settlement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use Flutterwave transfers if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need to pay out in markets Paystack does not cover, like Uganda or Tanzania&lt;/li&gt;
&lt;li&gt;You need mobile money coverage across multiple East and West African markets&lt;/li&gt;
&lt;li&gt;Your use case tolerates some variability in API reliability for the sake of broader coverage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use Afriex if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You are building a product where disbursement is the primary job: payroll, contractor payouts, scholarship disbursements, creator payouts&lt;/li&gt;
&lt;li&gt;Your recipients are spread across African countries and corridors, including mobile money wallets&lt;/li&gt;
&lt;li&gt;You need to pay out in USD to recipients outside Africa via SWIFT without a separate integration&lt;/li&gt;
&lt;li&gt;You need a detailed transaction status model that maps to how African payment networks actually behave&lt;/li&gt;
&lt;li&gt;You want to store recipient payment method details once and reuse them across multiple payouts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The cleanest setup for a product that does both collection and disbursement is: Paystack or Flutterwave for inbound (they are better positioned for that job), and Afriex for outbound. They are complementary, not competing. Paystack collects your revenue, Afriex pays your contractors.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting started with Afriex disbursements
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://business.afriex.com/" rel="noopener noreferrer"&gt;Afriex Business API&lt;/a&gt; has a free sandbox environment. No approval gate, no manual credentials process to get through before you can test. Create an account, grab your sandbox API key from &lt;strong&gt;Settings &amp;gt; API Keys&lt;/strong&gt;, and you can run the full disbursement flow — customer creation, payment method attachment, transaction execution, webhook delivery — before you touch production.&lt;/p&gt;

&lt;p&gt;The integration guide covering the full three-step flow with code examples is in the &lt;a href="https://dev.to/afriex/build-a-freelancer-payout-platform-with-the-afriex-sdk-and-nextjs"&gt;freelancer payout platform tutorial&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>afriex</category>
      <category>crossborder</category>
      <category>fintech</category>
      <category>stripe</category>
    </item>
    <item>
      <title>Afriex Webhook Integration Guide: Signature Verification, Event Handling, and Production Best Practices</title>
      <dc:creator>Victory Lucky</dc:creator>
      <pubDate>Mon, 25 May 2026 01:17:44 +0000</pubDate>
      <link>https://dev.to/afriex/afriex-webhook-integration-guide-signature-verification-event-handling-and-production-best-3c9o</link>
      <guid>https://dev.to/afriex/afriex-webhook-integration-guide-signature-verification-event-handling-and-production-best-3c9o</guid>
      <description>&lt;p&gt;When you create a transaction through the Afriex Business API, the response you get back is just the start. The transaction comes back with a status of &lt;code&gt;PENDING&lt;/code&gt;. What happens after that — whether it moves to &lt;code&gt;PROCESSING&lt;/code&gt;, &lt;code&gt;COMPLETED&lt;/code&gt;, &lt;code&gt;IN_REVIEW&lt;/code&gt;, or &lt;code&gt;FAILED&lt;/code&gt; arrives through webhooks.&lt;/p&gt;

&lt;p&gt;Most integration bugs in payment systems trace back to webhook handling, not the API calls themselves. Missed signature verification. Handlers that time out. Status updates applied twice. Fields read from parsed JSON instead of the raw body. These are the mistakes that cause payouts to look settled when they are not, or trigger duplicate notifications to your users.&lt;/p&gt;

&lt;p&gt;This article covers how Afriex webhooks work, every event the system fires, how to verify signatures correctly, how to build a handler that holds up in production, and how to test locally before you go live.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Afriex sends and when
&lt;/h2&gt;

&lt;p&gt;Afriex fires a signed HTTP POST to your configured webhook URL whenever a resource changes. Three resource types generate events.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transaction events&lt;/strong&gt; are the ones you will interact with most. Every time a transaction is created or its status changes, Afriex fires either &lt;code&gt;TRANSACTION.CREATED&lt;/code&gt; or &lt;code&gt;TRANSACTION.UPDATED&lt;/code&gt;. Here's the full status vocabulary that a transaction moves through:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PENDING&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Transaction received, waiting to be processed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PROCESSING&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Actively being processed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COMPLETED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Settled successfully&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SUCCESS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Alias for a settled transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FAILED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Failed. Check &lt;code&gt;meta&lt;/code&gt; for details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CANCELLED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cancelled before processing started&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;REFUNDED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Funds returned to sender&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IN_REVIEW&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Under manual review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;REJECTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rejected after review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RETRY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Being automatically retried by the network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UNKNOWN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Status could not be determined. Contact support&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Customer events&lt;/strong&gt; fire when a customer is created (&lt;code&gt;CUSTOMER.CREATED&lt;/code&gt;), their details are updated (&lt;code&gt;CUSTOMER.UPDATED&lt;/code&gt;), or they are deleted (&lt;code&gt;CUSTOMER.DELETED&lt;/code&gt;). These are useful for keeping your local customer records in sync with Afriex.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Payment method events&lt;/strong&gt; fire on creation (&lt;code&gt;PAYMENT_METHOD.CREATED&lt;/code&gt;), update (&lt;code&gt;PAYMENT_METHOD.UPDATED&lt;/code&gt;), and deletion (&lt;code&gt;PAYMENT_METHOD.DELETED&lt;/code&gt;). If a payment method is deleted on the Afriex side, your application needs to know so it can prompt the user to attach a new one before the next payout.&lt;/p&gt;

&lt;p&gt;There is also &lt;code&gt;CHECKOUT_SESSION.CREATED&lt;/code&gt; for checkout flows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before you go live: allowlist the IP addresses
&lt;/h2&gt;

&lt;p&gt;This step catches developers off guard. Before Afriex can deliver webhooks to your server, your firewall must allow inbound traffic from Afriex's IP addresses. Webhook requests from any other IP should be blocked regardless of whether the signature is valid.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Environment&lt;/th&gt;
&lt;th&gt;IP Address&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sandbox&lt;/td&gt;
&lt;td&gt;&lt;code&gt;34.234.189.210&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production&lt;/td&gt;
&lt;td&gt;&lt;code&gt;34.197.33.100&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Add these to your firewall or security group allowlist. Without this, Afriex webhook requests will be silently blocked before they reach your handler.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting up your endpoint
&lt;/h2&gt;

&lt;p&gt;In your &lt;a href="https://business.afriex.com/" rel="noopener noreferrer"&gt;Afriex dashboard&lt;/a&gt;, go to &lt;strong&gt;Developers&lt;/strong&gt; then the &lt;strong&gt;Webhooks&lt;/strong&gt; tab. Paste your endpoint URL and save. Your webhook public key is on the same screen — copy it and store it as an environment variable. You will need it for signature verification.&lt;/p&gt;

&lt;p&gt;Staging and production use different public keys. Make sure you are using the correct one for each environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Signature verification
&lt;/h2&gt;

&lt;p&gt;Every webhook Afriex sends includes an &lt;code&gt;x-webhook-signature&lt;/code&gt; header. This is a Base64-encoded RSA-SHA256 signature of the raw request body, signed with Afriex's private key. You verify it using the public key from your dashboard.&lt;/p&gt;

&lt;p&gt;Two things to get right here that developers frequently get wrong:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verify against the raw body, not parsed JSON.&lt;/strong&gt; If you parse the body to JSON first and then try to verify the signature against the re-serialized string, it will fail. The signature was computed against the exact bytes Afriex sent. Any transformation — even a whitespace difference — breaks it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reject the request immediately if verification fails.&lt;/strong&gt; Do not process the payload. Do not log it as a real event. Return &lt;code&gt;400&lt;/code&gt; or &lt;code&gt;401&lt;/code&gt; and stop.&lt;/p&gt;

&lt;p&gt;Here is the correct verification implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;verifyWebhookSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;verifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createVerify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RSA-SHA256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;verifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;verifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a Next.js API route, you need to read the raw body before any parsing happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/api/webhooks/route.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-webhook-signature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Missing signature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Read raw body before any parsing&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;verifyWebhookSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AFRIEX_WEBHOOK_PUBLIC_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid signature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// handle payload&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a Fastify application, you need to preserve the raw body before Fastify's JSON parser consumes it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Register this plugin before routes&lt;/span&gt;
&lt;span class="nx"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addContentTypeParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;parseAs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// Attach raw string to request for webhook verification&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;rawBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The payload structure
&lt;/h2&gt;

&lt;p&gt;Every Afriex webhook follows the same envelope:&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;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TRANSACTION.UPDATED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&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="err"&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;p&gt;The &lt;code&gt;event&lt;/code&gt; field tells you what happened. The &lt;code&gt;data&lt;/code&gt; field contains the resource. Here is what each resource type looks like:&lt;/p&gt;

&lt;h3&gt;
  
  
  Transaction payload
&lt;/h3&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;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TRANSACTION.UPDATED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&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;"transactionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"69d60071ab82306f11b03393"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"COMPLETED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WITHDRAW"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sourceAmount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.28847"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sourceCurrency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"destinationAmount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"destinationCurrency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NGN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"destinationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"690df3281c11eea59108fcaf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"customerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"69528240ba52c13b669fb239"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"meta"&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;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ref-withdraw-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"idempotencyKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"idem-withdraw-001"&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;"createdAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-08T07:14:57.444Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"updatedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-08T07:15:30.000Z"&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;h3&gt;
  
  
  Customer payload
&lt;/h3&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;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CUSTOMER.UPDATED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&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;"customerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"698b0440cba7ec3daee9163d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Smith"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"johnsmith@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"phone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"+2348012345678"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"countryCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"meta"&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;"createdAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-10T10:11:12.415Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"updatedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-11T15:30:45.123Z"&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;h3&gt;
  
  
  Payment method payload
&lt;/h3&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;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PAYMENT_METHOD.DELETED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&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;"paymentMethodId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"69f87b0dcc0ee96511560796"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"channel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BANK_ACCOUNT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"customerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"6922e4520a53e858ab42efa8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"institution"&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;"institutionCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"058"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"institutionName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GTBank"&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;"accountName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Smith"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"accountNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1234567890"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"countryCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NG"&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;
  
  
  Building a production-grade handler
&lt;/h2&gt;

&lt;p&gt;A webhook handler has one job: acknowledge receipt fast, then process asynchronously. Afriex expects a &lt;code&gt;2xx&lt;/code&gt; response within about 5 seconds. If your handler does database writes, sends emails, or calls other APIs synchronously before returning, you will hit that window under any real load.&lt;/p&gt;

&lt;p&gt;The pattern that holds up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify signature&lt;/li&gt;
&lt;li&gt;Return &lt;code&gt;200&lt;/code&gt; immediately&lt;/li&gt;
&lt;li&gt;Push the raw payload to a queue&lt;/li&gt;
&lt;li&gt;Process in a background worker
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Lean handler — verify, acknowledge, enqueue&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-webhook-signature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Missing signature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;verifyWebhookSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AFRIEX_WEBHOOK_PUBLIC_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid signature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Enqueue for async processing — do not process inline&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webhook&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&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="na"&gt;received&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handle each event correctly
&lt;/h3&gt;

&lt;p&gt;Not every status requires the same response. Here is a decision map for transaction events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleTransactionEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TransactionWebhookPayload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Update your database first&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateTransactionStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PROCESSING&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// Informational — no user-facing action needed&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;COMPLETED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SUCCESS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// Terminal success — notify user, update UI state&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendPayoutConfirmationEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;IN_REVIEW&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// Compliance hold — notify user that payout is under review&lt;/span&gt;
      &lt;span class="c1"&gt;// Do not mark as failed. Wait for further updates.&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;notifyPayoutUnderReview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RETRY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// Network is retrying automatically — no action needed&lt;/span&gt;
      &lt;span class="c1"&gt;// Do not alarm the user&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FAILED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;REJECTED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// Terminal failure — notify user, allow them to retry&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendPayoutFailedAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UNKNOWN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// Indeterminate — log and alert your team to investigate&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;alertTeam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Transaction &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; reached UNKNOWN status`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;IN_REVIEW&lt;/code&gt; and &lt;code&gt;RETRY&lt;/code&gt; statuses are the ones most developers handle incorrectly. &lt;code&gt;IN_REVIEW&lt;/code&gt; is not a failure — it is a compliance hold that will resolve into &lt;code&gt;COMPLETED&lt;/code&gt; or &lt;code&gt;REJECTED&lt;/code&gt;. If you mark it as failed and notify the user, you will have unhappy users chasing payouts that are actually in progress. &lt;code&gt;RETRY&lt;/code&gt; means the network is handling it automatically. No action needed on your end.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make your handler idempotent
&lt;/h3&gt;

&lt;p&gt;Afriex retries webhook delivery up to 12 times with exponential backoff. Your handler will receive the same event more than once. That is by design. Your code needs to handle it gracefully.&lt;/p&gt;

&lt;p&gt;The simplest approach: store a record of processed webhook event IDs and skip any you have already handled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processWebhookEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WebhookPayload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Check if we have already processed this exact event&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alreadyProcessed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;processedWebhooks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;processedWebhooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eventId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;alreadyProcessed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Already handled — acknowledge and return&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Process the event&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;handleTransactionEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Mark as processed&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;processedWebhooks&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;eventId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;processedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the idempotency key you can use a combination of &lt;code&gt;event&lt;/code&gt; type, resource ID, and &lt;code&gt;updatedAt&lt;/code&gt; timestamp. This way, the same status update arriving twice is treated as a duplicate and skipped, but a genuine status change on the same transaction (e.g., &lt;code&gt;PROCESSING&lt;/code&gt; followed by &lt;code&gt;COMPLETED&lt;/code&gt;) is treated as two distinct events.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handle customer and payment method events
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleCustomerEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CustomerWebhookPayload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CUSTOMER.UPDATED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Keep your local record in sync&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateLocalCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CUSTOMER.DELETED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Mark the customer as removed in your DB&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;markCustomerDeleted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handlePaymentMethodEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PaymentMethodWebhookPayload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PAYMENT_METHOD.DELETED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Remove from your DB and flag the customer as needing a new payout method&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;removePaymentMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentMethodId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;flagCustomerNeedsPaymentMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Retry behavior
&lt;/h2&gt;

&lt;p&gt;Afriex retries failed webhook deliveries up to 12 times. The schedule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;30s → 1m → 2m → 4m → 8m → 16m → 32m → 1h → 2h → 4h → 8h → 16h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A delivery is considered failed if your endpoint returns a non-2xx status or does not respond within about 5 seconds. This means your handler timing out is treated the same as a hard error — Afriex will retry.&lt;/p&gt;

&lt;p&gt;Two practical implications. First, your handler must respond quickly (within 5 seconds) regardless of what processing needs to happen — hence the enqueue-and-return pattern above. Second, you should never rely solely on webhooks for reconciliation. Build a polling fallback: periodically call &lt;code&gt;GET /api/v1/transaction/:id&lt;/code&gt; for transactions that have been in a non-terminal status for longer than expected. Webhooks are the fast path. The API is the source of truth.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing locally
&lt;/h2&gt;

&lt;p&gt;Afriex provides a sandbox-only endpoint for firing real signed webhooks against your local handler without needing to manufacture underlying activity. You create an entity (a customer, payment method, or transaction) in sandbox, then call the trigger endpoint with the entity ID and the event name you want to test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; https://sandbox.api.afriex.com/api/v1/webhooks/trigger &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-api-key: your-sandbox-api-key'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
    "event": "TRANSACTION.UPDATED",
    "entityId": "69d60071ab82306f11b03393"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afriex will send a real signed webhook to your configured callback URL using that entity as the payload. Because it is a real signed payload, your signature verification code runs exactly as it would in production.&lt;/p&gt;

&lt;p&gt;To receive it locally, expose your dev server with &lt;a href="https://ngrok.com/" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ngrok http 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register the HTTPS URL ngrok gives you as your webhook URL in the Afriex sandbox dashboard, then fire the trigger. You can test every event type this way — &lt;code&gt;CUSTOMER.CREATED&lt;/code&gt;, &lt;code&gt;PAYMENT_METHOD.DELETED&lt;/code&gt;, &lt;code&gt;TRANSACTION.UPDATED&lt;/code&gt; with any status — against a real entity in sandbox.&lt;/p&gt;

&lt;p&gt;The trigger endpoint returns &lt;code&gt;403 Forbidden&lt;/code&gt; in production, so there is no risk of accidentally firing test webhooks against your live environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Checklist before going live
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Afriex IP addresses added to your server allowlist (&lt;code&gt;34.197.33.100&lt;/code&gt; for production)&lt;/li&gt;
&lt;li&gt;[ ] Webhook public key stored as an environment variable, not hardcoded&lt;/li&gt;
&lt;li&gt;[ ] Signature verification runs against raw body bytes, not parsed JSON&lt;/li&gt;
&lt;li&gt;[ ] Handler returns &lt;code&gt;2xx&lt;/code&gt; within 5 seconds&lt;/li&gt;
&lt;li&gt;[ ] Processing happens asynchronously after acknowledgement&lt;/li&gt;
&lt;li&gt;[ ] All 11 transaction statuses handled explicitly — including &lt;code&gt;IN_REVIEW&lt;/code&gt;, &lt;code&gt;RETRY&lt;/code&gt;, and &lt;code&gt;UNKNOWN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Handler is idempotent — safe to receive the same event multiple times&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;PAYMENT_METHOD.DELETED&lt;/code&gt; event triggers a flag in your database&lt;/li&gt;
&lt;li&gt;[ ] Polling fallback implemented for transactions stuck in non-terminal status&lt;/li&gt;
&lt;li&gt;[ ] Tested all event types using the sandbox trigger endpoint&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;The full webhook reference is at &lt;a href="https://docs.afriex.com/api-reference/endpoint/webhooks/introduction" rel="noopener noreferrer"&gt;docs.afriex.com/api-reference/endpoint/webhooks/introduction&lt;/a&gt;. The transaction API reference, including the full request and response schema, is at &lt;a href="https://docs.afriex.com/api-reference/endpoint/transactions/create" rel="noopener noreferrer"&gt;docs.afriex.com/api-reference/endpoint/transactions/create&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>afriex</category>
      <category>devex</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How Agentic AI Is Changing Cross-Border Payments (and What It Means for Developers)</title>
      <dc:creator>Victory Lucky</dc:creator>
      <pubDate>Sun, 24 May 2026 06:21:36 +0000</pubDate>
      <link>https://dev.to/afriex/how-agentic-ai-is-changing-cross-border-payments-and-what-it-means-for-developers-3m12</link>
      <guid>https://dev.to/afriex/how-agentic-ai-is-changing-cross-border-payments-and-what-it-means-for-developers-3m12</guid>
      <description>&lt;p&gt;For most of the last decade, AI in payments meant one thing: fraud detection. A model sitting downstream, flagging suspicious transactions after the fact. Useful, but passive. The system still required a human or deterministic code to decide what to do next.&lt;/p&gt;

&lt;p&gt;That is changing fast. &lt;a href="https://thepaypers.com/payments/expert-views/2025-payments-retrospective-the-reshaping-forces-of-agentic-ai-a2a-rtps-and-cbdcs" rel="noopener noreferrer"&gt;Agentic AI emerged as the breakout technology of 2025, moving from demos into regulated payment workflows&lt;/a&gt;. The difference is not the model. It is the architecture. Agentic systems do not just classify or predict. They plan, execute multi-step workflows, and take action across external systems without a human in the loop for every step. In payments, that shift has real consequences for how infrastructure gets built and what developers need to understand.&lt;/p&gt;




&lt;h2&gt;
  
  
  What agentic AI actually means in a payments context
&lt;/h2&gt;

&lt;p&gt;The term gets used loosely, so a working definition is worth establishing. An AI agent in a payment context is a system that receives a high-level goal, decides what sequence of API calls to make to achieve it, executes them, handles failures, and reports the outcome, with no human approving each individual step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.elibrary.imf.org/view/journals/068/2026/004/article-A001-en.xml" rel="noopener noreferrer"&gt;The IMF's May 2026 note on agentic AI in payments&lt;/a&gt; describes the scope of experimentation as expanding rapidly: from fraud detection and compliance monitoring to treasury optimization and cross-border payment orchestration. &lt;a href="https://www.fenwick.com/insights/publications/is-2026-the-year-of-agentic-payments" rel="noopener noreferrer"&gt;Fenwick's 2026 agentic payments analysis&lt;/a&gt; draws the line clearly: unlike traditional autopay automation, agentic AI makes decisions and takes actions to achieve goals. It is not executing a predefined script.&lt;/p&gt;

&lt;p&gt;The practical difference for a developer is this: instead of writing code that calls &lt;code&gt;get_balance&lt;/code&gt;, evaluates the result, and conditionally calls &lt;code&gt;create_transaction&lt;/code&gt;, you expose those capabilities as tools and let the model decide the sequence based on a stated goal. The orchestration logic moves from your codebase to the model. The code you write shrinks. The capability surface expands.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where agentic payment automation is already happening
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.fxcintel.com/research/analysis/ai-earnings-mentions-2025" rel="noopener noreferrer"&gt;A March 2026 analysis of earnings calls across 24 companies in the cross-border payments space&lt;/a&gt; found AI mentions surging significantly year over year. The themes were not hypothetical. NatWest said AI agents can "execute complex banking workflows" on behalf of customers. Remitly announced plans to deploy agentic technology across productivity, fraud reduction, and decision-making in 2026.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://techinformed.com/agentic-ai-and-more-to-reshape-fintech-in-2026/" rel="noopener noreferrer"&gt;Agentic AI is moving toward anticipating intent, verifying identity, detecting fraud, and authorizing transactions in real time across platforms&lt;/a&gt;, all in a single autonomous workflow rather than across separate systems. Compliance teams are using agentic AI to shift from static rule-based watchlist screening to continuous, trigger-based monitoring. &lt;a href="https://substack.com/@samboboev/note/c-202832396" rel="noopener noreferrer"&gt;Watchlist screening currently generates 90 to 95 percent false positives&lt;/a&gt;, and agentic systems with richer contextual reasoning are actively pushing that number down.&lt;/p&gt;

&lt;p&gt;For cross-border payment infrastructure specifically, the primary application is treasury optimization and payment orchestration. &lt;a href="https://substack.com/@samboboev/note/c-202832396" rel="noopener noreferrer"&gt;Cross-border flows hit $190 trillion annually&lt;/a&gt; and legacy systems still route most of that through multi-step correspondent banking chains. Agentic payment systems can evaluate multiple rail options in real time, select the optimal one for a given corridor and amount, monitor for settlement status, and escalate to a human only when a transaction falls outside expected parameters.&lt;/p&gt;




&lt;h2&gt;
  
  
  The architectural challenge this creates
&lt;/h2&gt;

&lt;p&gt;The IMF note identifies what it calls a central architectural challenge: as AI agents gain the ability to initiate and execute payment transactions autonomously, the traditional assumption that a human authorizes each individual transaction breaks down. The legal and liability frameworks built around that assumption do not map cleanly onto autonomous agent behavior.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.fenwick.com/insights/publications/is-2026-the-year-of-agentic-payments" rel="noopener noreferrer"&gt;Existing financial and consumer protection laws built around human-decisioned transactions may not appropriately address the challenges raised by agentic payments&lt;/a&gt;. Companies building agentic payment automation need to navigate unsettled questions under AI laws, money transmitter regimes, and authorization frameworks simultaneously.&lt;/p&gt;

&lt;p&gt;For developers, this creates two concrete requirements. First, every action an agent takes on behalf of a user must be logged with enough detail to reconstruct exactly what the agent decided, what information it had at the time, and what it executed. Immutable audit logs are not a nice-to-have in agentic payment systems. They are the primary mechanism for accountability. Second, agent scope must be explicitly bounded. An agent authorized to send payroll disbursements should not be able to initiate arbitrary transactions outside that context. Tool-level permission scoping, not just API key permissions, is the right model here.&lt;/p&gt;




&lt;h2&gt;
  
  
  What agentic orchestration requires from payment APIs
&lt;/h2&gt;

&lt;p&gt;The shift toward autonomous payment systems changes the requirements for the payment APIs agents build on. These requirements are stricter than what human-driven integrations demand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool descriptions carry the same weight as endpoint documentation.&lt;/strong&gt; When a human developer integrates an API, they read the docs and write code. When an AI agent integrates a payment API, it reads the tool descriptions and decides what to call. An ambiguous tool description produces wrong agent behavior the same way an ambiguous endpoint contract produces bugs. Payment infrastructure providers who want their APIs used in agentic payment workflows need to treat tool descriptions as a first-class product concern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error responses need to be machine-interpretable, not just human-readable.&lt;/strong&gt; An agent that receives "Something went wrong" cannot decide whether to retry, escalate, or abort. Structured error codes like &lt;code&gt;INSUFFICIENT_BALANCE&lt;/code&gt;, &lt;code&gt;FX_RATE_EXPIRED&lt;/code&gt;, and &lt;code&gt;PAYMENT_METHOD_INVALID&lt;/code&gt; give the model the information it needs to make the right decision without human intervention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transaction status granularity matters more than it did before.&lt;/strong&gt; In a human-driven integration, a developer can decide how to handle an ambiguous status. In an agentic payment workflow, the agent needs enough signal to act correctly on its own. A payment API that returns &lt;code&gt;PENDING&lt;/code&gt; for multiple distinct states forces the agent to guess. A well-designed one surfaces the full status vocabulary the underlying network produces: &lt;code&gt;PENDING&lt;/code&gt;, &lt;code&gt;PROCESSING&lt;/code&gt;, &lt;code&gt;IN_REVIEW&lt;/code&gt;, &lt;code&gt;COMPLETED&lt;/code&gt;, &lt;code&gt;FAILED&lt;/code&gt;, &lt;code&gt;REJECTED&lt;/code&gt;, &lt;code&gt;RETRY&lt;/code&gt;, &lt;code&gt;REFUNDED&lt;/code&gt;. Each status maps to a different agent decision. &lt;code&gt;IN_REVIEW&lt;/code&gt; means wait and poll. &lt;code&gt;RETRY&lt;/code&gt; means the network is handling it. &lt;code&gt;REJECTED&lt;/code&gt; means surface to a human. The Afriex Business API exposes exactly this vocabulary on every transaction, which is one reason it is well-suited as infrastructure for agentic payment automation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Idempotency is non-negotiable.&lt;/strong&gt; Agents retry. Networks fail. &lt;a href="https://medium.com/@jyc.dev/idempotency-in-software-engineering-why-it-matters-and-how-to-implement-it-2025-guide-c1ef8ad21965" rel="noopener noreferrer"&gt;Without idempotency keys, payment APIs risk duplicate transactions that are costly and difficult to reverse&lt;/a&gt;. An autonomous payment system without idempotent operations is a double-payment incident waiting to happen. The Afriex SDK accepts an idempotency key on every transaction creation call, which means retrying a failed disbursement job is safe by design.&lt;/p&gt;




&lt;h2&gt;
  
  
  The African corridor context
&lt;/h2&gt;

&lt;p&gt;For developers building agentic payment automation for African markets, the infrastructure complexity is higher than most global payment APIs assume, and the choice of payment API matters more as a result.&lt;/p&gt;

&lt;p&gt;Mobile money is the dominant payment rail in East and West Africa, not cards and not bank transfers. FX volatility in corridors like NGN/USD means a rate that is valid at the moment an agent job is created may be meaningfully different at the moment of settlement. Transaction statuses like &lt;code&gt;IN_REVIEW&lt;/code&gt; reflect real compliance holds that African payment networks produce, not just generic processing delays. An agent operating in this environment needs access to tools that reflect those realities rather than abstracting them away.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://business.afriex.com/" rel="noopener noreferrer"&gt;Afriex Business API&lt;/a&gt; is built against this infrastructure directly. Mobile money, bank transfers, SWIFT, and local payment channels are all first-class integrations. Exchange rates are live across NGN, KES, GHS, GBP, and other African corridor pairs. The &lt;a href="https://docs.afriex.com/mcp/introduction" rel="noopener noreferrer"&gt;Afriex MCP server&lt;/a&gt; exposes the full API surface as 22 callable tools for agentic workflows: &lt;code&gt;get_rates&lt;/code&gt; for live rates before committing to a disbursement, &lt;code&gt;get_balance&lt;/code&gt; to verify funds before a payroll run starts, &lt;code&gt;resolve_payment_method&lt;/code&gt; to verify a recipient account before attaching it, &lt;code&gt;create_transaction&lt;/code&gt; with idempotency key support, and &lt;code&gt;get_transaction&lt;/code&gt; for status polling after execution. The full transaction status vocabulary, including &lt;code&gt;IN_REVIEW&lt;/code&gt;, &lt;code&gt;RETRY&lt;/code&gt;, and &lt;code&gt;REJECTED&lt;/code&gt;, is surfaced through webhooks so an agent always has enough signal to decide its next action correctly.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to build now versus what to watch
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Build now: agentic payroll disbursement.&lt;/strong&gt; The use case is clear, the failure modes are well-understood, and the liability surface is bounded because a human approves the payroll run before the agent executes it. The agent's autonomy is scoped to execution, not authorization. This is the lowest-risk entry point for agentic payment automation and has the most direct ROI. The architecture for this, including how it integrates with the Afriex Business API, is covered in the &lt;a href="https://dev.to/afriex"&gt;companion architecture document&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build now: agentic FX monitoring and settlement timing.&lt;/strong&gt; An agent that watches a currency corridor, evaluates whether the current rate is within a threshold, and triggers settlement when conditions are met is straightforward to implement using the Afriex MCP server's &lt;code&gt;get_rates&lt;/code&gt; tool on a schedule. It is immediately valuable for any business running frequent cross-border payment flows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch: fully autonomous payment authorization.&lt;/strong&gt; Agents that can initiate arbitrary payments based on their own assessment of conditions, without a human approving each batch, sit in genuinely unsettled legal territory. &lt;a href="https://www.fenwick.com/insights/publications/is-2026-the-year-of-agentic-payments" rel="noopener noreferrer"&gt;The legal framework for autonomous payment authorization is unresolved&lt;/a&gt;, and building ahead of regulatory clarity is a risk most developers should not take on without specific legal guidance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch: multi-agent payment orchestration.&lt;/strong&gt; Chains of agents handing off payment decisions to each other across organizational boundaries are one of the most technically and legally complex areas in agentic AI right now. &lt;a href="https://www.elibrary.imf.org/view/journals/068/2026/004/article-A001-en.xml" rel="noopener noreferrer"&gt;The IMF note&lt;/a&gt; identifies this as a central challenge. The infrastructure protocols for secure multi-agent communication across payment networks are still being standardized.&lt;/p&gt;




&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;Agentic AI does not change what cross-border payment infrastructure needs to do. It changes who is doing the orchestrating. The requirements for the underlying payment API layer get stricter as a result: machine-interpretable error codes, granular transaction status vocabularies, idempotent operations, and tool descriptions precise enough for a model to act on correctly without human clarification.&lt;/p&gt;

&lt;p&gt;For developers, the opportunity in agentic payment automation is real and immediate in bounded use cases. The risk is real too, and proportional to how much autonomous authorization you give the agent. Start with execution autonomy, keep authorization human, and build the audit trail as if regulators will read it. Because eventually, they will.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>fintech</category>
      <category>afriex</category>
      <category>agents</category>
    </item>
    <item>
      <title>Getting Started with Afriex MCP in Cursor</title>
      <dc:creator>0xSonOfUri</dc:creator>
      <pubDate>Sat, 16 May 2026 15:17:52 +0000</pubDate>
      <link>https://dev.to/afriex/getting-started-with-afriex-mcp-in-cursor-48f6</link>
      <guid>https://dev.to/afriex/getting-started-with-afriex-mcp-in-cursor-48f6</guid>
      <description>&lt;p&gt;AI coding tools are evolving quickly.&lt;/p&gt;

&lt;p&gt;But the real shift starts when your editor can interact with infrastructure directly.&lt;/p&gt;

&lt;p&gt;Instead of only generating code, AI tools can now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;query APIs&lt;/li&gt;
&lt;li&gt;interact with services&lt;/li&gt;
&lt;li&gt;retrieve live data&lt;/li&gt;
&lt;li&gt;execute workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s where MCP comes in.&lt;/p&gt;

&lt;p&gt;In this guide, you’ll learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;set up Afriex MCP in Cursor&lt;/li&gt;
&lt;li&gt;connect your Afriex account&lt;/li&gt;
&lt;li&gt;verify your setup&lt;/li&gt;
&lt;li&gt;understand how MCP changes the developer workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not a product demo.&lt;/p&gt;

&lt;p&gt;This is a practical setup guide for developers getting started with Afriex MCP.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is MCP?
&lt;/h2&gt;

&lt;p&gt;MCP (Model Context Protocol) is a standardized way for AI tools to interact with external systems and services.&lt;/p&gt;

&lt;p&gt;Instead of treating the AI editor as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“just a code generator”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;MCP allows it to become:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“an infrastructure-aware development environment”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With MCP, AI tools can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;access APIs&lt;/li&gt;
&lt;li&gt;call external tools&lt;/li&gt;
&lt;li&gt;retrieve live data&lt;/li&gt;
&lt;li&gt;orchestrate workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;directly from inside the editor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Normally, integrating financial infrastructure involves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reading documentation&lt;/li&gt;
&lt;li&gt;wiring SDKs manually&lt;/li&gt;
&lt;li&gt;testing requests&lt;/li&gt;
&lt;li&gt;switching between dashboards and terminals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With MCP:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the editor gains context&lt;/li&gt;
&lt;li&gt;workflows become faster&lt;/li&gt;
&lt;li&gt;AI can assist with infrastructure interactions directly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates a very different developer experience.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Afriex MCP?
&lt;/h2&gt;

&lt;p&gt;Afriex MCP exposes Afriex infrastructure capabilities to AI-enabled developer tools like Cursor.&lt;/p&gt;

&lt;p&gt;This allows Cursor to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;retrieve balances&lt;/li&gt;
&lt;li&gt;understand payment workflows&lt;/li&gt;
&lt;li&gt;scaffold integrations&lt;/li&gt;
&lt;li&gt;interact with Afriex APIs through MCP tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;directly from natural language prompts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before starting, you’ll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An Afriex Business account&lt;/li&gt;
&lt;li&gt;Cursor installed&lt;/li&gt;
&lt;li&gt;An Afriex API key&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Useful links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://business.afriex.com/" rel="noopener noreferrer"&gt;https://business.afriex.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.afriex.com/" rel="noopener noreferrer"&gt;https://docs.afriex.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cursor.com/" rel="noopener noreferrer"&gt;https://cursor.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1 — Log Into Afriex Business
&lt;/h2&gt;

&lt;p&gt;Head to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://business.afriex.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Log into your Afriex Business account.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — Open the Documentation
&lt;/h2&gt;

&lt;p&gt;Inside the dashboard:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to:

&lt;ul&gt;
&lt;li&gt;Developer&lt;/li&gt;
&lt;li&gt;API Documentation&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Afriex provides detailed developer documentation including MCP setup instructions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 — Open the MCP Guide
&lt;/h2&gt;

&lt;p&gt;Inside the docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open:

&lt;ul&gt;
&lt;li&gt;MCP Server&lt;/li&gt;
&lt;li&gt;Connecting MCP Clients&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This section contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;supported MCP clients&lt;/li&gt;
&lt;li&gt;setup instructions&lt;/li&gt;
&lt;li&gt;recommended configurations&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 4 — Select Cursor
&lt;/h2&gt;

&lt;p&gt;Afriex supports multiple MCP-capable tools and IDEs.&lt;/p&gt;

&lt;p&gt;In this guide, we’ll use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cursor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Copy the recommended Cursor MCP configuration from the docs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5 — Configure Cursor
&lt;/h2&gt;

&lt;p&gt;Inside Cursor:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Settings&lt;/li&gt;
&lt;li&gt;Navigate to:

&lt;ul&gt;
&lt;li&gt;Tools &amp;amp; MCPs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click:

&lt;ul&gt;
&lt;li&gt;Add Custom MCP&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Paste the configuration you copied from the Afriex documentation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6 — Update Your Credentials
&lt;/h2&gt;

&lt;p&gt;Update the configuration with your credentials:&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="nl"&gt;"x-afriex-api-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;"YOUR_API_KEY"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"x-afriex-environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use:&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="nl"&gt;"x-afriex-environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sandbox"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for testing environments if supported in your workflow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7 — Verify the Connection
&lt;/h2&gt;

&lt;p&gt;Now that Cursor is connected to Afriex MCP, try a simple prompt inside Cursor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use Afriex MCP to fetch my live balances
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything is configured correctly, Cursor should retrieve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NGN balances&lt;/li&gt;
&lt;li&gt;USD balances&lt;/li&gt;
&lt;li&gt;supported wallet balances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;directly from Afriex.&lt;/p&gt;

&lt;p&gt;At this point, your editor is no longer operating purely as a code assistant.&lt;/p&gt;

&lt;p&gt;It can now interact with infrastructure through MCP.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Makes This Interesting
&lt;/h2&gt;

&lt;p&gt;The important shift here is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“AI generates code.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The interesting part is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“AI can now interact with systems.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That changes the workflow significantly.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;constantly context switching&lt;/li&gt;
&lt;li&gt;manually wiring everything first&lt;/li&gt;
&lt;li&gt;navigating infrastructure alone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;developers can now work with AI tools that understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APIs&lt;/li&gt;
&lt;li&gt;infrastructure&lt;/li&gt;
&lt;li&gt;workflows&lt;/li&gt;
&lt;li&gt;integrations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;more directly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example MCP Workflow
&lt;/h2&gt;

&lt;p&gt;Once connected, you can start using prompts like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fetch my Afriex balances
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Help me scaffold an Afriex integration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generate a Next.js API route for Afriex virtual accounts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a webhook handler for Afriex payment events
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI editor now has context about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the infrastructure&lt;/li&gt;
&lt;li&gt;the available tools&lt;/li&gt;
&lt;li&gt;the integration patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;which makes the development experience much smoother.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security Notes
&lt;/h2&gt;

&lt;p&gt;When using MCP integrations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;never expose your API keys publicly&lt;/li&gt;
&lt;li&gt;avoid committing credentials to Git repositories&lt;/li&gt;
&lt;li&gt;use environment variables where possible&lt;/li&gt;
&lt;li&gt;rotate keys periodically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Treat your MCP configuration with the same care as production infrastructure credentials.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where This Goes Next
&lt;/h2&gt;

&lt;p&gt;Afriex MCP opens up workflows around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;payments&lt;/li&gt;
&lt;li&gt;virtual accounts&lt;/li&gt;
&lt;li&gt;payouts&lt;/li&gt;
&lt;li&gt;reconciliation&lt;/li&gt;
&lt;li&gt;infrastructure automation&lt;/li&gt;
&lt;li&gt;fintech developer tooling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And this is still early.&lt;/p&gt;

&lt;p&gt;As MCP ecosystems evolve, developer tools will become increasingly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;infrastructure-aware&lt;/li&gt;
&lt;li&gt;execution-aware&lt;/li&gt;
&lt;li&gt;workflow-aware&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Setting up Afriex MCP inside Cursor only takes a few minutes.&lt;/p&gt;

&lt;p&gt;But the implications are much bigger.&lt;/p&gt;

&lt;p&gt;We’re moving toward a world where AI editors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;understand infrastructure&lt;/li&gt;
&lt;li&gt;interact with APIs&lt;/li&gt;
&lt;li&gt;assist with integrations&lt;/li&gt;
&lt;li&gt;help orchestrate real systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;not just generate snippets.&lt;/p&gt;

&lt;p&gt;And honestly, that changes how software gets built.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Afriex Business
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://business.afriex.com/" rel="noopener noreferrer"&gt;https://business.afriex.com/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Afriex Documentation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.afriex.com/" rel="noopener noreferrer"&gt;https://docs.afriex.com/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cursor
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cursor.com/" rel="noopener noreferrer"&gt;https://cursor.com/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>afriex</category>
      <category>mcp</category>
      <category>fintech</category>
    </item>
    <item>
      <title>Build Cross-Border Payment AI Agents with the Afriex MCP Server</title>
      <dc:creator>Victory Lucky</dc:creator>
      <pubDate>Sat, 02 May 2026 04:20:50 +0000</pubDate>
      <link>https://dev.to/afriex/build-cross-border-payment-ai-agents-with-the-afriex-mcp-server-2l5j</link>
      <guid>https://dev.to/afriex/build-cross-border-payment-ai-agents-with-the-afriex-mcp-server-2l5j</guid>
      <description>&lt;p&gt;Payment integrations are straightforward until they are not. A single payout involves checking a balance, resolving a bank code, verifying an account number, looking up a live exchange rate, and then executing the transaction — each step a separate API call, each one requiring code you write and maintain.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.afriex.com/mcp/introduction" rel="noopener noreferrer"&gt;Afriex MCP server&lt;/a&gt; lets an AI agent handle that entire sequence through natural language. It exposes the complete &lt;a href="https://docs.afriex.com/" rel="noopener noreferrer"&gt;Afriex Business API&lt;/a&gt; as 20+ callable tools — customers, transactions, payment methods, exchange rates, and balances — that any MCP-compatible client can discover and use without SDK wiring.&lt;/p&gt;

&lt;p&gt;Connect it to Claude Desktop, Claude Code, or Cursor and your AI assistant can register customers, attach bank accounts and mobile money wallets, fetch live NGN/KES/GHS rates, and trigger cross-border payouts — all from a single prompt.&lt;/p&gt;

&lt;p&gt;If you are not yet familiar with Model Context Protocol and how it works, &lt;a href="https://dev.to/codewithveek/model-context-protocol-the-standard-that-lets-ai-agents-actually-do-things-4coj"&gt;this article covers the protocol, architecture, and security model&lt;/a&gt; before you continue here.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 20+ tools
&lt;/h2&gt;

&lt;p&gt;The server endpoint is &lt;code&gt;https://mcp.afriex.com/mcp&lt;/code&gt;. Full reference at &lt;a href="https://docs.afriex.com/mcp/introduction" rel="noopener noreferrer"&gt;docs.afriex.com/mcp/introduction&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;authenticate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sets your API key and environment for the session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;session_info&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns current session config — masked API key, environment, base URL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Customers
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;create_customer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates a customer with name, email, phone, and country code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_customers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns a paginated list of customers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_customer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fetches a customer by ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;delete_customer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deletes a customer by ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;update_customer_kyc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Updates KYC information for a customer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Transactions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;create_transaction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates a &lt;code&gt;DEPOSIT&lt;/code&gt;, &lt;code&gt;WITHDRAW&lt;/code&gt;, or &lt;code&gt;SWAP&lt;/code&gt; transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_transactions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns a paginated list of transactions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_transaction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fetches a transaction by ID&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Payment methods
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;create_payment_method&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates a payment method: &lt;code&gt;BANK_ACCOUNT&lt;/code&gt;, &lt;code&gt;SWIFT&lt;/code&gt;, &lt;code&gt;MOBILE_MONEY&lt;/code&gt;, &lt;code&gt;UPI&lt;/code&gt;, &lt;code&gt;INTERAC&lt;/code&gt;, &lt;code&gt;WE_CHAT&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_payment_methods&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns a paginated list of payment methods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_payment_method&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fetches a payment method by ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;delete_payment_method&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deletes a payment method by ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_institutions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lists banks and mobile money providers by country and channel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;resolve_institution_codes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resolves a SWIFT code or routing number to a bank name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;resolve_payment_method&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resolves recipient info by account number or phone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_crypto_wallet&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gets or creates a USDT/USDC crypto wallet (production only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_virtual_account&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gets or creates a virtual account (production only)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Organization
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_balance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fetches wallet balances for specified currencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_rates&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gets live exchange rates across supported corridors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;topup_balance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tops up sandbox balance (development only)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Connecting your client
&lt;/h2&gt;

&lt;p&gt;You will need an Afriex Business API key from the &lt;a href="https://business.afriex.com/" rel="noopener noreferrer"&gt;dashboard&lt;/a&gt;. Pass it via HTTP headers — this keeps credentials out of the conversation context and authenticates every tool call automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claude Desktop
&lt;/h3&gt;

&lt;p&gt;Add to &lt;code&gt;~/Library/Application Support/Claude/claude_desktop_config.json&lt;/code&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;"afriex"&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;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp.afriex.com/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&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;"x-afriex-api-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;"your-api-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"x-afriex-environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"development"&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;p&gt;Restart Claude Desktop. The Afriex tools will appear in the tools panel.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claude Code
&lt;/h3&gt;

&lt;p&gt;Add to &lt;code&gt;.claude/settings.json&lt;/code&gt; in your project:&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;"afriex"&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;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp.afriex.com/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&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;"x-afriex-api-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;"your-api-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"x-afriex-environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"development"&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;p&gt;Or via the CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add afriex &lt;span class="nt"&gt;--transport&lt;/span&gt; http https://mcp.afriex.com/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cursor
&lt;/h3&gt;

&lt;p&gt;Add to &lt;code&gt;.cursor/mcp.json&lt;/code&gt; in your project root:&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;"afriex"&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;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp.afriex.com/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&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;"x-afriex-api-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;"your-api-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"x-afriex-environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"development"&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;p&gt;Or go to &lt;strong&gt;Cursor Settings → MCP → Add new MCP Server&lt;/strong&gt;, set type to &lt;strong&gt;URL&lt;/strong&gt;, and enter &lt;code&gt;https://mcp.afriex.com/mcp&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Without HTTP headers
&lt;/h3&gt;

&lt;p&gt;If your client does not support custom headers, connect to the server URL without them and use the &lt;code&gt;authenticate&lt;/code&gt; tool as your first prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Authenticate with API key &lt;code&gt;your-api-key&lt;/code&gt; in the production environment."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The server stores your credentials for the session and uses them for all subsequent tool calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can build
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cross-border payout agent
&lt;/h3&gt;

&lt;p&gt;A complete payout as an MCP tool chain:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;list_institutions&lt;/code&gt; — resolve the correct bank or mobile money code for the recipient's country&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;create_customer&lt;/code&gt; — register the recipient&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;update_customer_kyc&lt;/code&gt; — complete KYC&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;resolve_payment_method&lt;/code&gt; — verify the account number before attaching it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;create_payment_method&lt;/code&gt; — attach the verified bank account or mobile wallet&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_rates&lt;/code&gt; — preview the live exchange rate&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_balance&lt;/code&gt; — confirm sufficient funds before committing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;create_transaction&lt;/code&gt; with &lt;code&gt;type: "WITHDRAW"&lt;/code&gt; — execute the payout&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_transaction&lt;/code&gt; — poll settlement status&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The agent chains these in sequence and branches on results — it will not call &lt;code&gt;create_transaction&lt;/code&gt; if &lt;code&gt;get_balance&lt;/code&gt; returns insufficient funds. You state the intent, the model decides the sequence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Payroll disbursement agent
&lt;/h3&gt;

&lt;p&gt;An agent that reads a payroll file and disburses to each employee across multiple African corridors:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;get_rates&lt;/code&gt; per currency corridor to confirm converted amounts upfront&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_balance&lt;/code&gt; to verify total funds before any disbursement starts&lt;/li&gt;
&lt;li&gt;For each employee: &lt;code&gt;list_customers&lt;/code&gt; to check if they exist, &lt;code&gt;create_customer&lt;/code&gt; if not, &lt;code&gt;create_payment_method&lt;/code&gt; if no payout method is on file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;create_transaction&lt;/code&gt; per employee&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_transaction&lt;/code&gt; to confirm each settlement&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If a customer already exists, the agent skips creation. If funds run short mid-run, it stops and reports before making the next call rather than failing silently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Operational queries from your IDE
&lt;/h3&gt;

&lt;p&gt;With the server connected in Claude Code or Cursor, you can query your Afriex account without leaving your editor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What is the current NGN to USD rate?" → &lt;code&gt;get_rates&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;"List my last 10 transactions and flag any that failed" → &lt;code&gt;list_transactions&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;"Check my USD and GBP balances" → &lt;code&gt;get_balance&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;"What is GTBank Nigeria's institution code?" → &lt;code&gt;list_institutions&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;"Top up my sandbox NGN balance by 500,000" → &lt;code&gt;topup_balance&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  FX rate monitoring agent
&lt;/h3&gt;

&lt;p&gt;An agent that watches a currency pair and acts when a target rate is hit:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;get_rates&lt;/code&gt; on a schedule for a specific corridor&lt;/li&gt;
&lt;li&gt;Compare against a configured threshold&lt;/li&gt;
&lt;li&gt;When the rate crosses, trigger &lt;code&gt;create_transaction&lt;/code&gt; or surface an alert&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Useful for businesses that time cross-border settlements against rate movements rather than executing at arbitrary times.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sandbox and environment
&lt;/h2&gt;

&lt;p&gt;Set &lt;code&gt;x-afriex-environment&lt;/code&gt; to &lt;code&gt;development&lt;/code&gt; for sandbox testing. The &lt;code&gt;topup_balance&lt;/code&gt; tool funds your sandbox wallet with any currency and amount, so you can test the full tool chain, customer creation through transaction settlement, without touching live funds.&lt;/p&gt;

&lt;p&gt;Switch to &lt;code&gt;production&lt;/code&gt; in the header when ready to go live.&lt;/p&gt;




&lt;h2&gt;
  
  
  MCP versus the Afriex SDK
&lt;/h2&gt;

&lt;p&gt;They are not alternatives — they serve different layers.&lt;/p&gt;

&lt;p&gt;Use the &lt;strong&gt;SDK&lt;/strong&gt; for production applications: deterministic code paths, typed inputs and outputs, full error handling control, and integration with your own data layer. The &lt;a href="https://dev.to/afriex/build-a-freelancer-payout-platform-with-the-afriex-sdk-and-nextjs"&gt;freelancer payout platform tutorial&lt;/a&gt; is a complete walkthrough of that approach.&lt;/p&gt;

&lt;p&gt;Use the &lt;strong&gt;MCP server&lt;/strong&gt; when an AI model is doing the orchestrating: agentic workflows, internal natural language tools, IDE queries during development, and rapid prototyping before you formalize an integration.&lt;/p&gt;

&lt;p&gt;Most production setups use both, the SDK for core application logic, the MCP server for the agentic layer on top.&lt;/p&gt;

&lt;p&gt;Full tool reference and setup docs at &lt;a href="https://docs.afriex.com/mcp/introduction" rel="noopener noreferrer"&gt;docs.afriex.com/mcp&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>mcp</category>
      <category>afriex</category>
    </item>
    <item>
      <title>Accept USDC / USDT Without Running Your Own Wallet Infrastructure (Afriex API)</title>
      <dc:creator>0xSonOfUri</dc:creator>
      <pubDate>Mon, 20 Apr 2026 12:08:15 +0000</pubDate>
      <link>https://dev.to/afriex/accept-usdc-usdt-without-running-your-own-wallet-infrastructure-afriex-api-3ig</link>
      <guid>https://dev.to/afriex/accept-usdc-usdt-without-running-your-own-wallet-infrastructure-afriex-api-3ig</guid>
      <description>&lt;p&gt;Stablecoins are becoming core infrastructure for global payments.&lt;/p&gt;

&lt;p&gt;But building with them usually means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;managing private keys&lt;/li&gt;
&lt;li&gt;running indexers&lt;/li&gt;
&lt;li&gt;tracking on-chain activity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s a lot of overhead.&lt;/p&gt;

&lt;p&gt;In this guide, you’ll learn how to &lt;strong&gt;accept USDC / USDT without running your own custody infrastructure&lt;/strong&gt;, using the Afriex API.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You’ll Build
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A backend endpoint that returns &lt;strong&gt;deposit instructions&lt;/strong&gt; (address + network)&lt;/li&gt;
&lt;li&gt;A webhook system to &lt;strong&gt;confirm payments reliably&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A production-ready flow using &lt;strong&gt;idempotency and retries-safe logic&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Mental Model
&lt;/h2&gt;

&lt;p&gt;Stop thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I need to manage wallets”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Start thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I’m building on payment rails”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With Afriex:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You &lt;strong&gt;don’t generate private keys&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You &lt;strong&gt;don’t run blockchain infrastructure&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You rely on Afriex for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deposit attribution&lt;/li&gt;
&lt;li&gt;transaction state&lt;/li&gt;
&lt;li&gt;settlement&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Your job is to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create a &lt;strong&gt;payment / invoice object&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;show deposit instructions&lt;/li&gt;
&lt;li&gt;confirm payments via &lt;strong&gt;webhooks&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 0 — Setup API Access
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Base URLs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Sandbox: &lt;code&gt;https://sandbox.api.afriex.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Production: &lt;code&gt;https://api.afriex.com&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Environment Variables
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AFRIEX_API_BASE_URL="https://api.afriex.com"
AFRIEX_API_KEY="your-api-key"
AFRIEX_WEBHOOK_PUBLIC_KEY="your-public-key"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Example Request
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-G&lt;/span&gt; &lt;span class="s2"&gt;"https://api.afriex.com/api/v1/org/balance"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-api-key: YOUR_AFRIEX_API_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 1 — Create Your Payment Object
&lt;/h2&gt;

&lt;p&gt;Afriex does not create payment links for you.&lt;/p&gt;

&lt;p&gt;You define your own &lt;strong&gt;invoice / payment intent&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;internal_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unique_reference&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USDC&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;expiresAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tip: Make &lt;code&gt;reference&lt;/code&gt; globally unique and reuse it across retries.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — Get Deposit Address
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Endpoint
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/v1/payment-method/crypto-wallet
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-G&lt;/span&gt; &lt;span class="s2"&gt;"https://api.afriex.com/api/v1/payment-method/crypto-wallet"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-api-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"asset=USDC"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Response
&lt;/h3&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;"data"&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="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ETHEREUM_MAINNET"&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="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TY..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TRON_MAINNET"&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;h3&gt;
  
  
  Backend Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;preferred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ETHEREUM_MAINNET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt;
  &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;preferred&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;preferred&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USDC&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Important Note
&lt;/h2&gt;

&lt;p&gt;This endpoint is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Production only&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It does not work in sandbox.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 — Display Payment Instructions
&lt;/h2&gt;

&lt;p&gt;Your UI should show:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Asset (USDC / USDT)&lt;/li&gt;
&lt;li&gt;Network&lt;/li&gt;
&lt;li&gt;Address (copy + QR)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also include:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sending on the wrong network may result in lost funds&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 4 — Confirm Payments (Webhooks)
&lt;/h2&gt;

&lt;p&gt;This is the most important part.&lt;/p&gt;

&lt;p&gt;Do NOT:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rely on user confirmation&lt;/li&gt;
&lt;li&gt;rely on block explorers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use &lt;strong&gt;Afriex webhooks&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Webhook Setup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Configure your webhook URL in the dashboard&lt;/li&gt;
&lt;li&gt;Copy your webhook public key&lt;/li&gt;
&lt;li&gt;Allowlist Afriex IPs&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Signature Verification
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;verifySignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;verifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createVerify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RSA-SHA256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;verifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;verifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Important: verify the &lt;strong&gt;raw request body&lt;/strong&gt;, not parsed JSON.&lt;/p&gt;




&lt;h3&gt;
  
  
  Events
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TRANSACTION.CREATED&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TRANSACTION.UPDATED&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Logic
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TRANSACTION.UPDATED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SUCCESS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;markInvoiceAsPaid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure this operation is idempotent.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5 — Idempotency
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Outbound
&lt;/h3&gt;

&lt;p&gt;Use:&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;"meta"&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;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"invoice_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"idempotencyKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"idem_invoice_123"&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;h3&gt;
  
  
  Inbound
&lt;/h3&gt;

&lt;p&gt;Webhooks may retry.&lt;/p&gt;

&lt;p&gt;Ensure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no duplicate credits&lt;/li&gt;
&lt;li&gt;safe re-processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use database constraints or “update-if-not-paid” logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6 — Reconciliation
&lt;/h2&gt;

&lt;p&gt;Use Afriex APIs for support and debugging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get transaction&lt;/li&gt;
&lt;li&gt;List transactions&lt;/li&gt;
&lt;li&gt;Get balance&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚠️ Common Pitfalls
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Crypto Wallet Not Working in Dev
&lt;/h3&gt;

&lt;p&gt;→ It’s production only&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Wrong Network Deposits
&lt;/h3&gt;

&lt;p&gt;→ Only present one supported network&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Webhook Verification Fails
&lt;/h3&gt;

&lt;p&gt;→ Use raw body + correct public key&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Duplicate Credits
&lt;/h3&gt;

&lt;p&gt;→ Fix with idempotency&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Stablecoins are becoming:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Core rails for global payments&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The real opportunity isn’t just using them.&lt;/p&gt;

&lt;p&gt;It’s building systems on top of them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;With Afriex, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accept USDC / USDT&lt;/li&gt;
&lt;li&gt;Avoid custody complexity&lt;/li&gt;
&lt;li&gt;Use webhooks for reliable confirmation&lt;/li&gt;
&lt;li&gt;Build production-ready payment systems&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;Afriex gives you the rails.&lt;/p&gt;

&lt;p&gt;You build the product.&lt;/p&gt;




</description>
      <category>fintech</category>
      <category>stablecoins</category>
      <category>blockchain</category>
      <category>web3</category>
    </item>
    <item>
      <title>What the African Fintech Infrastructure Stack Looks Like in 2026</title>
      <dc:creator>Victory Lucky</dc:creator>
      <pubDate>Fri, 17 Apr 2026 10:02:42 +0000</pubDate>
      <link>https://dev.to/afriex/what-the-african-fintech-infrastructure-stack-looks-like-in-2026-2cjl</link>
      <guid>https://dev.to/afriex/what-the-african-fintech-infrastructure-stack-looks-like-in-2026-2cjl</guid>
      <description>&lt;p&gt;A decade ago, the African fintech story was about access. Could you give someone a mobile wallet? Get them into the formal system? Those were the right questions. &lt;a href="https://www.bcg.com/publications/2026/beyond-payments-unlocking-africas-second-fintech-wave" rel="noopener noreferrer"&gt;Africa now accounts for roughly 74 percent of global mobile transaction volumes&lt;/a&gt;, processing over $1.1 trillion in 2024. The access problem is largely solved.&lt;/p&gt;

&lt;p&gt;The question in 2026 is about plumbing. What does the underlying infrastructure actually look like, where are the layers, and where are the gaps? This matters because the infrastructure you build against determines what is actually possible.&lt;/p&gt;

&lt;p&gt;Here is a ground-level look at each layer of the stack.&lt;/p&gt;




&lt;h2&gt;
  
  
  The payment rails layer
&lt;/h2&gt;

&lt;p&gt;Three types of payment rails coexist in Africa, and understanding how they relate is the first thing any developer building in this space needs to internalize.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mobile money networks&lt;/strong&gt; are the dominant rail. &lt;a href="https://sdk.finance/blog/fintech-kenya-2025-landscape-overview-growth-drivers-and-barriers/" rel="noopener noreferrer"&gt;M-Pesa processes over 61 million transactions daily and serves more than 50 million active users in Kenya alone&lt;/a&gt;. MTN Mobile Money, Airtel Money, and Orange Money cover large parts of West and Central Africa. In many markets, these are not supplements to banking — they are the primary financial infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Domestic instant payment systems&lt;/strong&gt; handle bank-to-bank flows within countries. Nigeria's NIBSS Instant Payments (NIP) is the largest, &lt;a href="https://businessday.ng/opinion/article/zone-nibss-collaboration-show-what-africas-payment-space-desperately-needs/" rel="noopener noreferrer"&gt;processing more real-time transactions than many systems in more developed economies&lt;/a&gt;. Kenya has Pesalink, South Africa has PayShap. Each handles intra-country clearing well. None connects natively to the others.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PAPSS — the Pan-African Payment and Settlement System&lt;/strong&gt; — is the cross-border layer. Launched in January 2022 by the African Union and Afreximbank, &lt;a href="https://african.business/2025/11/african-banker/africas-payment-revolution-papss-network-expands-powering-continental-trade-dream" rel="noopener noreferrer"&gt;PAPSS had expanded to 19 countries with over 150 commercial banks and 14 payment switches connected as of 2025&lt;/a&gt;. It enables real-time cross-border payments in local currencies, bypassing the dollar and euro intermediaries that have historically made intra-African trade expensive.&lt;/p&gt;

&lt;p&gt;Two major products launched in 2025. In June, &lt;a href="https://www.afreximbank.com/africa-launches-first-pan-african-card-scheme-papsscard/" rel="noopener noreferrer"&gt;PAPSSCARD launched as Africa's first continental card scheme&lt;/a&gt; — a joint venture between Afreximbank, PAPSS, and Mercury Payment Services that keeps card processing fees, data, and value within the continent. In July, &lt;a href="https://www.afreximbank.com/papss-and-interstellar-unveil-african-currency-marketplace-eliminating-5-billion-trade-bottleneck/" rel="noopener noreferrer"&gt;PAPSS and Interstellar launched the PAPSS African Currency Marketplace (PACM)&lt;/a&gt; for direct peer-to-peer exchange of African currencies without converting through a third currency. Over 80 corporates transacted across 12 currency pairs during the pilot. The target is eliminating an estimated $5 billion per year in cross-border transaction costs.&lt;/p&gt;

&lt;p&gt;PAPSS has not yet closed the fragmentation between regional systems. EAPS and COMESA's REPSS have existed for years but struggle with limited liquidity. PAPSS's stated ambition is to act as a "network of networks" for continental net settlement — but that integration is still in progress.&lt;/p&gt;

&lt;p&gt;There is also a blockchain layer emerging. &lt;a href="https://techcrunch.com/2024/03/18/nigerian-fintech-zone-raises-8-5m-to-scale-its-decentralized-payment-infrastructure/" rel="noopener noreferrer"&gt;Zone is Africa's first regulated blockchain payment network, licensed by the CBN&lt;/a&gt;. Its architecture routes transactions directly between institutions without a central intermediary. Zenith Bank, First Bank, and UBA joined the network in July 2024. By end of 2024, Zone had processed over ₦1 trillion in transactions. &lt;a href="https://techcabal.com/2025/03/07/zone-processes-1-trillion/" rel="noopener noreferrer"&gt;NIBSS subsequently joined as a regulatory node&lt;/a&gt; — the first time a major regulator has operated within a blockchain payment network at this scale.&lt;/p&gt;




&lt;h2&gt;
  
  
  The identity and KYC layer
&lt;/h2&gt;

&lt;p&gt;Africa does not have a unified identity system. Nigeria has the BVN and NIN. Kenya has Huduma Namba. Ghana has the Ghana Card. These are nationally strong but do not connect across borders, which creates friction every time a service tries to onboard a user from another country.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.elibrary.imf.org/view/journals/087/2025/004/article-A001-en.xml" rel="noopener noreferrer"&gt;A 2025 IMF report on digital payments in Sub-Saharan Africa&lt;/a&gt; found that as of 2021, only 65 percent of the region's population were enrolled in national identification systems, which was below other emerging market averages.&lt;/p&gt;

&lt;p&gt;For developers, KYC is not a solved problem you can call one API for. Depending on which countries you are serving, you are either integrating with a national identity API where one exists, relying on document verification providers like Smile Identity or Youverify, or handling manual review for edge cases. The compliance burden scales non-linearly with each country you add.&lt;/p&gt;

&lt;p&gt;Nigeria adds a hard constraint. The BVN and NIN are designated as &lt;a href="https://www.banwo-ighodalo.com/grey-matter/how-nigerias-data-localization-regime-shapes-fintechs-handling-of-financial-identity-and-transaction-data/" rel="noopener noreferrer"&gt;Critical National Information Infrastructure under Nigeria's 2024 Designation Order&lt;/a&gt;, meaning data derived from them cannot be transferred outside Nigeria without specific NITDA approval. This is a constraint that must be resolved at the infrastructure level, not the application level.&lt;/p&gt;




&lt;h2&gt;
  
  
  The cross-border settlement layer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://transak.com/blog/africa-fintech-stablecoin-report-2026" rel="noopener noreferrer"&gt;Only 12 percent of intra-African transactions are fully processed on the continent&lt;/a&gt;. The remaining 88 percent are routed through the US or Europe. Afreximbank's President Benedict Oramah has noted it is easier for a bank in an African country to finance trade with a European counterpart than with its neighbours.&lt;/p&gt;

&lt;p&gt;This is the result of regulatory fragmentation, thin FX liquidity in many markets, and a correspondent banking system built around USD and EUR clearing. PAPSS, PAPSSCARD, and PACM are the formal responses. But two other forces are already reshaping settlement in ways the formal layer has not fully absorbed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stablecoins&lt;/strong&gt; have moved from fringe to functional. &lt;a href="https://transak.com/blog/africa-fintech-stablecoin-report-2026" rel="noopener noreferrer"&gt;Kenya ranks 5th globally for transactional stablecoin use. Nigeria received over $30 billion in DeFi value in 2024&lt;/a&gt;, making Sub-Saharan Africa the global leader in DeFi adoption. March 2025 alone saw $25 billion in on-chain volume in Nigeria driven by a currency devaluation window. Nigeria's Investment and Securities Act 2025 formally recognized digital assets as securities. South Africa had approved 248 crypto licenses by December 2024. Institutional flows are gradually shifting toward regulated stablecoins like USDC alongside USDT.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FX volatility&lt;/strong&gt; is a core architectural constraint. &lt;a href="https://techpoint.africa/insight/usd-backed-stablecoins-fuel-nigerias-trade-amid-fx-uncertainty/" rel="noopener noreferrer"&gt;Ola Oyetayo, CEO of Verto, described how Nigerian businesses now prioritize speed of settlement over rate optimization&lt;/a&gt; — the risk of being caught in a devaluation window during a slow settlement cycle often exceeds the cost of a slightly worse rate. Any payment infrastructure operating in these markets must be honest about settlement timing, not just the rate at the moment of quote.&lt;/p&gt;




&lt;h2&gt;
  
  
  The open banking and data layer
&lt;/h2&gt;

&lt;p&gt;Nigeria and Kenya are the furthest along. &lt;a href="https://www.bcg.com/publications/2026/beyond-payments-unlocking-africas-second-fintech-wave" rel="noopener noreferrer"&gt;BCG's 2026 fintech report identifies open banking reforms in both countries&lt;/a&gt; as the primary mechanism for expanding data portability and enabling data-driven credit infrastructure. The CBK has supported interoperability and risk-based KYC discussions. Nigeria's CBN is developing standardized APIs for licensed third-party account data access with user consent.&lt;/p&gt;

&lt;p&gt;In practice today, open banking in Africa is market-specific. Mono covers Nigeria. Stitch covers Southern Africa. Continent-wide coverage does not yet exist as a stable surface to build against. The &lt;a href="https://www.businesswire.com/news/home/20251126842408/en/Africa-Embedded-Finance-Business-Report-2025" rel="noopener noreferrer"&gt;embedded finance market was valued at $11.9 billion in 2024 and is projected to reach $18 billion by 2030&lt;/a&gt; — and a significant portion of that depends on open finance data rails maturing enough to support lending and insurance products priced against transaction history.&lt;/p&gt;

&lt;p&gt;Data sovereignty remains a hard constraint in this layer. Nigeria's NDPA 2023 and NITDA's localisation guidelines mean transaction and identity data for Nigerian users cannot flow freely to infrastructure outside the country. Every architecture decision involving this data has a compliance dimension that has to be resolved upfront.&lt;/p&gt;




&lt;h2&gt;
  
  
  The compliance and RegTech layer
&lt;/h2&gt;

&lt;p&gt;Fraud prevention has moved from a compliance checkbox to core infrastructure. &lt;a href="https://techpoint.africa/guide/african-fintech-outlook/" rel="noopener noreferrer"&gt;Nikolai Barnwell, CEO of pawaPay, described 2025 as a year defined by regulatory and infrastructure maturity rather than headline innovation&lt;/a&gt;. In the same Techpoint Africa fintech leaders survey, Okpagu was direct: "If a fintech isn't investing in real-time, AI-led risk scoring that looks at the whole picture of a transaction, losses from sophisticated fraud will likely outpace growth."&lt;/p&gt;

&lt;p&gt;Compliance-as-a-service is becoming its own infrastructure category. The trend is toward API-accessible KYC, AML screening, and sanctions checking that can be called at onboarding and transaction time — rather than each company building and maintaining these pipelines across multiple jurisdictions independently. Regulatory requirements update frequently and simultaneously across different markets. Maintaining this in-house is a significant ongoing engineering cost.&lt;/p&gt;

&lt;p&gt;The deepest structural problem remains regulatory fragmentation. Licensing requirements for payment service providers differ by country. KYC standards are not harmonized. AML thresholds vary. &lt;a href="https://consonanceclub.substack.com/p/fintech-in-africa-is-changing-but" rel="noopener noreferrer"&gt;Consonance Club's 2026 analysis notes the defining shift is toward compliance platforms&lt;/a&gt; precisely because the ecosystem spans 54 currencies and regulatory regimes.&lt;/p&gt;




&lt;h2&gt;
  
  
  The embedded finance layer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.bcg.com/publications/2026/beyond-payments-unlocking-africas-second-fintech-wave" rel="noopener noreferrer"&gt;BCG projects African fintech revenues growing roughly 13x to approximately $65 billion by 2030&lt;/a&gt;, with embedded finance as a primary driver.&lt;/p&gt;

&lt;p&gt;M-Pesa has expanded from P2P transfers into savings, investments, loans, BNPL, virtual cards, and insurance. MTN MoMo now spans payments, e-commerce, insurance, lending, and remittances. &lt;a href="https://consonanceclub.substack.com/p/fintech-in-africa-is-changing-but" rel="noopener noreferrer"&gt;Moniepoint crossed unicorn status in October 2024 after its $110 million Series C and processes more than $22 billion in monthly transactions&lt;/a&gt;, having moved well beyond POS into full SME banking.&lt;/p&gt;

&lt;p&gt;The B2B shift is the dominant investment thesis — away from consumer products and toward the rails other businesses build on. &lt;a href="https://insights.techcabal.com/whats-next-for-african-tech-5-key-predictions-for-2026/" rel="noopener noreferrer"&gt;TechCabal's 2026 predictions document a 42 percent surge in expansions and a 72 percent spike in M&amp;amp;A in 2025&lt;/a&gt;, with Tier 1 market players acquiring competitors in neighbouring markets to build regional infrastructure positions rather than rebuilding from scratch. NALA's transformation from a consumer remittance app into Rafiki — a B2B payout infrastructure company — is the clearest example of where the sector is heading.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this means if you are building on this stack
&lt;/h2&gt;

&lt;p&gt;The stack has real depth. Mobile money coverage is extensive. Domestic instant payment systems work. PAPSS is expanding. Zone is proving a regulated blockchain payment layer is achievable. The identity and compliance layers are improving.&lt;/p&gt;

&lt;p&gt;The gaps are equally real. 88 percent of intra-African transactions still route through foreign intermediaries. Identity infrastructure is nationally strong but not cross-border interoperable. Open banking APIs are market-specific. Regulatory requirements across 54 countries do not harmonize.&lt;/p&gt;

&lt;p&gt;For a developer, your rails matter as much as your code. The payment API you choose determines which markets you can serve, how honest the rate information is, and whether the webhook events reflect how African payment networks actually behave — including &lt;code&gt;IN_REVIEW&lt;/code&gt; holds, &lt;code&gt;PROCESSING&lt;/code&gt; delays, and the difference between a &lt;code&gt;FAILED&lt;/code&gt; and &lt;code&gt;REJECTED&lt;/code&gt; status.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://business.afriex.com/" rel="noopener noreferrer"&gt;Afriex Business API&lt;/a&gt; is built directly against this infrastructure reality. Mobile money, bank transfers, SWIFT, and local payment channels are all first-class integrations. Exchange rates are live across NGN, KES, GHS, GBP, and other African market pairs. Webhooks cover every meaningful status transition in the African payment lifecycle. If you want to see how the full integration works end to end — KYC registration, payment method attachment, payout execution, and webhook handling — the &lt;a href="https://dev.to/afriex/build-a-freelancer-payout-platform-with-the-afriex-sdk-and-nextjs-2oc8"&gt;freelancer payout platform tutorial&lt;/a&gt; walks through exactly that.&lt;/p&gt;

&lt;p&gt;The stack is real. The gaps are known. Building on it well requires understanding both.&lt;/p&gt;

</description>
      <category>fintech</category>
      <category>afriex</category>
      <category>crossborder</category>
      <category>payments</category>
    </item>
    <item>
      <title>Why Most Payment APIs Fail Developers Building for African Markets</title>
      <dc:creator>Victory Lucky</dc:creator>
      <pubDate>Wed, 15 Apr 2026 14:34:04 +0000</pubDate>
      <link>https://dev.to/afriex/why-most-payment-apis-fail-developers-building-for-african-markets-4dfj</link>
      <guid>https://dev.to/afriex/why-most-payment-apis-fail-developers-building-for-african-markets-4dfj</guid>
      <description>&lt;p&gt;There is a version of this problem that does not get talked about enough. A developer somewhere in Lagos, Nairobi, or Accra opens the docs for a payment API they want to integrate. The quickstart looks clean. The sandbox spins up fast. The first test transaction goes through without a hitch. Three weeks later, they are deep in the actual integration and everything starts falling apart in ways the documentation did not warn them about.&lt;/p&gt;

&lt;p&gt;This is not a capability problem. The infrastructure exists. Africa processed over $1.1 trillion in mobile money transactions in 2024, more than any other region in the world — a figure documented in the &lt;a href="https://intasend.com/payments/payment-apis-and-interoperability-building-africa-s-financial-future" rel="noopener noreferrer"&gt;Payment APIs and Interoperability report&lt;/a&gt; on Africa's financial infrastructure. The rails are real. The problem is that most payment APIs were designed for a different developer, in a different market, and then shipped to everyone else with the expectation that it would just work.&lt;/p&gt;

&lt;p&gt;It does not just work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The documentation was written for a developer in San Francisco
&lt;/h2&gt;

&lt;p&gt;This sounds harsh but it is just accurate. Open the getting started guide for most major payment APIs and you will find examples that create charges in USD, reference Stripe's ecosystem as a benchmark, and use webhook examples that assume a stable server with a static IP. The error codes are documented for card-based payments. The currency handling assumes floating-point precision does not matter much because dollar amounts are stable.&lt;/p&gt;

&lt;p&gt;None of that maps to what you are actually building.&lt;/p&gt;

&lt;p&gt;If you are building something that pays a contractor in Ghana via MTN Mobile Money, or routes a disbursement to a bank account in Kenya while your business collects in dollars, you will not find a worked example for that in most API docs. You will find a generic &lt;code&gt;/transfers&lt;/code&gt; endpoint and a note that says "contact support for regional availability." That note is doing a lot of work.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://financeinafrica.com/insights/apis-africas-developers-money-code/" rel="noopener noreferrer"&gt;Finance in Africa analysis of the developer API ecosystem&lt;/a&gt; found that some providers still require extended approval processes or manual credentials before you can even access certain regional endpoints. Chipper Cash's Head of Payments, who is quoted in the same piece, also cited sandbox reliability and inadequate developer support as persistent gaps. You cannot prototype your way through a manual approval gate. So before you have written a line of production code, you are already blocked.&lt;/p&gt;

&lt;h2&gt;
  
  
  FX volatility is a runtime problem, not a business problem
&lt;/h2&gt;

&lt;p&gt;Every developer building for markets like Nigeria has had to think about exchange rates in a way that developers in stable currency markets never do. Between mid-2023 and late 2024, the naira went from roughly N460 to the dollar to nearly N1,740 — &lt;a href="https://punchng.com/nairas-tumultuous-trajectory/" rel="noopener noreferrer"&gt;Bloomberg ranked it the worst-performing currency in 2024 after a 70 percent drop in one year&lt;/a&gt;. By early 2026 it had stabilized somewhere in the N1,350 to N1,450 range, but analysts &lt;a href="https://businessday.ng/market-intelligence/article/winners-losers-of-naira-volatility-in-2024/" rel="noopener noreferrer"&gt;documented businesses reporting FX losses in the hundreds of billions of naira&lt;/a&gt; across the cement, FMCG, and brewing sectors during the peak volatility window.&lt;/p&gt;

&lt;p&gt;That is not a footnote. That is a core architectural constraint.&lt;/p&gt;

&lt;p&gt;The problem is that most payment API docs treat exchange rates as a lookup, not a liability. You call a rates endpoint, you get a number, you show it to your user. What they do not tell you is what happens between the moment your user confirms the amount and the moment the transaction settles. In volatile markets, that spread can be meaningful.&lt;/p&gt;

&lt;p&gt;Ola Oyetayo, CEO of Nigerian fintech Verto, explained in a &lt;a href="https://techpoint.africa/insight/usd-backed-stablecoins-fuel-nigerias-trade-amid-fx-uncertainty/" rel="noopener noreferrer"&gt;Techpoint Africa interview on FX uncertainty&lt;/a&gt; that when the naira moves 5 to 10 percent in a matter of days, providers widen their spreads as a defensive move — not out of opportunism, but because they need to account for the slippage between the moment a customer initiates a transfer and the moment it actually settles in the global market. During high-volatility windows, pricing updates can happen minute by minute. If the API you integrated does not expose that to you, your users will see one number and receive another, and they will blame your product, not the underlying rails.&lt;/p&gt;

&lt;p&gt;A payment API built for this market surfaces rate information that is honest about timing, expiry, and settlement risk. Most do not.&lt;/p&gt;

&lt;h2&gt;
  
  
  NITDA and the compliance layer most docs skip
&lt;/h2&gt;

&lt;p&gt;Here is the part that catches international developers building for Nigerian users completely off guard.&lt;/p&gt;

&lt;p&gt;Nigeria's data regulatory landscape is layered. &lt;a href="https://digitalpolicyalert.org/digest/dpa-digital-digest-nigeria" rel="noopener noreferrer"&gt;NITDA has mandated since 2019 that subscriber and consumer data be hosted within Nigeria&lt;/a&gt;. The Nigeria Data Protection Act 2023 (NDPA), which supersedes the earlier NDPR, requires that organisations processing data for more than 200 Nigerian residents within a six-month period register as Data Controllers or Processors of Major Importance. As &lt;a href="https://www.banwo-ighodalo.com/grey-matter/how-nigerias-data-localization-regime-shapes-fintechs-handling-of-financial-identity-and-transaction-data/" rel="noopener noreferrer"&gt;Banwo and Ighodalo's analysis of Nigeria's data localisation regime&lt;/a&gt; explains, the CBN's 2020 guidelines on electronic payment channels further require that domestic transactions be routed through a local switch.&lt;/p&gt;

&lt;p&gt;This means the payment API you integrated, which is running on AWS us-east-1 and stores your transaction logs in a US-based database, may be putting you in technical breach of Nigerian data localisation rules the moment you launch with Nigerian users. A &lt;a href="https://nigerianjournalsonline.org/index.php/MJETS/article/download/4018/3772/7289" rel="noopener noreferrer"&gt;2025 peer-reviewed study of Nigerian fintech software developers&lt;/a&gt; published in the Multidisciplinary Journal of Engineering, Technology and Sciences found that while engineers were generally aware of NDPR and GDPR concepts, the hardest part was implementing compliance in actual coding and system design — partly because of a lack of clear compliance documentation and region-specific toolkits.&lt;/p&gt;

&lt;p&gt;That gap is where most payment API providers leave you. The technical documentation tells you how to make an API call. It does not tell you whether that call, and the data it generates, is compliant with the regulatory environment of the markets you are serving.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fragmentation is not an accident
&lt;/h2&gt;

&lt;p&gt;Africa is not one market. It is 54 countries with &lt;a href="https://intasend.com/payments/payment-apis-and-interoperability-building-africa-s-financial-future" rel="noopener noreferrer"&gt;178 different mobile money services&lt;/a&gt;, different currencies, different regulatory environments, and different payment rails. A payment that settles in seconds in Kenya via M-Pesa uses entirely different infrastructure than a bank transfer in South Africa or a mobile money disbursement in Ghana.&lt;/p&gt;

&lt;p&gt;Chipper Cash's Adeeyo, quoted in the &lt;a href="https://financeinafrica.com/insights/apis-africas-developers-money-code/" rel="noopener noreferrer"&gt;Finance in Africa developer ecosystem analysis&lt;/a&gt;, said publicly that fragmentation remains a major challenge both technically and institutionally, and that regulatory inconsistency across countries makes regional scaling harder. That is coming from someone operating in this market full-time. For a developer integrating a payment API, that fragmentation shows up as undocumented behaviour: a call that works in sandbox for one country silently returns a different response structure in production for another, error messages that are not granular enough to tell you whether a failure was a network issue, a KYC issue, or an FX liquidity issue.&lt;/p&gt;

&lt;p&gt;Benjamin Fernandes, founder of NALA, documented this in a &lt;a href="https://medium.com/@Benji_Fernandes/why-are-payments-in-africa-still-1-built-introducing-rafiki-api-dc215fe821a6" rel="noopener noreferrer"&gt;detailed post on why payments in emerging markets are still 1 percent built&lt;/a&gt;. Between May 2023 and January 2024, NALA averaged 25 payment partner issues per month. Webhook callbacks that did not fire. Incorrect reconciliation. APIs that confirmed delivery when the money had not moved. These are not edge cases. These are the operational reality of building on payment infrastructure that was not designed with African markets as the primary concern.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a payment API built for this market actually looks like
&lt;/h2&gt;

&lt;p&gt;It surfaces real-time exchange rates with honest settlement windows, not just a number. It handles mobile money, bank accounts, SWIFT, and local payment methods as first-class channels, not afterthoughts. It documents its regulatory posture explicitly — where data is stored, how transfers are handled under Nigerian or Kenyan data law, what compliance burden it takes on versus what it leaves with you.&lt;/p&gt;

&lt;p&gt;It also designs its error responses for the failure modes that actually happen in these markets: FX liquidity gaps, KYC tier limitations, mobile network outages that interrupt but do not fail a transaction. The &lt;a href="https://docs.afriex.com" rel="noopener noreferrer"&gt;Afriex Business API&lt;/a&gt; is built around exactly these constraints. The &lt;a href="https://docs.afriex.com/api-reference/endpoint/rates/get" rel="noopener noreferrer"&gt;exchange rates&lt;/a&gt; endpoint exposes live rates for supported currency pairs including NGN, KES, GHS, and GBP. The &lt;a href="https://docs.afriex.com/api-reference/endpoint/payment-methods/create" rel="noopener noreferrer"&gt;payment methods&lt;/a&gt; layer handles bank accounts, mobile money (including MTN Mobile Money), SWIFT, UPI, and Interac as fully supported channels, not beta features. The webhooks are signed, retried with exponential backoff, and documented for every transaction status that matters, including &lt;code&gt;IN_REVIEW&lt;/code&gt; for compliance holds that are a real operational scenario in African markets.&lt;/p&gt;

&lt;p&gt;That is what it looks like when the API was designed with this market in mind from the start, not retrofitted after the fact.&lt;/p&gt;

&lt;p&gt;If you are building something that moves money across African borders and the payment API you are using does not talk about FX settlement windows, does not document its compliance posture for Nigerian or Kenyan data law, and does not have mobile money as a first-class channel, you are not using the wrong feature. You are using the wrong tool.&lt;/p&gt;

</description>
      <category>api</category>
      <category>fintech</category>
      <category>developer</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
