<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged pawapay</title>
<link>https://coderlegion.com/tag/pawapay</link>
<description>Powered by Question2Answer</description>
<item>
<title>The Easiest Way to Integrate pawaPay in Your Java Spring Boot App</title>
<link>https://coderlegion.com/19406/the-easiest-way-to-integrate-pawapay-in-your-java-spring-boot-app</link>
<description>&lt;p&gt;Building a high-throughput mobile money gateway is often romanticized as a simple REST API integration. The reality, however, is a brutal landscape of undocumented MNO network drops, complex cryptographic signature requirements, and terrifying double-spend vulnerabilities. When development teams attempt to hand-roll these integrations using standard Java HTTP clients like OkHttp or RestTemplate, they inadvertently construct a fragile house of cards. They pull highly sensitive environment secrets directly into the JVM heap to manually compute &lt;code&gt;Signature-Input&lt;/code&gt; headers, exposing their financial infrastructure to memory scraping and reverse-engineering. Furthermore, relying on blocking I/O to wait for unpredictable African telecom networks guarantees that thread pools will eventually exhaust, triggering cascading outages across the entire application architecture.&lt;/p&gt;
&lt;p&gt;This is the integration illusion. You think you are building a simple payment gateway, but you are actually attempting to build a secure, fault-tolerant, state-managing cryptographic vault from scratch.&lt;/p&gt;
&lt;p&gt;We recognized that engineering teams do not have months to waste debugging manual boilerplate. You need speed, concurrency, and ironclad security from day one. To solve this, we engineered the pawaPay Java SDK—a completely new paradigm for mobile money integration that shifts the heavy lifting away from your application logic and into a compiled, memory-safe environment. By adopting this architecture, you can execute non-blocking, asynchronous mobile money deposits and payouts in under five minutes.&lt;/p&gt;
&lt;h5 id=&quot;step-one-securing-the-artifact&quot;&gt;Step One: Securing the Artifact&lt;/h5&gt;
&lt;p&gt;The foundation of a secure integration begins with your dependency management. We distribute the official package directly through Maven Central, ensuring you receive cryptographically verified, unaltered binaries. You can view the live artifact registry at &lt;a rel=&quot;nofollow&quot; href=&quot;https://central.sonatype.com/artifact/com.katorymnd/pawapay-java-sdk&quot;&gt;https://central.sonatype.com/artifact/com.katorymnd/pawapay-java-sdk&lt;/a&gt; to verify the latest release versions.&lt;/p&gt;
&lt;p&gt;To bring the native power of the SDK into your project, simply declare it within your &lt;code&gt;pom.xml&lt;/code&gt; configuration file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;com.katorymnd&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;pawapay-java-sdk&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;LATEST_VERSION&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once synced, you are no longer just importing a library; you are embedding a highly optimized Java Native Interface (JNI) vault engineered in Rust. This core will handle all payload serialization and cryptographic signing entirely outside of the standard Java memory space, neutralizing standard de-compilation threats immediately.&lt;/p&gt;
&lt;h5 id=&quot;step-two-zero-trust-configuration&quot;&gt;Step Two: Zero-Trust Configuration&lt;/h5&gt;
&lt;p&gt;We engineer under the assumption that application state can be volatile, which is why we enforce a strict, zero-trust initialization process. There are no fallback secrets, and there are no dummy variables allowed. Your master API tokens and your specific Katorymnd license key must be injected via strict environment variables. If these are missing or malformed, the SDK aborts the boot sequence instantly to protect your system from executing unauthenticated requests.&lt;/p&gt;
&lt;p&gt;To wire this into a Spring Boot application, you construct a dedicated &lt;code&gt;@Configuration&lt;/code&gt; class. This class uses our &lt;code&gt;Config.Builder&lt;/code&gt; to construct the environment parameters and boot the &lt;code&gt;ApiClient&lt;/code&gt; securely.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;import com.katorymnd.pawapay.sdk.api.ApiClient;
import com.katorymnd.pawapay.sdk.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class PawaPayGatewayConfig {

    @Bean
    public ApiClient pawaPayClient() {
        String apiKey = System.getenv(&quot;PAWAPAY_API_TOKEN&quot;);
        String licenseKey = System.getenv(&quot;KATORYMND_PAWAPAY_SDK_LICENSE_KEY&quot;);

        Config config = new Config.Builder()
            .apiKey(apiKey)
            .environment(&quot;production&quot;)
            .timeout(30000)
            .build();

        return new ApiClient(config, licenseKey, true, &quot;v2&quot;);
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By defining the &lt;code&gt;ApiClient&lt;/code&gt; as a Spring Bean, you ensure the native vault is loaded exactly once during the application lifecycle. The vault establishes the secure connection pool, locks in the hardware anchoring, and remains resident in memory, ready to process thousands of concurrent requests without the overhead of re-initialization.&lt;/p&gt;
&lt;h5 id=&quot;step-three-the-reactive-endpoint&quot;&gt;Step Three: The Reactive Endpoint&lt;/h5&gt;
&lt;p&gt;The true architectural elegance of this integration reveals itself at the routing layer. Because the SDK was built specifically for modern, high-throughput environments, every core method returns a &lt;code&gt;CompletableFuture&lt;/code&gt;. This maps natively to Project Reactor, allowing you to build completely non-blocking Spring WebFlux endpoints.&lt;/p&gt;
&lt;p&gt;When a payload arrives, your application threads do not wait. The request is handed to the native core, and the thread is immediately liberated back to the pool to handle other users.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;import com.katorymnd.pawapay.sdk.api.ApiClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import java.util.Map;

import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;

@Configuration
public class DepositController {

    @Bean
    public RouterFunction&amp;lt;ServerResponse&amp;gt; handleDeposit(ApiClient pawaPayClient) {
        return route(POST(&quot;/api/v2/payments/deposit&quot;), request -&amp;gt; 
            request.bodyToMono(Map.class)
                .flatMap(payload -&amp;gt; Mono.fromFuture(
                    pawaPayClient.initiateDepositV2(
                        payload.get(&quot;transactionId&quot;).toString(),
                        payload.get(&quot;amount&quot;).toString(),
                        payload.get(&quot;currency&quot;).toString(),
                        payload.get(&quot;phone&quot;).toString(),
                        payload.get(&quot;provider&quot;).toString(),
                        &quot;Spring WebFlux Integration&quot;,
                        null, null, null
                    )
                ))
                .flatMap(response -&amp;gt; ServerResponse.ok().bodyValue(response))
                .onErrorResume(error -&amp;gt; ServerResponse.badRequest().bodyValue(Map.of(&quot;error&quot;, error.getMessage())))
        );
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In just three steps, you have bypassed weeks of manual REST implementation. You possess an endpoint that can absorb massive MNO latency spikes without crashing your server, backed by a cryptographic engine that mathematically guarantees your payload integrity.&lt;/p&gt;
&lt;h5 id=&quot;validating-the-architecture&quot;&gt;Validating the Architecture&lt;/h5&gt;
&lt;p&gt;Before writing a single line of your own code, you can visually verify this exact execution flow. We have exposed our internal, Javalin-based disposable execution environment to the public. By visiting the live playground at &lt;a rel=&quot;nofollow&quot; href=&quot;https://katorymnd.dev/pawapay-demo/java/&quot;&gt;https://katorymnd.dev/pawapay-demo/java/&lt;/a&gt;, you can input your sandbox tokens and watch the native core process deposits, handle automated config syncs, and securely fetch MNO availability in real-time. It provides a transparent, terminal-level view of the exact security validations happening beneath the surface of the SDK.&lt;/p&gt;
&lt;h5 id=&quot;transitioning-to-enterprise-production&quot;&gt;Transitioning to Enterprise Production&lt;/h5&gt;
&lt;p&gt;While the artifact is freely available on Maven Central for integration testing, deploying this architecture into a live financial environment requires absolute operational certainty. For businesses processing real capital, we offer the Premium License infrastructure detailed at &lt;a rel=&quot;nofollow&quot; href=&quot;https://katorymnd.com/pawapay-payment-sdk/java&quot;&gt;https://katorymnd.com/pawapay-payment-sdk/java&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Securing a premium license unlocks critical production features, including custom domain locking to prevent your API keys from being used outside of authorized IP addresses, emergency technical support SLAs, and architectural consultation for your specific database synchronization strategy. It is the definitive path for teams that refuse to compromise on gateway resilience.&lt;/p&gt;
&lt;p&gt;Stop wrestling with clunky HTTP clients and vulnerable cryptographic libraries. Cut the boilerplate, implement the native SDK, and secure your mobile money infrastructure today.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/19406/the-easiest-way-to-integrate-pawapay-in-your-java-spring-boot-app</guid>
<pubDate>Mon, 01 Jun 2026 10:08:14 +0000</pubDate>
</item>
<item>
<title>pawaPay PHP SDK v4.4.0 adds dual V1/V2 support</title>
<link>https://coderlegion.com/4629/pawapay-php-sdk-v4-4-0-adds-dual-v1-v2-support</link>
<description>&lt;h2 id=&quot;pawapay-php-sdk-v4-4-0-dual-version-support-v1-v2-seamless-migration&quot;&gt;&lt;strong&gt;pawaPay PHP SDK v4.4.0, Dual Version Support [V1 + V2], Seamless Migration&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;pawaPay helps teams accept and move money across African markets with a clean API surface. In &lt;strong&gt;v4.4.0&lt;/strong&gt; of the &lt;strong&gt;pawaPay PHP SDK&lt;/strong&gt;, we introduced &lt;strong&gt;dual version support&lt;/strong&gt; so &lt;strong&gt;V1&lt;/strong&gt; and &lt;strong&gt;V2&lt;/strong&gt; can live together in one codebase. Think of it as two gears turning in sync, V1 for stability, V2 for velocity.&lt;/p&gt;
&lt;p&gt;This design gives you the freedom to adopt V2 where it shines, while keeping proven V1 flows earning in production.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;table-of-contents&quot;&gt;Table of contents&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Why dual version support&lt;/li&gt;
&lt;li&gt;What changed in v4.4.0&lt;/li&gt;
&lt;li&gt;Environment setup and configuration&lt;/li&gt;
&lt;li&gt;Full runnable examples, V1 and V2&lt;/li&gt;
&lt;li&gt;Hosted Payment Page, end to end&lt;/li&gt;
&lt;li&gt;Verification flow, returnUrl and verify without cURL&lt;/li&gt;
&lt;li&gt;Testing and safety nets with PHPUnit&lt;/li&gt;
&lt;li&gt;Migration playbook, step by step&lt;/li&gt;
&lt;li&gt;Edge cases, country notes, and logging&lt;/li&gt;
&lt;li&gt;Troubleshooting guide&lt;/li&gt;
&lt;li&gt;Security notes and hardening tips&lt;/li&gt;
&lt;li&gt;Where to get it&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-why-dual-version-support&quot;&gt;1) Why dual version support&lt;/h3&gt;
&lt;p&gt;Upgrading payments is risky. Teams struggle with three realities:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;All or nothing switches increase outage risk.&lt;/li&gt;
&lt;li&gt;Two SDKs mean duplicate configs and twice the drift.&lt;/li&gt;
&lt;li&gt;Testing in production is scary without a clean rollback.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Our approach&lt;/strong&gt;: keep &lt;strong&gt;V1&lt;/strong&gt; as the default, allow &lt;strong&gt;V2&lt;/strong&gt; to be toggled by environment or per call. You get gradual rollouts, quick comparisons, and instant fallback.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-what-changed-in-v4-4-0&quot;&gt;2) What changed in v4.4.0&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Version aware client&lt;/strong&gt;. Pass &lt;code&gt;v1&lt;/code&gt; or &lt;code&gt;v2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hosted Payment Page&lt;/strong&gt; usable from both versions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Split config files&lt;/strong&gt; for clarity: &lt;code&gt;active_conf_v1.json&lt;/code&gt;, &lt;code&gt;active_conf_v2.json&lt;/code&gt;, plus versioned MNO availability JSONs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better failure mapping&lt;/strong&gt; via an improved &lt;code&gt;FailureCodeHelper&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cleaner errors&lt;/strong&gt; that mention the version expectation.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-environment-setup-and-configuration&quot;&gt;3) Environment setup and configuration&lt;/h3&gt;
&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; with clear tokens per environment.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dotenv&quot;&gt;# .env
ENVIRONMENT=sandbox
PAWAPAY_SANDBOX_API_TOKEN=your_sandbox_api_token_here
PAWAPAY_PRODUCTION_API_TOKEN=your_production_api_token_here

# Choose default API version across your app
PAWAPAY_API_VERSION=v1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Composer install:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;composer require katorymnd/pawa-pay-integration
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-full-runnable-examples-v1-and-v2&quot;&gt;4) Full runnable examples, V1 and V2&lt;/h3&gt;
&lt;h4 id=&quot;a-minimal-v1-example-server-side-initiation&quot;&gt;A) Minimal V1 example, server side initiation&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;&amp;lt;?php
/**
 * Example: Minimal V1 deposit initiation
 * Goal, show a working flow with V1 as default.
 * Notes, reads ENV, builds a V1 client, initiates a classic deposit call.
 */

