NyuzaDeveloper documentation

Overview

Add Pay with Nyuza to your application with express checkout: authenticate, create a checkout, redirect the customer, and confirm payment. Nyuza creates the order, invoice and payment in a single request.

Before you begin

Start in Nyuza Console under Organization profile → API Clients. Create a client to generate your client ID and secret, then use those credentials to obtain a token. Have your organization identifier, institution, channel and payment-provider configuration ready, along with an HTTPS callback endpoint and a customer return page.

Where your code runs

ComponentResponsibility
Customer browserStarts the purchase, opens paymentUrl and displays your return page.
Your backendKeeps credentials, obtains the token, calls checkout, saves invoiceId/paymentId and verifies payment.
Nyuza checkoutHosts the customer payment experience.
Your webhook endpointReceives lifecycle updates from Nyuza and updates your purchase records.

Browser → your backend → Nyuza API. Your backend returns paymentUrl to the browser. Nyuza sends callbacks directly to your backend, even if the customer closes the browser.

Prepare your integration values

ValueWhere it comes fromHow you use it
clientId / clientSecretConsole → Organization profile → API ClientsAuthenticate from your backend.
organizationIdYour Nyuza organization configurationUse the organization UUID in the checkout path; do not substitute the API client ID or organization display code.
institution / channelYour integration configuration supplied by NyuzaSend as headers. RGI is an example institution; use the channel assigned to your integration consistently.
paymentProviderIdYour configured payment provider in NyuzaSelects the provider for invoice/payment creation.
paymentSourceYour supported integration source; the examples use WEBUse the same source when configuring return URLs.
referenceNumberGenerated by your applicationUse a purchase reference such as ORDER-DEMO-001.
request-idGenerated by your backend for the requestKeep it in diagnostic logs; it is not an authentication token or a documented idempotency key.
Callback URL / return URLRoutes implemented by your applicationBackend notification endpoint / customer-facing result page.

Choose your integration

ApproachWhen to use it
Express checkout · recommendedCreate the order, invoice and payment together, then redirect the customer to hosted checkout.
Separate stepsManage order, invoice, payment and transaction creation independently when your use case requires it.

Environment and examples

Express checkout nonproduction origin:

HTTP
https://dev.nonprod.rolengiapps.com

Endpoint paths are shown independently of the origin. Use the authentication and payment API environment URLs supplied during onboarding.

Use the environment URLs supplied during onboarding. Values in {{doubleBraces}} are placeholders; replace them with your configuration or transaction data. Example identifiers, amounts and enum values are illustrative.

NEXT STEPCreate your API credentials →

Your first test payment

Follow one purchase, ORDER-DEMO-001, from setup to confirmation. Complete this in your test environment before accepting customer payments.

Walkthrough defaults

The examples below use provisional defaults to make the walkthrough concrete. Verify the URLs, units, required fields and response mappings against your environment before using them in production.

SettingExample default
API originhttps://dev.nonprod.rolengiapps.com
Authentication URLhttps://dev.nonprod.rolengiapps.com/uhasibu/auth/token/opaque
Payment lookup URLhttps://dev.nonprod.rolengiapps.com/uhasibu/v1/api/malipo/{paymentId}
Institution / channel / sourceRGI / api / WEB
Token typeOpaque
TimestampUnix time in milliseconds, generated for each request.
Purchase amount1500 KES, treating amounts as major currency units for this example.
Purchase referenceORDER-DEMO-001
StepActionExpected result
1. Prepare credentialsCreate an API client in Console and save its client ID and secret on your backend.Credentials ready for authentication.
2. Prepare callbacksConfigure your public callback URL, headers and customer return URL in Console.Your backend can receive notifications.
3. AuthenticateSend auth.json using the authentication cURL example.An authentication token to use in API requests.
4. Create checkoutSubmit a validated checkout.json with details.referenceNumber set to ORDER-DEMO-001.The checkout result contains invoiceId, paymentId and paymentUrl.
5. Store and redirectSave both identifiers against ORDER-DEMO-001 and redirect the browser to paymentUrl.The customer reaches Nyuza checkout.
6. Complete the testFollow the test payment procedure for your configured provider.A payment outcome is available through callbacks or payment lookup.
7. Confirm and replayVerify the outcome, amount and currency, then resend the event from History.Your purchase updates correctly and fulfillment happens only once.

