Skip to content

NPCU → SenteRail Migration Guide

SenteRail (legal entity SenteRail Technologies Company Limited, formerly trading as NPCU — National Payments Corporation of Uganda) is renaming the brand and the technical identifiers that go with it. This guide is the single reference for integrators upgrading from NPCU-prefixed identifiers to SenteRail-prefixed identifiers.

Hard cutover

This is a hard cutover, not a soft deprecation. On the cutover day:

  • Every live merchant API key (npcu_live_*) and test key (npcu_test_*) stops parsing instantly.
  • Every webhook verifier reading X-NPCU-Signature stops validating instantly. Merchants must redeploy verifier code keyed on X-SenteRail-Signature.
  • Every NPCU_* and VITE_NPCU_* environment variable read stops resolving — CI/CD systems must rename secrets in lockstep.

There is no compatibility window. Plan your rollout to land all four changes at the same time.

Effective date

  • Cutover day: <TBD — product to confirm>.
  • Merchant notice: a minimum of 30 days written notice is required before the cutover under standard PSP terms. The cutover date will be communicated on the merchant portal and via the merchant-of-record email on file no fewer than 30 days before it lands.

What changes

SurfaceBefore (NPCU)After (SenteRail)Action
Brand / display"NPCU", "National Payments Corporation of Uganda""SenteRail"Update merchant-facing copy.
Legal entityNPCU Ltd (or trading name)SenteRail Technologies Company LimitedUpdate procurement/finance records.
Apex domainnpcu.co.ug, *.npcu.co.ug, *.npcu.ugsenterail.com, *.senterail.comRe-point integrations; paths unchanged.
Backend env varsNPCU_*SENTERAIL_*Rename in Doppler / Vault / CI.
Frontend env varsVITE_NPCU_*VITE_SENTERAIL_*Rename in build env.
HTTP webhook headersX-NPCU-*X-SenteRail-*Redeploy verifier code.
gRPC metadatax-npcu-*x-senterail-*Internal — co-deployed by SenteRail.
API key prefixnpcu_test_* / npcu_live_*srail_test_* / srail_live_*Rotate via merchant portal.

Endpoint mapping

Paths are unchanged. Only the host changes.

SurfaceBeforeAfter
Public APIhttps://api.npcu.co.ughttps://api.senterail.com
Sandbox APIhttps://api.sandbox.npcu.co.ughttps://api.sandbox.senterail.com
Developer docshttps://docs.npcu.co.ughttps://docs.senterail.com
Hosted checkouthttps://pay.npcu.co.ug (or pay.npcu.ug)https://pay.senterail.com
Checkout SDKhttps://js.pay.npcu.co.ughttps://js.pay.senterail.com
Status pagehttps://status.npcu.co.ughttps://status.senterail.com
Operator apphttps://app.npcu.co.ughttps://app.senterail.com
Auth sandboxhttps://auth.sandbox.npcu.co.ughttps://auth.sandbox.senterail.com
Console sandboxhttps://console.sandbox.npcu.co.ughttps://console.sandbox.senterail.com

The internal operator alias ops.senterail.com is retained for back-office tooling.

Webhook header mapping

The five outbound HTTP headers on every signed webhook delivery rename in lockstep. Algorithm (HMAC-SHA256 over ${t}.${body} where t is the timestamp parsed from the signature header) is unchanged.

BeforeAfter
X-NPCU-SignatureX-SenteRail-Signature
X-NPCU-Event-IdX-SenteRail-Event-Id
X-NPCU-Delivery-IdX-SenteRail-Delivery-Id
X-NPCU-Event-TypeX-SenteRail-Event-Type
X-NPCU-Api-VersionX-SenteRail-Api-Version

Internal gRPC metadata keys also rename (x-npcu-request-id, x-npcu-correlation-id, x-npcu-partner-id, x-npcu-sacco-id, x-npcu-merchant-id, x-npcu-cell-id, x-npcu-actor-type, x-npcu-actor-id, x-npcu-actor-scopes, x-npcu-ai-mode, x-npcu-idempotency-keyx-senterail-*). These are server-to-server and ship co-deployed; no integrator action.

API key prefix mapping

BeforeAfter
npcu_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxsrail_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
npcu_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxsrail_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Existing keys must be rotated. Old npcu_* prefixes are no longer parsed by the auth middleware after the cutover.

How to rotate:

  1. Sign in to the merchant portal at https://senterail.com/sign-in/merchant.
  2. Open Developers → API keys.
  3. For each active key, click Rotate and copy the new srail_* key.
  4. Deploy the new key to every environment that previously held the legacy key (production, staging, CI, plugin settings).
  5. Revoke the legacy key once your rollout is verified.

Backend environment variable mapping