require_once __DIR__ . '/../vendor/autoload.php';

use Dotenv\Dotenv;
use Katorymnd\PawaPayIntegration\Api\ApiClient;

$dotenv = Dotenv::createImmutable(__DIR__ . '/..');
$dotenv-&amp;gt;load();

$environment = getenv('ENVIRONMENT') ?: 'sandbox';
$sslVerify   = ($environment === 'production');
$apiTokenKey = 'PAWAPAY_' . strtoupper($environment) . '_API_TOKEN';
$apiToken    = $_ENV[$apiTokenKey] ?? null;

if (!$apiToken) {
    http_response_code(500);
    echo 'Missing API token';
    exit;
}

$client = new ApiClient($apiToken, $environment, $sslVerify, 'v1');

// Payload shape may differ per version, keep V1 fields here
$resp = $client-&amp;gt;initiateDeposit([
    'amount'      =&amp;gt; '15000',
    'currency'    =&amp;gt; 'UGX',
    'phoneNumber' =&amp;gt; '256753456789',
    'mno'         =&amp;gt; 'MTN',
    'reason'      =&amp;gt; 'Order #1234',
]);

header('Content-Type: application/json');
echo json_encode($resp, JSON_PRETTY_PRINT);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;b-minimal-v2-example-auto-flow&quot;&gt;B) Minimal V2 example, auto flow&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;&amp;lt;?php
/**
 * Example: Minimal V2 deposit initiation (auto)
 * Goal, exercise the new V2 route without touching V1 code.
 * Notes, just switch the version to v2.
 */

