DEV Community

Cover image for Seamless Transition: pawaPay PHP SDK Now Supports V1 and V2 Side-by-Side
katorymnddev
katorymnddev

Posted on

Seamless Transition: pawaPay PHP SDK Now Supports V1 and V2 Side-by-Side

In the fast-moving world of fintech, upgrading APIs is often a painful, time-consuming process. Developers face tough decisions:

  • Do we fully switch to the new version and risk breaking existing integrations?
  • Or do we stay on the old version and miss out on new features?

With the latest release of the pawaPay PHP SDK (v4.4.0), we decided to break that cycle. Instead of forcing a single upgrade path, we’ve introduced dual-version support, allowing V1 and V2 to run side-by-side, instantly switchable.

Why This Matters

  1. Zero downtime – You can keep using your existing V1 or default integration while testing V2 in parallel.
  2. Gradual migration – Move specific endpoints to V2 at your own pace.
  3. One codebase, both worlds – No need to maintain separate SDK installations.

What’s New in v4.4.0

  • Dual-version support – V1 remains the default, V2 can be enabled with a simple configuration switch.
  • Hosted Payment Page integration – Works with both V1 and V2 for quick payment collection via redirect.
  • Improved configuration structure – Separate active_conf_v1.json and active_conf_v2.json for clarity.
  • Updated MNO availability – Separate JSONs for each version.
  • Refined API client – Enhanced to handle both versions with minimal code changes.

Example: Switching Between V1 and V2

 // API version switch, default v1 $apiVersion = getenv('PAWAPAY_API_VERSION') ?: 'v1'; // switch v1 or v2 
Enter fullscreen mode Exit fullscreen mode

Hosted Payment Page in Action

<?php declare(strict_types=1); header('Content-Type: application/json'); require_once __DIR__ . '/../vendor/autoload.php'; use Dotenv\Dotenv; use Katorymnd\PawaPayIntegration\Api\ApiClient; use Katorymnd\PawaPayIntegration\Utils\Helpers; use Katorymnd\PawaPayIntegration\Utils\Validator; use Monolog\Logger; use Monolog\Handler\StreamHandler; use Whoops\Run; use Whoops\Handler\PrettyPageHandler; // Dev error page $whoops = new Run(); $whoops->pushHandler(new PrettyPageHandler()); $whoops->register(); // .env $dotenv = Dotenv::createImmutable(__DIR__ . '/../'); $dotenv->load(); // Environment / token / SSL / API version $environment = getenv('ENVIRONMENT') ?: 'sandbox'; $sslVerify = ($environment === 'production'); // Choose API version: 'v1' or 'v2' $apiVersion = getenv('PAWAPAY_API_VERSION') ?: 'v1'; // Change to v1 or v2 $apiTokenKey = 'PAWAPAY_' . strtoupper($environment) . '_API_TOKEN'; $apiToken = $_ENV[$apiTokenKey] ?? null; if (!$apiToken) { echo json_encode(['success' => false, 'errorMessage' => 'API token not found for the selected environment.']); exit; } // Logging $log = new Logger('pawaPayLogger'); $log->pushHandler(new StreamHandler(__DIR__ . '/../logs/payment_success.log', \Monolog\Level::Info)); $log->pushHandler(new StreamHandler(__DIR__ . '/../logs/payment_failed.log', \Monolog\Level::Error)); // Client (version-aware) $pawaPayClient = new ApiClient($apiToken, $environment, $sslVerify, $apiVersion); // Inputs $depositId = Helpers::generateUniqueId(); $returnUrl = isset($_POST['returnUrl']) ? trim($_POST['returnUrl']) : 'https://example.com/paymentProcessed'; $statementDescription = isset($_POST['statementDescription']) ? trim($_POST['statementDescription']) : 'ProjectPayment123'; $amount = isset($_POST['amount']) ? trim($_POST['amount']) : '15000'; $currency = isset($_POST['currency']) ? strtoupper(trim($_POST['currency'])) : 'UGX'; $msisdn = isset($_POST['msisdn']) ? preg_replace('/\D/', '', trim($_POST['msisdn'])) : '256753456789'; $language = isset($_POST['language']) ? strtoupper(trim($_POST['language'])) : 'EN'; $country = isset($_POST['country']) ? strtoupper(trim($_POST['country'])) : 'UGA'; $reason = isset($_POST['reason']) ? trim($_POST['reason']) : 'Project payment'; $metadata = []; // metadata can be JSON string or array $metadataRaw = $_POST['metadata'] ?? ''; try { // Validate description (4–22) $statementDescription = Validator::validateStatementDescription($statementDescription); // Validate amount if provided if ($amount !== '') { $amount = Validator::symfonyValidateAmount($amount); } // Parse/validate metadata if ($metadataRaw !== '' && $metadataRaw !== null) { if (is_string($metadataRaw)) { $decoded = json_decode($metadataRaw, true); if (json_last_error() !== JSON_ERROR_NONE || !is_array($decoded)) { throw new InvalidArgumentException('Invalid metadata JSON.'); } $metadata = $decoded; } elseif (is_array($metadataRaw)) { $metadata = $metadataRaw; } else { throw new InvalidArgumentException('Unsupported metadata format.'); } if (!empty($metadata) && method_exists(Validator::class, 'validateMetadataItemCount')) { Validator::validateMetadataItemCount($metadata); } } // Build params once; client will adapt to v1/v2 $params = [ 'depositId' => $depositId, 'returnUrl' => $returnUrl, // V1 name; client maps to V2 'customerMessage' automatically 'statementDescription' => $statementDescription, // V1 fields: 'amount' => $amount, 'msisdn' => $msisdn, // Common 'language' => $language, 'country' => $country, 'reason' => $reason, 'metadata' => $metadata, ]; // If currency also provided, client will send amountDetails for V2 automatically if ($currency !== '') { $params['currency'] = $currency; } // Call auto (routes to v1 or v2) $resp = $pawaPayClient->createPaymentPageSessionAuto($params); // << UPDATED if (in_array($resp['status'], [200, 201], true)) { $redirectUrl = $resp['response']['redirectUrl'] ?? null; if ($redirectUrl) { $log->info('Payment Page session created', [ 'depositId' => $depositId, 'redirectUrl' => $redirectUrl, 'response' => $resp['response'], ]); echo json_encode([ 'success' => true, 'depositId' => $depositId, 'redirectUrl' => $redirectUrl, 'version' => $apiVersion, // helpful to surface ]); exit; } $log->error('No redirectUrl in successful response', ['depositId' => $depositId, 'response' => $resp['response']]); echo json_encode(['success' => false, 'errorMessage' => 'No redirectUrl returned by API.']); exit; } // Non-success HTTP status $log->error('Failed to create Payment Page session', ['depositId' => $depositId, 'response' => $resp]); echo json_encode([ 'success' => false, 'errorMessage' => 'Failed to create Payment Page session.', 'status' => $resp['status'] ?? null, 'raw' => $resp['response'] ?? null, 'version' => $apiVersion, ]); exit; } catch (Throwable $e) { $log->error('Error creating Payment Page session', [ 'depositId' => $depositId, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); echo json_encode(['success' => false, 'errorMessage' => $e->getMessage()]); exit; } 
Enter fullscreen mode Exit fullscreen mode

Get the SDK

The full source code and examples are available on GitHub:
🔗 pawaPay PHP SDK – GitHub Repository

Top comments (0)