Every NPCU_* backend environment variable renames to SENTERAIL_*. The value semantics are unchanged. Renames apply to:

  • All runtime, security, session, API key, webhook secret, callback, settlement, reconciliation, ledger, SMTP, and bootstrap variables.
  • Mock provider flags (NPCU_MTN_USE_MOCKSENTERAIL_MTN_USE_MOCK, etc.).
  • Step-up authentication flags (NPCU_STEP_UP_PIISENTERAIL_STEP_UP_PII).

The exhaustive list lives in api/internal/config/config.go after the cutover; the current list is mirrored in the Configuration Reference.

Frontend environment variable mapping

BeforeAfter
VITE_NPCU_API_BASE_URLVITE_SENTERAIL_API_BASE_URL
VITE_NPCU_API_BASE_URL_SANDBOXVITE_SENTERAIL_API_BASE_URL_SANDBOX
VITE_NPCU_INTERNAL_HOSTSVITE_SENTERAIL_INTERNAL_HOSTS
VITE_NPCU_ENVVITE_SENTERAIL_ENV
VITE_NPCU_FORCE_MOCKSVITE_SENTERAIL_FORCE_MOCKS
VITE_NPCU_FLAGSVITE_SENTERAIL_FLAGS
VITE_NPCU_FLAGS_OFFVITE_SENTERAIL_FLAGS_OFF

Webhook verifier code samples

The signature scheme is unchanged: ${t}.${body} HMAC-SHA256 with the merchant-provided signing secret, base64 encoded, sent in the X-SenteRail-Signature header as t=<unix-seconds>,v1=<base64-mac>.

JavaScript / Node

js
import crypto from 'node:crypto'