require_once __DIR__ . '/../vendor/autoload.php';

use Dotenv\Dotenv;
use Katorymnd\PawaPayIntegration\Api\ApiClient;

$dotenv = Dotenv::createImmutable(__DIR__ . '/..');
$dotenv-&amp;gt;load();

$environment = getenv('ENVIRONMENT') ?: 'sandbox';
$sslVerify   = ($environment === 'production');
$apiTokenKey = 'PAWAPAY_' . strtoupper($environment) . '_API_TOKEN';
$apiToken    = $_ENV[$apiTokenKey] ?? null;

if (!$apiToken) {
    http_response_code(500);
    echo 'Missing API token';
    exit;
}

$client = new ApiClient($apiToken, $environment, $sslVerify, 'v2');

// V2 supports initiateDepositAuto which may return a redirect, or a ready status
$resp = $client-&amp;gt;initiateDepositAuto([
    'amount'      =&amp;gt; '15000',
    'currency'    =&amp;gt; 'UGX',
    'phoneNumber' =&amp;gt; '256753456789',
    'mno'         =&amp;gt; 'MTN',
    'returnUrl'   =&amp;gt; 'https://example.com/paymentProcessed',
]);

header('Content-Type: application/json');
echo json_encode($resp, JSON_PRETTY_PRINT);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Why both examples matter&lt;/strong&gt;&lt;br&gt;
You can test V2 side by side, compare payloads and responses, and fall back to V1 with a single parameter change.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-hosted-payment-page-end-to-end&quot;&gt;5) Hosted Payment Page, end to end&lt;/h3&gt;
&lt;p&gt;The hosted page is the fastest way to collect a payment with one redirect.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;&amp;lt;?php
/**
 * Example: Hosted Payment Page, works with V1 or V2
 * Goal, show a clean redirect based on initiateDepositAuto.
 */