Keep a purchase record

JSON
{
  "purchaseReference": "ORDER-DEMO-001",
  "invoiceId": "{{invoiceId}}",
  "paymentId": "{{paymentId}}",
  "paymentUrl": "{{paymentUrl}}"
}

This is an example record for your application, not an API response. Store the purchase’s expected amount and currency alongside it so you can verify the payment outcome.

Authentication

Start in Console

Create an API client first, then exchange its client ID and secret for a token.

Open API Clients ↗

Create your API credentials

Sign in to Nyuza Console and open Organization profile → API Clients. Your organization can create as many API clients as needed, each with its own client ID and secret.

StepAction
1. Open API ClientsSelect your organization profile in the Console sidebar, then open API Clients.
2. Create a clientSelect Add Client to generate a client ID and secret.
3. Save the credentialsCopy the client ID and client secret into your backend configuration. Keep the secret secure and out of browser code.
4. Obtain a tokenSend the credentials to the authentication endpoint below. Use the resulting token for API requests.

Use separate API clients when you need independent credentials for different applications or integrations. The API Clients list provides Copy for the client ID, Copy Secret, and Delete actions.

Obtain an authentication token

Nyuza uses OAuth 2.0 with Keycloak. Client applications obtain an opaque token through the Nyuza authentication service.

POST/auth/token/opaque

Request

HTTP
Content-Type: application/json
JSON
{
  "clientId": "{{clientId}}",
  "clientSecret": "{{clientSecret}}"
}
FieldTypeDescription
clientIdstringClient identifier issued for the integration.
clientSecretstringClient credential issued for the integration.

Token request with cURL

Run this on your development machine or backend. Save your credentials as auth.json using the JSON structure above; keep that file out of source control. The URL below expands the authentication URL in the supplied test collection.

Shell
curl --request POST \
  'https://dev.nonprod.rolengiapps.com/uhasibu/auth/token/opaque' \
  --header 'Content-Type: application/json' \
  --data-binary @auth.json

Store the token returned by the authentication service on your backend and send it with its token type. Follow the expiry returned by the service; obtain a new token when needed. A new token does not require a new API client.

Authenticated requests

Send the token and its returned token type in the Authorization header. The integration reference uses Opaque as the default token type.

HTTP
Authorization: {{tokenType}} {{authToken}}

Payment API headers

HeaderValue or example
Authorization{{tokenType}} {{authToken}}
institution{{institution}} — example RGI
channel{{channel}} — use your configured integration channel.
request-idA request identifier, for example a UUID.
timestamp{{timestamp}} — generate a current request timestamp.
Content-Typeapplication/json for JSON request bodies.

Credential handling

Make authenticated requests from your application backend. Store client secrets and tokens securely and exclude them from logs. Keep request identifiers separate from authentication tokens.

Illustrative token response

Example response mapping only. The actual response may use a different envelope or field names; map the values returned by your authentication endpoint.

JSON
{
  "access_token": "example-token",
  "token_type": "Opaque",
  "expires_in": 3600
}

In this example, access_token becomes NYUZA_TOKEN and token_type becomes NYUZA_TOKEN_TYPE. expires_in is an illustrative lifetime in seconds, not a guaranteed Nyuza token lifetime.

NEXT STEPStart express checkout →

Express checkout

Save these three values

invoiceId · paymentId · paymentUrl. Save the identifiers against your purchase, then redirect the customer to paymentUrl.

Jump to the checkout result →
POST/duka-manager/v1/api/orders/{organizationId}/checkout/express

Submit your order details using your organizationId in the path. Nyuza creates a new order, creates its invoice and payment, and saves the invoice and payment references against the order.

Send the request