export function verifySenteRailSignature(rawBody, header, secret) {
  if (!header) return false
  const parts = Object.fromEntries(header.split(',').map(p => p.trim().split('=')))
  const t = parts.t
  const v1 = parts.v1
  if (!t || !v1) return false

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${t}.${rawBody}`)
    .digest('base64')

  const a = Buffer.from(expected)
  const b = Buffer.from(v1)
  return a.length === b.length && crypto.timingSafeEqual(a, b)
}

// usage with express raw body:
//   const ok = verifySenteRailSignature(req.rawBody, req.get('X-SenteRail-Signature'), SECRET)

PHP

php
function verify_senterail_signature(string $rawBody, ?string $header, string $secret): bool {
    if (!$header) return false;
    $parts = [];
    foreach (explode(',', $header) as $kv) {
        $kv = trim($kv);
        [$k, $v] = array_pad(explode('=', $kv, 2), 2, null);
        if ($k !== null && $v !== null) {
            $parts[$k] = $v;
        }
    }
    if (empty($parts['t']) || empty($parts['v1'])) return false;

    $expected = base64_encode(hash_hmac('sha256', $parts['t'] . '.' . $rawBody, $secret, true));
    return hash_equals($expected, $parts['v1']);
}

Go

go
package webhook

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "net/http"
    "strings"
)

func VerifySenteRailSignature(rawBody []byte, r *http.Request, secret []byte) bool {
    header := r.Header.Get("X-SenteRail-Signature")
    if header == "" {
        return false
    }
    var t, v1 string
    for _, part := range strings.Split(header, ",") {
        kv := strings.SplitN(strings.TrimSpace(part), "=", 2)
        if len(kv) != 2 {
            continue
        }
        switch kv[0] {
        case "t":
            t = kv[1]
        case "v1":
            v1 = kv[1]
        }
    }
    if t == "" || v1 == "" {
        return false
    }
    mac := hmac.New(sha256.New, secret)
    mac.Write([]byte(t + "." + string(rawBody)))
    expected := base64.StdEncoding.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(v1))
}

Python

python
import base64
import hashlib
import hmac

def verify_senterail_signature(raw_body: bytes, header: str | None, secret: bytes) -> bool:
    if not header:
        return False
    parts = {}
    for kv in header.split(","):
        kv = kv.strip()
        if "=" in kv:
            k, v = kv.split("=", 1)
            parts[k] = v
    t = parts.get("t")
    v1 = parts.get("v1")
    if not t or not v1:
        return False

    mac = hmac.new(secret, f"{t}.{raw_body.decode('utf-8')}".encode("utf-8"), hashlib.sha256)
    expected = base64.b64encode(mac.digest()).decode("ascii")
    return hmac.compare_digest(expected, v1)

Plugin upgrade notes

For all commerce platform plugins (WooCommerce, WHMCS, Shopify, Wix, Magento, OpenCart, PrestaShop, Bagisto):

  • Display name, icon, and asset paths change to SenteRail brand.
  • Default API host changes to api.senterail.com and checkout host to pay.senterail.com.
  • Storage keys are preserved. Your existing settings, gateway IDs, option keys, order meta, action/filter hook names, module slugs, and config-path keys continue to work after the upgrade. You do not need to migrate stored data.
  • You must install a rotated API key. The plugin settings UI will prompt for the new srail_* key on first run after upgrade.

Per-plugin specifics:

PluginDisplay nameDefault API hostStorage / handle preserved
WooCommerce"SenteRail Pay"api.senterail.comgateway id npcu_checkout, option key woocommerce_npcu_checkout_settings, order meta _npcu_session_id, _npcu_payment_id, _npcu_idempotency_key, _npcu_webhook_event_ids, _npcu_payment_rail, action/filter hook names
WHMCS"SenteRail Pay"api.senterail.commodule slug npcuug, function names npcuug_*
Shopify"SenteRail Pay"api.senterail.comwebhook topic subs, app handle if already published
Wix"SenteRail Pay"api.senterail.comintegrationType, app ID
Magento"SenteRail Pay"api.senterail.commodule name NPCU_Checkout, config_path keys
OpenCart"SenteRail Pay"api.senterail.comfolder/class names, install hooks
PrestaShop"SenteRail Pay"api.senterail.comfolder/class names, install hooks
Bagisto"SenteRail Pay"api.senterail.comfolder/class names, install hooks

Rollout checklist

Before the cutover date:

  • [ ] Rotate at least one API key per environment and confirm the new srail_* key parses end-to-end against the sandbox.
  • [ ] Redeploy webhook verifier code reading X-SenteRail-Signature (the legacy header stops being emitted on cutover day).
  • [ ] Rename NPCU_* / VITE_NPCU_* secrets in your CI, Doppler/Vault, and runtime config.
  • [ ] Re-point any hard-coded host literals (api.npcu.co.ug, pay.npcu.*, js.pay.npcu.*) to *.senterail.com.
  • [ ] If you operate plugins, run the new plugin version against the sandbox with rotated keys before moving to production.
  • [ ] Confirm your monitoring dashboards alert on X-SenteRail-Signature failures rather than X-NPCU-Signature failures.

This rebrand has Uganda regulatory and contractual exposure that must be cleared before public flip. The items below are tracked in docs/legal/senterail-name-change-evidence.md; each remains a Sandra-flagged item until official-source evidence is captured.

Conservative-language scrub

Under SenteRail, the following claims are not made on this site until evidence is captured in docs/legal/senterail-name-change-evidence.md:

  • "regulated by Bank of Uganda" / "BoU regulated"
  • "authorized sandbox participant" / "supervised in the BoU regulatory sandbox"
  • "licensed payment provider" / "pursuing full licensing"
  • "PDPO registered" / "registered Data Controller"
  • "appointed Data Protection Officer"
  • "national" / "official"
  • Specific UMRA / FIA / NIRA / URA registration claims

When discussing regulatory engagement, SenteRail uses descriptive language:

  • "engages with the Bank of Uganda under the National Payment Systems Act 2020" (not "regulated by").
  • "aligns its data handling with the Data Protection and Privacy Act 2019" (not "PDPO registered").
  • "renders UMRA-aligned report templates from the ledger" (not "produces UMRA returns automatically").

Open verification list

Each item below is open until official-source evidence is filed:

  • URSB — company name change continuity NPCU Ltd → SenteRail Technologies Company Limited, or new incorporation with business-transfer documentation.
  • BoU — current PSP / payment-system status under new legal name; sandbox status if applicable.
  • FIA — accountable-person filing under new name.
  • PDPO / NITA-U — controller/processor registration carried over or refiled.
  • URA — TIN/taxpayer name update.
  • Bank settlement accounts — name change on each collection / disbursement account.
  • Provider contracts — MTN MoMo, Airtel Money, Mastercard (Move + Gateway), Jenga, EFRIS, identity providers — addendums or novation under the new legal name.
  • Merchant notices — the hard-cutover decision requires ≥30 days written notice to merchants of brand and legal-entity change and the API-key / webhook-header break.
  • Privacy policy, DPA, ToS, AML/KYC — re-execution under new name.
  • Trademark filing — "SenteRail" word + logo at URSB (Ugandan trademark register); "rails" usage cleared against EAC market.

Hard-cutover-specific blockers

  • Mass merchant API key rotation event — under what authority, with what notice, and on what day?
  • Use of word "rails" in product positioning — trademark conflict check in EAC market.
  • Retention of assets/NPCU-Brand-Assets/ until URSB name-change continuity evidence is captured.

Sandra Nabossa is a legal review persona, not a licensed advocate's formal opinion. Every regulatory claim above is flagged "to be verified from official source" until evidence is filed in docs/legal/senterail-name-change-evidence.md.