require_once __DIR__ . '/../vendor/autoload.php';

use Dotenv\Dotenv;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Katorymnd\PawaPayIntegration\Api\ApiClient;
use Katorymnd\PawaPayIntegration\Utils\Helpers;

$dotenv = Dotenv::createImmutable(__DIR__ . '/..');
$dotenv-&amp;gt;load();

$environment = getenv('ENVIRONMENT') ?: 'sandbox';
$sslVerify   = ($environment === 'production');
$apiVersion  = getenv('PAWAPAY_API_VERSION') ?: 'v1';
$apiTokenKey = 'PAWAPAY_' . strtoupper($environment) . '_API_TOKEN';
$apiToken    = $_ENV[$apiTokenKey] ?? null;

if (!$apiToken) {
    http_response_code(500);
    echo 'Missing API token';
    exit;
}

$log = new Logger('pawaPay');
$log-&amp;gt;pushHandler(new StreamHandler(__DIR__ . '/../logs/payment_success.log', \Monolog\Level::Info));
$log-&amp;gt;pushHandler(new StreamHandler(__DIR__ . '/../logs/payment_failed.log',  \Monolog\Level::Error));

$client = new ApiClient($apiToken, $environment, $sslVerify, $apiVersion);

// Inputs, in real apps take these from your form
$depositId            = Helpers::generateUniqueId();
$amount               = '15000';
$currency             = 'UGX';
$msisdn               = '256753456789';
$mno                  = 'MTN';
$returnUrl            = 'https://your-app.example/returnUrl.php';
$statementDescription = 'Order 1234';

// V2 will typically return a redirect URL for the hosted page
try {
    $resp = $client-&amp;gt;initiateDepositAuto([
        'amount'      =&amp;gt; $amount,
        'currency'    =&amp;gt; $currency,
        'phoneNumber' =&amp;gt; $msisdn,
        'mno'         =&amp;gt; $mno,
        'returnUrl'   =&amp;gt; $returnUrl,
        'reason'      =&amp;gt; $statementDescription,
        'metadata'    =&amp;gt; ['depositId' =&amp;gt; $depositId],
    ]);

    if (!empty($resp['redirectUrl'])) {
        header('Location: ' . $resp['redirectUrl']);
        exit;
    }

    // If no redirect is required, log and show the result
    $log-&amp;gt;info('Hosted page not required', ['resp' =&amp;gt; $resp]);
    header('Content-Type: application/json');
    echo json_encode($resp, JSON_PRETTY_PRINT);
} catch (\Throwable $e) {
    $log-&amp;gt;error('Hosted page error', ['ex' =&amp;gt; $e-&amp;gt;getMessage()]);
    http_response_code(500);
    echo 'Payment initiation failed';
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;6-verification-flow-returnurl-and-verify-without-curl&quot;&gt;6) Verification flow, returnUrl and verify without cURL&lt;/h3&gt;
&lt;p&gt;Many teams prefer not to make HTTP calls to a sibling script. You can include the verifier directly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;verify.php&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;&amp;lt;?php
/**
 * verify.php
 * Purpose, encapsulate verification logic that can be included from returnUrl.php
 * Input, expects $client and $transactionId to be in scope when included
 * Output, returns associative array $verification
 */

if (!isset($client) || !isset($transactionId)) {
    throw new \RuntimeException('verify.php expects $client and $transactionId');
}

$verification = $client-&amp;gt;checkTransactionStatus([
    'transactionId' =&amp;gt; $transactionId,
]);

// The parent script will read $verification
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;returnUrl.php&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;&amp;lt;?php
/**
 * returnUrl.php
 * Purpose, handle redirected users after payment, then verify internally
 */

require_once __DIR__ . '/../vendor/autoload.php';

use Dotenv\Dotenv;
use Katorymnd\PawaPayIntegration\Api\ApiClient;