HeaderExample
Authorization{{tokenType}} {{authToken}}
accept*/*
institutionRGI
channel{{channel}} — use your configured integration channel.
request-id{{requestId}}
accept-languageen
timestamp{{timestamp}} — generate a current request timestamp.
Content-Typeapplication/json

The request combines order details with payment configuration. The full nested structure is available in Request reference.

FieldTypeDescription
detailsobjectOrder lines, reference number, amounts and fulfillment details. details.referenceNumber is forwarded to invoice creation.
auditobjectCustomer, member and organization context.
recommendedAmountnumberCopied to invoice metadata when supplied.
paymentProviderIdstringPayment provider identifier forwarded to invoice creation.
paymentSourcestringPayment source forwarded to invoice creation.
returnUrlstringCustomer return URL, copied to invoice metadata when nonblank.
standingOrderWalletRefIdstringStanding order wallet reference forwarded to invoice creation.
invoiceMetadataobjectAdditional invoice metadata. See Request reference for precedence rules.
paymentAllocationsarrayProvider, wallet and amount allocations forwarded to invoice creation.
organizationIdstringOrganization identifier.

Send checkout with cURL

Save your checkout JSON as checkout.json. Set NYUZA_ORGANIZATION_ID, NYUZA_TOKEN_TYPE, NYUZA_TOKEN, NYUZA_INSTITUTION, NYUZA_CHANNEL, NYUZA_REQUEST_ID and NYUZA_TIMESTAMP in your local shell. Use a fresh timestamp in the format required by your endpoint configuration.

Shell
curl --request POST \
  "https://dev.nonprod.rolengiapps.com/duka-manager/v1/api/orders/${NYUZA_ORGANIZATION_ID}/checkout/express" \
  --header "Authorization: ${NYUZA_TOKEN_TYPE} ${NYUZA_TOKEN}" \
  --header "institution: ${NYUZA_INSTITUTION}" \
  --header "channel: ${NYUZA_CHANNEL}" \
  --header "request-id: ${NYUZA_REQUEST_ID}" \
  --header "timestamp: ${NYUZA_TIMESTAMP}" \
  --header 'accept-language: en' \
  --header 'Content-Type: application/json' \
  --data-binary @checkout.json

Save the result and redirect

The checkout data object contains three fields:

JSON
{
  "invoiceId": "{{invoiceId}}",
  "paymentUrl": "{{paymentUrl}}",
  "paymentId": "{{paymentId}}"
}
FieldPurpose
invoiceIdIdentifier of the invoice created for the order. Retain it for invoice correlation.
paymentIdIdentifier of the payment. Use it for payment lookup and reconciliation.
paymentUrlHosted checkout URL. Redirect the customer to this exact returned value.

Save invoiceId and paymentId against your purchase, then redirect the customer to the exact paymentUrl returned. This URL identifies the hosted checkout for the invoice. A successful response means checkout has been initiated; confirm the payment before fulfillment.

Starter checkout body

Save this illustrative starter body as checkout.json and replace the identifier placeholders. It is a compact starting point, not a validated minimum schema; your organization may require additional order or audit fields.

JSON
{
  "details": {
    "referenceNumber": "ORDER-DEMO-001",
    "description": "Demo service purchase",
    "saleOrderLines": [
      {
        "productSaleOrders": [
          {
            "productName": "Demo service",
            "productType": "SERVICE_PRODUCT",
            "quantity": 1,
            "price": 1500,
            "total": 1500,
            "fulfillmentType": "SALE"
          }
        ],
        "total": 1500
      }
    ],
    "total": 1500
  },
  "organizationId": "{{organizationId}}",
  "paymentProviderId": "{{paymentProviderId}}",
  "paymentSource": "WEB",
  "returnUrl": "https://merchant.example/payment/result"
}
NEXT STEPConfirm the payment →

Confirm payment

Confirm before fulfillment

A browser return is not proof of payment. Verify the outcome, amount and currency using a callback or payment lookup.

Process payment updates →

Use webhook notifications to follow payment lifecycle updates. Retrieve the payment when you need to check or reconcile its current outcome.

GET/uhasibu/v1/api/malipo/{paymentId}

Use the paymentId returned by express checkout. Match the result to your purchase and verify the payment outcome, amount and currency before fulfillment. A customer reaching your return page does not confirm payment; they may also close checkout before returning.

Recover an interrupted checkout

Keep your purchase reference and request identifier. If checkout times out or fails after processing begins, reconcile any existing order, invoice or payment before creating another checkout. Records may already have been created.

Find payment and transaction records

GET/uhasibu/v1/api/malipo/checkout?page=0&length=20
GET/uhasibu/v1/api/malipo/transactions?page=0&length=20

These endpoints list payments and transactions respectively. Match records using the relevant payment or correlation identifier. A list response alone is not confirmation that the current payment succeeded.

Troubleshoot the first payment

SymptomWhat to check
Authentication rejectedCheck the environment, client ID, secret and Authorization token type. Obtain a new token if the current one has expired.
Checkout request rejectedRead the returned error details; check organization, configured identifiers, required fields and JSON types before correcting the request.
Checkout times outRecord the purchase reference and request ID. Inspect existing payment records and Console history before starting another checkout; do not assume a timeout means nothing was created.
No callback reaches your appCheck that webhooks are enabled, the public HTTPS URL is reachable and the configured headers are accepted by your route. Inspect History for the delivery result.
Callback delivered but purchase unchangedInspect your handler logs and reference matching. Resolve the processing issue, then resend the event.
Duplicate fulfillmentPersist processing state against event references and make business actions safe to repeat. Preserve later lifecycle updates for the same payment.

Illustrative payment mapping

The following is an application-side example, not a confirmed Nyuza response schema. Map the actual payment response into your own record before applying business rules.

JSON
{
  "purchaseReference": "ORDER-DEMO-001",
  "paymentId": "{{paymentId}}",
  "status": "succeeded",
  "amount": 1500,
  "currency": "KES"
}

For your application, a simple status model can use pending, succeeded and failed. These are local example labels, not documented API enum values. Map the provider’s actual statuses explicitly; keep unknown outcomes unresolved until verified.

NEXT STEPConfigure webhooks →

Webhooks

Configure once. Receive lifecycle updates.

Console → Settings → Tools & Integrations → Webhooks. Duplicate deliveries retain the same reference keys.

Open Webhooks ↗

Configure and test your callback before starting the first checkout. This ensures your backend can receive the resulting lifecycle events.

Subscribe to lifecycle events so your backend receives payment updates independently of the customer’s browser. Configure a callback endpoint, process notifications, and use delivery history to investigate or resend events.

Configure in Console

Open console.nyuza.com and navigate to Settings → Tools & Integrations → Webhooks. On the Webhooks & Integrations page, open the Configuration tab.

StepAction
1. Enable the webhookSelect Enable payment status webhook.
2. Set the callback URLEnter the HTTPS endpoint in your application that will receive webhook POST requests.
3. Add callback headersUse Add Header to supply any authentication or routing headers required by your endpoint, such as X-API-KEY. These headers are included with each webhook request.
4. Set payment return URLsUse Add URL to configure a customer return URL for each applicable source and channel. Match these values to your integration configuration.
5. SaveSelect Save Settings to apply the configuration.

Understand the URLs

URLPurpose
paymentUrlReturned by express checkout. Redirect the customer here to pay.
returnUrlYour customer-facing page after checkout. Set it in the checkout request or configure return URLs by source and channel in Console.
Callback URLYour backend endpoint for webhook POST requests. Configure it in Console or through merchant settings.

Process lifecycle events

Validate your configured authentication headers and match the notification to the stored payment and purchase. Use the reported payment outcome to update your records, and verify amount and currency before fulfillment.

Duplicate deliveries, including manual resends, retain the same reference keys. Record successful processing against those references and acknowledge duplicates without repeating fulfillment or other business actions. If processing previously failed, allow the resent event to complete the work.

A payment can receive multiple lifecycle updates. Deduplicate the same event while allowing later updates for that payment; paymentId alone is not sufficient to distinguish them.

Review history and resend

Open the History tab under Settings → Tools & Integrations → Webhooks. Each row shows the callback time, payment ID, destination URL, HTTP status and delivery success. Filter by a preset period or date range, then select Search. Use the page controls to browse results.

To redeliver an event, locate its row, check the payment ID and destination, and select Resend. The resent event keeps the same reference keys and does not create a new payment.

HTTP status and Success describe callback delivery. A 200 response confirms that your endpoint responded successfully; determine payment success from the event data or payment lookup.

Configure through the API

POST/uhasibu/v1/api/malipo/settings/merchant

Configure your application callback destination and customer return URL using the shared payment API headers.

JSON
{
  "webHookSettings": {
    "enabled": true,
    "paymentStatusCallbackUrl": "{{callbackUrl}}",
    "notifyAnkaraStatus": false,
    "headers": {
      "X-Client-Key": "{{callbackKey}}"
    }
  },
  "paymentReturnUrlSettings": [
    {
      "paymentSource": "WEB",
      "channel": "{{channel}}",
      "returnUrl": "{{returnUrl}}"
    }
  ]
}

The example enables external callbacks and disables Ankara notification routing. The headers object sets custom headers for callback requests; X-Client-Key is an example.

GET/uhasibu/v1/api/malipo/settings/merchant

Read back the settings after configuration and verify the callback URL, headers and return URL.

Test callbacks during local development

Implement a POST route on your backend, for example /webhooks/nyuza. Make it reachable through a public HTTPS development endpoint or tunnel; Nyuza cannot reach a localhost address on your laptop. Configure that public URL and your chosen authentication header in Console.

Send a local test request to verify your route accepts JSON and validates the header. Then complete a test checkout using the test procedure supplied for your configured provider. Inspect Console → Webhooks → History and your application logs. Select Resend to verify that the same event does not repeat fulfillment.

A delivery test checks connectivity. Verify business processing separately: the callback must match the purchase, update the payment outcome correctly and handle repeated deliveries.

Illustrative event mapping

Use this example to structure your handler’s internal event record. It does not define the Nyuza webhook payload or its reference-key names.

JSON
{
  "eventReference": "example-event-001",
  "purchaseReference": "ORDER-DEMO-001",
  "paymentId": "{{paymentId}}",
  "eventType": "payment.updated",
  "status": "succeeded",
  "amount": 1500,
  "currency": "KES"
}

Map the webhook’s actual duplicate reference keys to your internal eventReference. A resend maps to the same eventReference; a distinct lifecycle event must remain distinguishable. The example eventType and status are illustrative labels.

Separate-step integration

Choose this approach when you need control over each creation stage: create an order, create its invoice, create a payment, then initiate a payment transaction. The payment operations below follow order and invoice creation. Use the authenticated headers from Authentication.

Create a payment

POST/uhasibu/v1/api/malipo
JSON
{
  "externalReference": "{{externalReference}}",
  "amount": {"amount": 1500, "currency": "KES"},
  "paymentSource": "WEB",
  "description": "{{paymentDescription}}",
  "requestPayment": false,
  "phoneNumber": "{{paymentPhoneNumber}}",
  "paymentTerm": "PREPAID",
  "allowSplitPayments": true
}

Retain the payment identifier for the transaction request and subsequent lookup.

Create a transaction

POST/uhasibu/v1/api/malipo/transactions
JSON
{
  "paymentRefId": "{{paymentId}}",
  "paymentProviderType": "MPESA",
  "paymentSource": "WEB",
  "description": "{{paymentDescription}}",
  "amount": {"amount": 1500, "currency": "KES"},
  "externalTransactionId": "{{externalTransactionId}}",
  "correlationId": "{{correlationId}}",
  "customerRefId": "{{customerRefId}}",
  "requestPayment": true,
  "phoneNumber": "{{paymentPhoneNumber}}"
}

paymentRefId links the transaction to the payment record. Store the payment, transaction, external reference and correlation identifiers separately. The example uses MPESA, WEB, PREPAID and KES.

Request reference

Use this reference for the full express checkout structure and optional invoice metadata behavior. Include the fields needed for your use case and replace the example values with transaction data.

The full body is a field reference, not a minimum-required request. Required fields depend on request validation and your configuration. Use a validated request for your organization when testing.

Understand the amount fields

FieldRole
quantity / priceDescribe the product quantity and price.
total / discountAppear at product, order-line and order levels. Keep values consistent with your purchase and the API’s calculation rules.
recommendedAmountPassed into invoice metadata; it does not itself confirm a charge.
minimumOrderAmountContributes to the minimumAmount metadata calculation described below.
paymentAllocations[].amountContains an allocation amount and currency. These allocations are forwarded to invoice creation.

Use the currency and amount units configured for your payment integration. Do not infer amount units or charge calculation rules from the illustrative values in the field reference.

Invoice metadata

Nyuza copies invoiceMetadata, then applies the dedicated request values and the order-derived minimum. Other metadata keys are preserved.

Metadata keyValue usedPrecedence
recommendedAmountThe request recommendedAmount, when non-null.Replaces invoiceMetadata.recommendedAmount when supplied. Otherwise the existing metadata value is retained.
returnUrlThe request returnUrl, when non-null and nonblank.Replaces invoiceMetadata.returnUrl when supplied. Otherwise the existing metadata value is retained.
minimumAmountThe sum of positive minimumOrderAmount values across the saved order’s product entries.Replaces invoiceMetadata.minimumAmount when the sum is positive. Otherwise the existing metadata value is retained.

Minimum amount

Each positive minimumOrderAmount contributes once to the sum. Missing, zero and negative values are excluded. Quantity is not multiplied into this calculation.

Product entryQuantityminimumOrderAmountContribution
A3100100
B15050
C200
Total——150

This example produces minimumAmount = 150. The calculation uses the saved order’s product entries.

Full request structure

JSON
{
  "details": {
    "saleOrderLines": [
      {
        "productSaleOrders": [
          {
            "productVariantId": "string",
            "productName": "Demo service",
            "productType": "SERVICE_PRODUCT",
            "variantName": "string",
            "quantity": 1,
            "price": 1500,
            "discount": 0,
            "total": 1500,
            "minimumOrderAmount": 0,
            "fulfillmentType": "SALE",
            "inventoryUnitId": "string",
            "serviceAssetAssignmentId": "string",
            "billable": true,
            "costed": true
          }
        ],
        "discount": 0,
        "total": 1500
      }
    ],
    "serviceAssetAssignmentId": "string",
    "referenceNumber": "ORDER-DEMO-001",
    "discount": 0,
    "total": 1500,
    "favorite": true,
    "description": "Demo purchase",
    "fulfillmentMethod": "PICKUP",
    "deliveryAddress": "string",
    "preferredRiderPhone": "string",
    "deliveryEstimate": 0,
    "fulfillmentAddress": "string",
    "fulfillmentContactName": "string",
    "fulfillmentContactPhone": "string",
    "fulfillmentInstructions": "string",
    "requestedFulfillmentAt": "string",
    "fulfillmentFee": 0
  },
  "audit": {
    "customers": [
      "string"
    ],
    "agentMemberId": "string",
    "creatorMemberId": "string",
    "organizationId": "string",
    "stageId": "string",
    "organizationBranchId": "string"
  },
  "recommendedAmount": 1500,
  "paymentProviderId": "paymentProviderId",
  "paymentSource": "WEB",
  "returnUrl": "https://merchant.example/payment/result",
  "standingOrderWalletRefId": "string",
  "invoiceMetadata": {
    "additionalProp1": {},
    "additionalProp2": {},
    "additionalProp3": {}
  },
  "paymentAllocations": [
    {
      "standingOrderWalletRefId": "string",
      "paymentProviderId": "string",
      "amount": {
        "amount": 1500,
        "currency": "KES"
      }
    }
  ],
  "organizationId": "string"
}