$dotenv = Dotenv::createImmutable(__DIR__ . '/..');
$dotenv-&amp;gt;load();

$environment = getenv('ENVIRONMENT') ?: 'sandbox';
$sslVerify   = ($environment === 'production');
$apiVersion  = getenv('PAWAPAY_API_VERSION') ?: 'v2';
$apiTokenKey = 'PAWAPAY_' . strtoupper($environment) . '_API_TOKEN';
$apiToken    = $_ENV[$apiTokenKey] ?? null;

$client = new ApiClient($apiToken, $environment, $sslVerify, $apiVersion);

// Transaction id from provider redirect or your metadata
$transactionId = $_GET['txId'] ?? $_POST['txId'] ?? null;

if (!$transactionId) {
    http_response_code(400);
    echo 'Missing transactionId';
    exit;
}

// Include verifier in-process, no cURL required
$verification = [];
require __DIR__ . '/verify.php';

// Persist result, show receipt, or redirect to success page
header('Content-Type: application/json');
echo json_encode(['verified' =&amp;gt; true, 'result' =&amp;gt; $verification], JSON_PRETTY_PRINT);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Why this is safe and simple&lt;/strong&gt;&lt;br&gt;
You keep the verification logic pure PHP, no extra HTTP hops, fewer moving parts, clearer stack traces.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;7-testing-and-safety-nets-with-phpunit&quot;&gt;7) Testing and safety nets with PHPUnit&lt;/h3&gt;
&lt;p&gt;A tiny guard to ensure your app respects the chosen version.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;&amp;lt;?php
/**
 * tests/VersionSelectionTest.php
 * Purpose, assert the client honors PAWAPAY_API_VERSION, prevent regressions.
 */

use PHPUnit\Framework\TestCase;
use Katorymnd\PawaPayIntegration\Api\ApiClient;

final class VersionSelectionTest extends TestCase
{
    public function testChoosesV2WhenEnvSaysV2(): void
    {
        $client = new ApiClient('fake', 'sandbox', false, 'v2');
        $this-&amp;gt;assertTrue(method_exists($client, 'initiateDepositAuto'), 'V2 method should exist');
    }

    public function testChoosesV1WhenEnvSaysV1(): void
    {
        $client = new ApiClient('fake', 'sandbox', false, 'v1');
        $this-&amp;gt;assertTrue(method_exists($client, 'initiateDeposit'), 'V1 method should exist');
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add more tests for response shape, error handling, and idempotency.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;8-migration-playbook-step-by-step&quot;&gt;8) Migration playbook, step by step&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Inventory&lt;/strong&gt; your live flows, deposit, payout, refund, reconciliation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enable dual version&lt;/strong&gt; globally with &lt;code&gt;PAWAPAY_API_VERSION=v1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stand up V2&lt;/strong&gt; in sandbox, run the minimal V2 example.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mirror payloads&lt;/strong&gt; and compare responses, store both logs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dark launch&lt;/strong&gt; V2 for a small traffic slice, or specific markets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor&lt;/strong&gt; failure codes with &lt;code&gt;FailureCodeHelper&lt;/code&gt;, alert on deltas.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flip&lt;/strong&gt; a single product line to V2, observe for one full billing cycle.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rollback rule&lt;/strong&gt; documented, set the env back to &lt;code&gt;v1&lt;/code&gt; if anything spikes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expand&lt;/strong&gt; V2 to more flows after two stable weeks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retire&lt;/strong&gt; V1 only when every flow is stable in V2.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Alternative to env flags&lt;/strong&gt;&lt;br&gt;
Use a &lt;strong&gt;feature flag service&lt;/strong&gt; or a &lt;strong&gt;per request switch&lt;/strong&gt;. Env is simple, flags give you finer control per tenant, market, or route.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;9-edge-cases-country-notes-and-logging&quot;&gt;9) Edge cases, country notes, and logging&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MNO requirements differ&lt;/strong&gt;. Some providers are strict on MSISDN formatting, always sanitize to digits and include country code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Currencies vary&lt;/strong&gt;. Lock your currency per market in config, do not trust form inputs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retrials&lt;/strong&gt;. Use idempotent keys where available, or your own &lt;code&gt;depositId&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logs by version&lt;/strong&gt;. Write to &lt;code&gt;payment_v1.log&lt;/code&gt; and &lt;code&gt;payment_v2.log&lt;/code&gt; so comparisons are easy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metadata discipline&lt;/strong&gt;. Pass your order or cart reference to simplify reconciliation.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;10-troubleshooting-guide&quot;&gt;10) Troubleshooting guide&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Missing API token&lt;/strong&gt;, check &lt;code&gt;.env&lt;/code&gt; names and &lt;code&gt;ENVIRONMENT&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Method not found&lt;/strong&gt; when calling V2, confirm you constructed the client with &lt;code&gt;v2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unexpected currency error&lt;/strong&gt;, confirm ISO code and market pairing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Callback not firing&lt;/strong&gt;, verify your &lt;code&gt;returnUrl&lt;/code&gt; is public and uses HTTPS in production.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Random declines&lt;/strong&gt;, try a different MNO in sandbox, then compare failure codes with &lt;code&gt;FailureCodeHelper&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;11-security-notes-and-hardening-tips&quot;&gt;11) Security notes and hardening tips&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Keep tokens outside your repo, use &lt;code&gt;.env&lt;/code&gt; and secret managers.&lt;/li&gt;
&lt;li&gt;Verify all incoming callbacks, do not rely only on query params, call &lt;code&gt;checkTransactionStatus&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Store only what you need, mask MSISDN in logs, rotate tokens on schedule.&lt;/li&gt;
&lt;li&gt;Enforce TLS, set &lt;code&gt;sslVerify=true&lt;/code&gt; in production.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;12-where-to-get-it&quot;&gt;12) Where to get it&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub&lt;/strong&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/katorymnd/pawa-pay-integration&quot;&gt;https://github.com/katorymnd/pawa-pay-integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Composer&lt;/strong&gt;,&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;composer require katorymnd/pawa-pay-integration
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;closing-note&quot;&gt;Closing note&lt;/h4&gt;
&lt;p&gt;Dual version support is a calm bridge, not a cliff. Start with V1, test V2 where it counts, and move forward with confidence. If you want a diagram of the V1 to V2 rollout path, say the word and I will add a crisp visual.&lt;/p&gt;
</description>
<category>Full-stack Dev</category>
<guid isPermaLink="true">https://coderlegion.com/4629/pawapay-php-sdk-v4-4-0-adds-dual-v1-v2-support</guid>
<pubDate>Tue, 12 Aug 2025 19:24:36 +0000</pubDate>
</item>
<item>
<title>pawaPay SDK offers secure, user-friendly mobile money integration for PHP, ideal for African e-commerce.</title>
<link>https://coderlegion.com/454/pawapay-sdk-offers-secure-user-friendly-mobile-money-integration-ideal-african-commerce</link>
<description>&lt;h2&gt;pawaPay Payment SDK: Streamline Your Payment Processes in PHP&lt;/h2&gt;
&lt;p&gt;In the fast-paced world of &lt;strong&gt;e-commerce and online transactions&lt;/strong&gt;, efficiency, security, and simplicity are paramount. Integrating payment solutions can often be a complex and time-consuming process, especially when dealing with multiple payment methods across different regions. The &lt;strong&gt;pawaPay Payment SDK&lt;/strong&gt; emerges as a robust solution, specially tailored for PHP applications, aiming to streamline the integration of pawaPay's Mobile Money payment solutions. This lightweight, easy-to-use SDK encapsulates powerful functionalities into a developer-friendly package, enabling seamless payment processes within your applications.&lt;/p&gt;
&lt;h3&gt;What is pawaPay?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;pawaPay&lt;/strong&gt; is a &lt;strong&gt;mobile money payment platform&lt;/strong&gt; designed to empower businesses to seamlessly send and receive payments across various African countries using mobile wallets. With over &lt;strong&gt;300 million mobile money users&lt;/strong&gt; in Africa, pawaPay taps into this expansive ecosystem, providing rapid settlements, eliminating chargebacks, and ensuring secure transaction processing—all accessible from a unified dashboard. pawaPay offers integrations with all major mobile money operators, delivering a scalable and efficient solution for businesses aiming to leverage the burgeoning mobile money market in Africa.&lt;/p&gt;
&lt;h3&gt;Why Mobile Money in Africa?&lt;/h3&gt;
&lt;p&gt;Mobile money has revolutionized financial transactions in Africa, where traditional banking infrastructure is limited. It allows users to perform financial transactions using their mobile phones, without needing a bank account. This has significantly increased financial inclusion and has become the preferred method of payment for many Africans. By integrating mobile money into your applications, you open your business to a vast and growing market.&lt;/p&gt;
&lt;h2&gt;Introducing the pawaPay Payment SDK&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;pawaPay Payment SDK&lt;/strong&gt; is a secure and straightforward way to integrate pawaPay's mobile money payment services into PHP applications. By handling the complexities of direct API calls and providing a set of easy-to-implement methods, this SDK allows developers to efficiently process payments without needing deep expertise in payment systems.&lt;/p&gt;
&lt;h3&gt;Key Benefits&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Simplified Integration&lt;/strong&gt;: Reduce development time with an SDK that abstracts complex API interactions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enhanced Security&lt;/strong&gt;: Leverage built-in security features adhering to the latest standards.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Comprehensive Functionality&lt;/strong&gt;: Access a wide range of payment operations through simple methods.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Key Features of the pawaPay Payment SDK&lt;/h2&gt;
&lt;h3&gt;Easy to Use&lt;/h3&gt;
&lt;p&gt;With comprehensive documentation and ready-to-use code snippets, integrating the pawaPay SDK into your existing PHP projects is straightforward. The SDK reduces the complexity involved in coding for mobile money payment systems, allowing you to focus on other critical aspects of your application.&lt;/p&gt;
&lt;h3&gt;Security at Its Core&lt;/h3&gt;
&lt;p&gt;Built with security as a priority, the SDK adheres to the latest standards in data encryption, validations, and other security protocols to ensure that all transactions are protected against fraud and breaches.&lt;/p&gt;
&lt;h3&gt;Comprehensive Payment Functions&lt;/h3&gt;
&lt;p&gt;Whether you're looking to &lt;strong&gt;initiate deposits&lt;/strong&gt;, &lt;strong&gt;manage payouts&lt;/strong&gt;, &lt;strong&gt;process refunds&lt;/strong&gt;, or &lt;strong&gt;check transaction statuses&lt;/strong&gt;, the SDK covers all bases, providing you with the tools you need to manage various mobile money payment operations efficiently.&lt;/p&gt;
&lt;h3&gt;Cross-Network Compatibility&lt;/h3&gt;
&lt;p&gt;The SDK supports integration with all major mobile money operators in Africa, ensuring that your application can process payments regardless of the user's mobile money provider.&lt;/p&gt;
&lt;h2&gt;System Requirements&lt;/h2&gt;
&lt;p&gt;Before you begin, ensure that your environment meets the following system requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PHP Version&lt;/strong&gt;: 8.0 or higher&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Composer&lt;/strong&gt;: PHP dependency manager installed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;cURL&lt;/strong&gt;: Required for making HTTP requests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenSSL&lt;/strong&gt;: For secure HTTPS connections&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JSON Extension&lt;/strong&gt;: Enabled in PHP for working with JSON data&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSL Certification&lt;/strong&gt;: A valid SSL certificate on your server&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web Server&lt;/strong&gt;: Apache 2.4, LiteSpeed V8.1, or any modern server capable of running PHP&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Installation and Setup&lt;/h2&gt;
&lt;h3&gt;Step 1: Install the SDK via Composer&lt;/h3&gt;
&lt;p&gt;Begin by installing the SDK using Composer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;composer require katorymnd/pawa-pay-integration
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 2: Configure Your API Keys&lt;/h3&gt;
&lt;p&gt;After installation, you need to configure your API keys provided by pawaPay. You can set them in your application's environment variables.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;require_once 'vendor/autoload.php';

use Dotenv\Dotenv;
use Katorymnd\PawaPayIntegration\Api\ApiClient;
use Katorymnd\PawaPayIntegration\Utils\Helpers;
use Katorymnd\PawaPayIntegration\Utils\Validator;
use Katorymnd\PawaPayIntegration\Utils\FailureCodeHelper;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Whoops\Run;
use Whoops\Handler\PrettyPageHandler;

// Initialize Whoops error handler for development
$whoops = new Run();
$whoops-&amp;gt;pushHandler(new PrettyPageHandler());
$whoops-&amp;gt;register();

// Load the environment variables from the .env file
$dotenv = Dotenv::createImmutable(__DIR__ . '/../');
$dotenv-&amp;gt;load();


// Set the environment and SSL verification based on the production status
$environment = getenv('ENVIRONMENT') ?: 'sandbox'; // Default to sandbox if not specified
$sslVerify = $environment === 'production';  // SSL verification true in production

// Dynamically construct the API token key
$apiTokenKey = 'PAWAPAY_' . strtoupper($environment) . '_API_TOKEN';

// Get the API token based on the environment
$apiToken = $_ENV[$apiTokenKey] ?? null;


if (!$apiToken) {
    echo json_encode([
        'success' =&amp;gt; false,
        'errorMessage' =&amp;gt; 'API token not found for the selected environment.'
    ]);
    exit;
}

// Initialize Monolog for logging
$log = new Logger('pawaPayLogger');
$log-&amp;gt;pushHandler(new StreamHandler(__DIR__ . '/../logs/payment_success.log', \Monolog\Level::Info));
$log-&amp;gt;pushHandler(new StreamHandler(__DIR__ . '/../logs/payment_failed.log', \Monolog\Level::Error));

// Create a new instance of the API client with SSL verification control
$pawaPayClient = new ApiClient($apiToken, $environment, $sslVerify);

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Replace &lt;code&gt;'YOUR_PAWAPAY_API_KEY'&lt;/code&gt; with your actual API key provided by pawaPay in your '.env file.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Using the SDK&lt;/h2&gt;
&lt;h3&gt;Initiating a Payment&lt;/h3&gt;
&lt;p&gt;To initiate a payment, you can use the &lt;code&gt;initiateDeposit&lt;/code&gt; method. Here's how:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;



// Initialize the API client
$pawaPayClient = new ApiClient($apiToken, $environment, $sslVerify);

// Generate a unique transaction ID (UUID v4)
$transactionId = Helpers::generateUniqueId();

// Define payment details
$amount = '1000'; // Payment amount in KES (Kenyan Shilling)
$currency = 'KES'; // Currency code
$correspondent = 'MPESA_KEN'; // Correspondent ID for the MPESA network
$recipientMsisdn = '254712345678'; // Recipient's phone number

// Custom transaction description
$description = 'Payment for invoice -1234';

// Optional metadata for the transaction
$metadata = [
    'order_id' =&amp;gt; '1234',
    'customer_id' =&amp;gt; '5678',
];

// Step 1: Validate the amount
$validatedAmount = Validator::symfonyValidateAmount($amount);

// Step 2: Validate the description
$validatedDescription = Validator::validateStatementDescription($description);

// Step 3: Validate metadata if provided
if (!empty($metadata)) {
    Validator::validateMetadataItemCount($metadata);
}

// Step 4: Initiate the payment
$response = $pawaPayClient-&amp;gt;initiatePayment(
    $transactionId,
    $validatedAmount,
    $currency,
    $correspondent,
    $recipientMsisdn,
    $validatedDescription,
    $metadata
);

// Step 5: Check if the response is successful
if ($response['status'] === 200) {
    // Payment was initiated successfully
    $log-&amp;gt;info('Payment initiated successfully', [
        'transactionId' =&amp;gt; $transactionId,
        'response' =&amp;gt; $response['response'],
    ]);
    echo &quot;Payment initiation successful. Transaction ID: $transactionId&quot;;
} else {
    // Payment initiation failed; handle the error
    $log-&amp;gt;error('Payment initiation failed', [
        'transactionId' =&amp;gt; $transactionId,
        'error' =&amp;gt; $response-&amp;gt;getErrorMessage(),
    ]);
    echo &quot;Payment initiation failed. Error: &quot; . $response-&amp;gt;getErrorMessage();
}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;In this example, we initiate a payment of 1000 KES to a recipient on the MPESA network. We check if the response is successful and handle it accordingly.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Checking Transaction Status&lt;/h3&gt;
&lt;p&gt;To check the status of a transaction:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;
 // Proceed to check the transaction status
        $statusResponse = $pawaPayClient-&amp;gt;checkTransactionStatus($transactionId, 'deposit');

        if ($statusResponse['status'] === 200) {
            $depositInfo = $statusResponse['response'][0]; // Get the deposit info
            $depositStatus = $depositInfo['status'];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Replace &lt;code&gt;'TRANSACTION_ID_RECEIVED'&lt;/code&gt; with the transaction ID you received when initiating the payment.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Handling Errors&lt;/h3&gt;
&lt;p&gt;The SDK is designed to handle errors gracefully. Here's how you might handle exceptions:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;try {
     $response = $pawaPayClient-&amp;gt;initiateDeposit(
        $depositId,
        $validatedAmount,
        $currency,
        $mno,
        $payerMsisdn,
        $validatedDescription,
        $metadata
    );

    // Process response
} catch (Exception $e) {
    // Log error details
    error_log('Payment initiation error: ' . $e-&amp;gt;getMessage());
    // Inform the user
    echo &quot;An error occurred while processing your payment. Please try again.&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Using try-catch blocks allows you to catch exceptions thrown by the SDK and handle them appropriately.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Best Practices&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Secure API Keys&lt;/strong&gt;: Never expose your API keys in your code repositories. Use environment variables or secure configuration files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validate User Input&lt;/strong&gt;: Always validate and sanitize user input to prevent injection attacks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Error Logging&lt;/strong&gt;: Implement robust error logging to aid in troubleshooting.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transaction Monitoring&lt;/strong&gt;: Regularly monitor transactions to detect any suspicious activity.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Use Cases&lt;/h2&gt;
&lt;h3&gt;E-commerce Platforms&lt;/h3&gt;
&lt;p&gt;Integrate seamless payment solutions that cater to both sellers and buyers. With pawaPay, customers can pay using their preferred mobile money provider, increasing conversion rates.&lt;/p&gt;
&lt;h3&gt;Marketplaces&lt;/h3&gt;
&lt;p&gt;Facilitate secure and reliable transactions between multiple parties with ease. The SDK supports splitting payments and managing payouts to different vendors.&lt;/p&gt;
&lt;h3&gt;Donation Platforms&lt;/h3&gt;
&lt;p&gt;Allow donors to contribute easily via mobile money, broadening your reach to users without traditional banking services.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;pawaPay Payment SDK&lt;/strong&gt; for PHP simplifies the integration of mobile money payment solutions, enabling businesses to tap into the vast African market easily. By abstracting the complexities of payment processing and providing robust security features, the SDK allows developers to focus on building great applications while ensuring seamless and secure transactions.&lt;/p&gt;
&lt;p&gt;Whether you're an e-commerce platform, a subscription service, or a marketplace, integrating pawaPay can significantly enhance your payment processing capabilities, reduce development time, and provide a better user experience.&lt;/p&gt;
&lt;h2&gt;Get Started Today&lt;/h2&gt;
&lt;p&gt;Visit the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/katorymnd/pawa-pay-integration&quot;&gt;pawaPay Payment SDK GitHub Repository&lt;/a&gt; to access the SDK, documentation, and examples. Start integrating and unlock new opportunities in the African mobile money ecosystem.&lt;/p&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://pawapay.io/&quot;&gt;pawaPay Official Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.pawapay.io/&quot;&gt;pawaPay API Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/katorymnd/pawa-pay-integration&quot;&gt;pawaPay Payment SDK on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt; &lt;/p&gt;
&lt;hr&gt;
</description>
<category>Coding</category>
<guid isPermaLink="true">https://coderlegion.com/454/pawapay-sdk-offers-secure-user-friendly-mobile-money-integration-ideal-african-commerce</guid>
<pubDate>Mon, 25 Nov 2024 22:51:21 +0000</pubDate>
</item>
</channel>
</rss>