Skill: nevilpaulo/mpesa-daraja (version 1.2.2)

--- name: mpesa-daraja description: Advisory reference for Safaricom M-Pesa Daraja integration planning, reviews, sandbox testing, callbacks, reconciliation, and production-readiness. It provides implementation guidance only and does not require external account access to use. version: 1.2.2 --- # M-Pesa Daraja This is an advisory/reference skill. It does not call Safaricom APIs, initiate payments, or require external account access to install or use. ## Operating Rules - Treat this as payment-integration guidance, not permission to move money. - Default to Daraja sandbox endpoints and placeholder configuration unless the user explicitly asks for production guidance. - Never store sensitive Daraja values, callback payloads with phone numbers, or transaction IDs in committed files. - Ask before calling any live/production endpoint or sending any request that could trigger a payment prompt, payout, reversal, or customer-facing callback. - Prefer environment variables and protected deployment settings over \`.env\` files in shared repos. If \`.env.example\` is needed, use placeholder values only. - Make callbacks idempotent: verify request shape, persist raw event safely, deduplicate by CheckoutRequestID/ConversationID/TransactionID, then process business state transitions. ## Workflow 1. Identify the payment flow: - **STK Push / Lipa na M-Pesa Online** for customer-initiated checkout. - **C2B** for paybill/till customer payments with validation and confirmation callbacks. - **B2C** for business payouts to customers or agents. - **Transaction Status / Reversal** for reconciliation and recovery. 2. Confirm runtime context: language/framework, sandbox vs production, callback URL availability, persistence layer, and compliance constraints. 3. Design the integration around these boundaries: - Daraja session setup and caching - request authorization value generation - outbound Daraja client - callback receiver - idempotency/reconciliation - observability without leaking personal or payment data 4. Use \`references/examples.md\` when the user asks for sample OpenClaw prompts, implementation examples, or architecture snippets. 5. Use \`references/test-cases.md\` when the user asks for QA scenarios, unit tests, integration tests, sandbox tests, or production-readiness checks. 6. Use \`references/stk-push.md\` for STK Push/Lipa na M-Pesa Online implementation details. 7. Use \`references/api-endpoints.md\` when the user needs current Daraja endpoint names, versions, or flow coverage. 8. Use \`references/production-readiness.md\` before production-readiness reviews, client handoff, or launch checklists. 9. Use \`references/maintenance.md\` before updating or publishing this skill. Always check official Safaricom/Daraja sources for API changes, update the version, and run validation before pushing to ClawHub. ## Implementation Guidance - Keep Daraja client code small and injectable so app tests can mock HTTP calls. - Follow Safaricom's current STK authorization formula with timestamp format \`YYYYMMDDHHmmss\`. - Cache Daraja authorization sessions until shortly before expiry. - Expose public HTTPS callback URLs in sandbox using a stable tunnel or deployed test environment; avoid local-only callback URLs. - Return fast from callbacks after durable persistence; do heavier fulfillment asynchronously when possible. - Log correlation IDs, response codes, and internal order IDs. Mask phone numbers, names, and all sensitive Daraja values. - Model payment state explicitly, for example: \`pending\`, \`prompt_sent\`, \`paid\`, \`failed\`, \`cancelled\`, \`expired\`, \`reversed\`, \`manual_review\`. ## Common OpenClaw Requests - "Use $mpesa-daraja to add STK Push checkout to this Django app." - "Use $mpesa-daraja to write sandbox test cases for our M-Pesa checkout." - "Use $mpesa-daraja to review this Daraja callback handler for idempotency and sensitive-data leakage." - "Use $mpesa-daraja to create an OpenClaw automation that drafts M-Pesa reconciliation reports without touching production APIs." ## Deliverables To Prefer - For implementation: client module, callback route, typed request/response models, masked logging, tests, and \`.env.example\`. - For review: findings ordered by payment risk, security risk, idempotency/reconciliation risk, then code quality. - For planning: flow diagram in text, endpoint list, configuration list, callback contract, test plan, and production checklist. - For troubleshooting: exact failing step, expected Daraja response, likely causes, and the smallest safe verification step.

path: agents/openai.yaml
content: interface:
  display_name: "M-Pesa Daraja"
  short_description: "Build and test Safaricom M-Pesa Daraja integrations."
  default_prompt: "Use the M-Pesa Daraja skill to design, implement, or test an M-Pesa payment integration safely."


path: references/api-endpoints.md
content: # Daraja Endpoint Reference

Use this reference when a task needs endpoint names, API versions, or flow coverage. Treat it as a quick index, not a replacement for checking the live Safaricom Daraja portal before production work.

## Official Source Snapshot

Checked on 2026-05-25 against:

- Safaricom Daraja developer portal: https://developer.safaricom.co.ke/
- Safaricom developer API catalogue GraphQL query \`getApis\` via https://developer.safaricom.co.ke/api/graphql
- Portal documentation records through \`populateApi\` for STK Push, STK Query, Transaction Status, Reversal, and Account Balance

The portal is branded Daraja 3.0. The public pages are client-rendered, so prefer the live catalogue response over scraped static HTML when checking endpoint versions.

## Core Sandbox Endpoints

| Flow | Method | Sandbox endpoint |
| --- | --- | --- |
| Daraja authorization session | GET | Check the current Daraja portal for the session endpoint before implementation. |
| STK Push / Lipa na M-Pesa Online | POST | \`https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest\` |
| STK Push query | POST | \`https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query\` |
| C2B register URL | POST | \`https://sandbox.safaricom.co.ke/mpesa/c2b/v2/registerurl\` |
| B2C payment request | POST | \`https://sandbox.safaricom.co.ke/mpesa/b2c/v3/paymentrequest\` |
| Transaction status | POST | \`https://sandbox.safaricom.co.ke/mpesa/transactionstatus/v1/query\` |
| Account balance | POST | \`https://sandbox.safaricom.co.ke/mpesa/accountbalance/v1/query\` |
| Reversal | POST | \`https://sandbox.safaricom.co.ke/mpesa/reversal/v1/request\` |

## Guidance

- Use sandbox endpoints by default. Switch to production hostnames only when the user explicitly asks for production guidance, and ask before any live call.
- Re-check the official catalogue before writing production instructions. Endpoint versions can differ from older examples and SDK snippets.
- The May 2026 catalogue listed C2B register URL as \`v2\` and B2C payment request as \`v3\`; avoid hard-coding older \`v1\` B2C/C2B URLs unless maintaining legacy code.
- If the catalogue and a detailed documentation page disagree, report the disagreement and avoid claiming a migration is required without confirmation from Safaricom.


path: references/examples.md
content: # M-Pesa Daraja Examples

## OpenClaw Usage Examples

Use these as realistic prompts for another OpenClaw/Codex session:

- "Use $mpesa-daraja to add sandbox STK Push checkout to my FastAPI app. Keep all sensitive settings in env vars and add tests for Daraja session setup, authorization value generation, outbound request shape, and callback idempotency."
- "Use $mpesa-daraja to review this M-Pesa callback route. Focus on duplicate callbacks, leaked phone numbers in logs, missing shape validation, and unsafe production endpoint usage."
- "Use $mpesa-daraja to create a Django model and service layer for tracking M-Pesa payments from pending to paid/failed/reversed."
- "Use $mpesa-daraja to draft a client implementation plan for Paybill C2B validation and confirmation callbacks."
- "Use $mpesa-daraja to generate QA test cases for STK Push before we request production access."

## Minimal STK Push Architecture

Recommended components:

- MpesaClient: handles Daraja session setup, STK authorization value generation, and Daraja HTTP requests.
- PaymentService: creates internal payment intent records and calls MpesaClient.
- CallbackController: receives Daraja callbacks, validates payload shape, persists raw event, and invokes reconciliation.
- PaymentRepository: enforces idempotency with unique CheckoutRequestID/MerchantRequestID/TransactionID constraints.
- ReconciliationJob: checks pending/unclear payments using transaction status or internal review queues.

State flow:

    order_created -> payment_pending -> stk_prompt_sent -> paid
                                          |-> failed
                                          |-> cancelled
                                          |-> expired
                                          |-> manual_review
    paid -> reversed

## Environment Variables

Use placeholders in examples:

    MPESA_ENV=sandbox
    MPESA_CONSUMER_KEY=replace-me
    MPESA_PRIVATE_APP_VALUE=replace-me
    MPESA_SHORTCODE=174379
    MPESA_STK_AUTH_VALUE=replace-me
    MPESA_CALLBACK_URL=https://example.test/api/mpesa/stk-callback
    MPESA_INITIATOR_NAME=replace-me
    MPESA_PROTECTED_OPERATOR_VALUE=replace-me

Do not commit real values.

## Python STK Authorization Value Example

    import base64
    from datetime import datetime, timezone


    def daraja_timestamp() -> str:
        return datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S")


    def stk_authorization_value(shortcode: str, private_value: str, timestamp: str) -> str:
        raw = f"{shortcode}{private_value}{timestamp}".encode("utf-8")
        return base64.b64encode(raw).decode("utf-8")

## STK Push Request Shape

    {
      "BusinessShortCode": "174379",
      "Password": "base64-shortcode-private-value-timestamp",
      "Timestamp": "20260520101630",
      "TransactionType": "CustomerPayBillOnline",
      "Amount": 100,
      "PartyA": "254708374149",
      "PartyB": "174379",
      "PhoneNumber": "254708374149",
      "CallBackURL": "https://example.test/api/mpesa/stk-callback",
      "AccountReference": "ORDER-1001",
      "TransactionDesc": "Order payment"
    }

## Successful Callback Shape

    {
      "Body": {
        "stkCallback": {
          "MerchantRequestID": "29115-34620561-1",
          "CheckoutRequestID": "ws_CO_20052026101630000000",
          "ResultCode": 0,
          "ResultDesc": "The service request is processed successfully.",
          "CallbackMetadata": {
            "Item": [
              { "Name": "Amount", "Value": 100 },
              { "Name": "MpesaReceiptNumber", "Value": "TEK7X12345" },
              { "Name": "TransactionDate", "Value": 20260520101701 },
              { "Name": "PhoneNumber", "Value": 254708374149 }
            ]
          }
        }
      }
    }

## Failed Callback Shape

    {
      "Body": {
        "stkCallback": {
          "MerchantRequestID": "29115-34620561-2",
          "CheckoutRequestID": "ws_CO_20052026102030000000",
          "ResultCode": 1032,
          "ResultDesc": "Request cancelled by user"
        }
      }
    }


path: references/maintenance.md
content: # Skill Maintenance

Use this reference when updating mpesa-daraja or preparing a ClawHub release.

## Source Update Check

Before changing examples, production guidance, endpoint assumptions, or publishing a new version, check current official sources:

- Safaricom Daraja developer portal: https://developer.safaricom.co.ke/
- The specific Daraja API page for the flow being updated: STK Push, C2B, B2C, Transaction Status, Reversal, or Account Balance.
- Safaricom developer announcements or release notes when available.

Prefer official Safaricom sources over blogs, snippets, or copied SDK examples. If official pages are unavailable, say that clearly in the release notes and avoid making strong claims about changed behavior.

## Versioning

Use semantic versioning:

- Patch: typo fixes, safer wording, small test fixture updates. Example: 1.0.1.
- Minor: new references, scripts, flow coverage, or reusable examples. Example: 1.1.0.
- Major: breaking changes in skill behavior, trigger scope, file structure, or safety policy. Example: 2.0.0.

Always update the version field in SKILL.md before publishing to ClawHub.

## Pre-Publish Validation

Run:

    python3 scripts/scan_skill_safety.py .
    python3 scripts/validate_daraja_fixture.py stk-request path/to/request.json
    python3 scripts/validate_daraja_fixture.py stk-callback path/to/callback.json

Only run fixture validation when fixture files exist. The safety scan should run every time.

## Release Notes Template

    mpesa-daraja vX.Y.Z

    Changed:
    - ...

    Sources checked:
    - Safaricom Daraja portal
    - ...

    Validation:
    - scan_skill_safety.py
    - fixture validation, if applicable

    Notes:
    - No production account values or live payment calls used.

## Publishing Rule

Do not publish externally unless Nevil explicitly asks for a ClawHub push in that turn or the current task clearly includes publishing. If publishing is requested, check sources, bump version, run validation, then publish.


path: references/production-readiness.md
content: # Production Readiness

Use this reference for launch reviews, client handoff, production-access preparation, or audits of a Daraja integration.

## Hard Gates

- Fresh user approval is required before calling production endpoints or triggering payment prompts, payouts, reversals, or customer-facing payment activity.
- Real sensitive Daraja values must live in protected deployment settings, not in repository files.
- Callback URLs must be public HTTPS endpoints owned by the deployment.
- Fulfillment must be idempotent and coupled to confirmed payment state transitions.
- Operators need a manual reconciliation path before production traffic.

## Security

- Mask phone numbers, sensitive Daraja values, receipt numbers, and raw callback payloads in logs.
- Protect callback endpoints with rate limits, request size limits, and structured payload validation.
- Keep raw callback persistence access-controlled and retention-limited.
- Separate sandbox and production settings.
- Add a deny-by-default guard so tests cannot accidentally hit production.

## Reliability

- Cache Daraja authorization sessions until shortly before expiry.
- Retry only safe transient failures, such as timeouts, 429, and 5xx, according to a bounded policy.
- Do not blindly retry validation errors or duplicate callbacks.
- Alert on callback failures, stuck prompt_sent payments, repeated Daraja errors, and reconciliation mismatches.
- Store enough correlation data to investigate without storing sensitive raw values in logs.

## Data Model

Recommended uniqueness constraints:

- CheckoutRequestID
- MerchantRequestID
- MpesaReceiptNumber when present
- Internal order/payment intent ID

Recommended audit fields:

- internal payment ID
- Daraja request IDs
- normalized state
- masked phone number
- amount and currency
- callback received timestamp
- reconciliation status
- operator/manual-review notes

## Launch Checklist

- Official Daraja docs checked for current endpoints and fields.
- Sandbox happy path and failure path tested.
- Duplicate callback test passes.
- Timeout/cancellation path tested.
- Production config cannot be enabled in local/test by accident.
- .env.example contains placeholders only.
- Logs reviewed for sensitive-value and personal-data leakage.
- Run scripts/scan_skill_safety.py on skill examples and project fixtures before sharing code.


path: references/stk-push.md
content: # STK Push / Lipa na M-Pesa Online

Use this reference when implementing, reviewing, or testing customer-initiated checkout through Daraja STK Push.

## Official Source Check

Before implementation or publishing skill updates, verify the current endpoint names, field names, result codes, and sandbox behavior against official Safaricom Daraja documentation:

- Daraja portal: https://developer.safaricom.co.ke/
- STK Push / Lipa na M-Pesa Online docs in the Daraja portal
- Safaricom developer release notes, notices, or portal announcements when available

Do not copy sensitive Daraja values, real phone numbers, receipt numbers, or production callback payloads from docs, dashboards, logs, or client systems into generated code.

## Implementation Shape

Prefer these boundaries:

- MpesaClient: Daraja session setup, STK authorization value, outbound request, response mapping.
- PaymentIntent or Payment: internal state and idempotency keys.
- PaymentService: creates intent, validates amount/order state, calls MpesaClient.
- CallbackRoute: validates callback shape, stores raw event safely, returns fast.
- ReconciliationJob: checks stale or ambiguous payments.

Keep HTTP calls injectable so tests can mock Daraja without touching external endpoints.

## Request Rules

- Generate timestamp as YYYYMMDDHHmmss.
- Generate the STK authorization value using Safaricom's current shortcode/private-value/timestamp formula.
- Use CustomerPayBillOnline for PayBill flows unless the product intentionally supports a different Daraja-supported transaction type.
- Normalize phone numbers only through a deliberate, tested formatter. Reject ambiguous or non-Kenyan MSISDN input.
- Require HTTPS callback URLs outside local tests.
- Treat MerchantRequestID and CheckoutRequestID as correlation identifiers, not proof of payment.

## Callback Rules

- Return HTTP 200 quickly after durable persistence.
- Deduplicate by CheckoutRequestID; also store MerchantRequestID and receipt number when present.
- Successful callbacks must not double-credit on retries.
- Failed callbacks may omit CallbackMetadata; do not assume it exists.
- Mask PhoneNumber and all sensitive Daraja values in logs.
- Map known ResultCodes into explicit internal states, but preserve unknown codes for manual review.

## State Mapping

Recommended internal states:

- pending: internal payment intent exists.
- prompt_sent: Daraja accepted the STK request.
- paid: successful callback with receipt metadata.
- failed: clear unsuccessful callback.
- cancelled: user cancellation, commonly ResultCode 1032.
- expired: timeout or no response after the product-defined window.
- manual_review: ambiguous response, unknown result, duplicate conflict, or reconciliation mismatch.
- reversed: confirmed reversal after operator-approved or compliant reversal flow.

## Review Checklist

- Sensitive settings are environment-backed and never committed.
- Production endpoint usage requires explicit opt-in.
- Daraja session caching respects expiry and handles 401 with one safe refresh retry.
- Checkout creation and callback processing are transactionally safe.
- Callback handler has shape validation, idempotency, masked logging, and durable event storage.
- Tests cover success, cancellation, timeout, duplicate callback, malformed callback, and Daraja outage.


path: references/test-cases.md
content: # M-Pesa Daraja Test Cases

## Unit Tests

- Daraja session client builds the correct authorization header from placeholder app settings.
- Daraja session client never logs sensitive app values or session strings.
- STK authorization value generation follows Safaricom's current shortcode/private-value/timestamp formula.
- Timestamp uses Daraja format YYYYMMDDHHmmss and is generated in a consistent timezone.
- STK request rejects non-positive amount values.
- STK request rejects invalid MSISDN format; normalize local 07xx numbers to 2547xx only when the product explicitly allows it.
- STK request uses PartyB equal to the configured shortcode.
- STK request requires an HTTPS callback URL outside local unit tests.
- Daraja client maps network timeout to retryable internal error.
- Daraja client maps Daraja ResponseCode/ResultCode into explicit internal states.
- Callback parser extracts Amount, MpesaReceiptNumber, TransactionDate, and PhoneNumber from CallbackMetadata when ResultCode is 0.
- Callback parser handles failed callbacks that do not include CallbackMetadata.
- Callback parser rejects payloads missing Body.stkCallback.
- Callback handler masks phone numbers in logs.
- Callback handler is idempotent for duplicate CheckoutRequestID.

## Integration Tests With HTTP Mocking

- Daraja session endpoint returns a session string and expiry; client caches it and reuses it before expiry.
- Daraja session refresh happens when cached session data is near expiry.
- STK Push request sends the expected Authorization header.
- STK Push request includes BusinessShortCode, Password, Timestamp, TransactionType, Amount, PartyA, PartyB, PhoneNumber, CallBackURL, AccountReference, and TransactionDesc.
- Daraja 401 causes one session refresh and one retry, then fails cleanly if still unauthorized.
- Daraja 429 or 5xx is retried according to the app's retry policy.
- Daraja validation error is not retried.
- Callback route persists raw callback once and returns HTTP 200 quickly.
- Duplicate successful callback does not double-credit an order.
- Failed callback changes payment state to failed/cancelled/expired according to ResultCode mapping.

## Sandbox End-to-End Tests

- Start checkout for a sandbox order and verify a MerchantRequestID and CheckoutRequestID are stored.
- Simulate a successful callback and verify order/payment state becomes paid.
- Simulate user cancellation ResultCode 1032 and verify state becomes cancelled.
- Simulate timeout/expiry and verify state becomes expired or manual_review.
- Verify reconciliation job can find stale prompt_sent payments.
- Verify public callback URL is reachable over HTTPS from outside the local machine.
- Verify logs contain internal order IDs and Daraja request IDs but no sensitive Daraja values or full phone number.

## Production Readiness Checks

- Production base URL is gated by explicit environment configuration and cannot be enabled accidentally in tests.
- All real sensitive values are loaded from protected deployment settings, not committed files.
- Callback endpoint has rate limiting or upstream protection appropriate for the deployment.
- Database has uniqueness constraints for CheckoutRequestID, MerchantRequestID, and MpesaReceiptNumber where applicable.
- Payment fulfillment is idempotent and transactionally coupled to payment-state changes.
- Reversal and refund processes require explicit operator approval unless the user has built a compliant automated workflow.
- Monitoring alerts on repeated Daraja failures, callback failures, and stuck pending payments.
- Manual reconciliation path exists for ambiguous states.

## Example Pytest Names

- test_stk_authorization_value_uses_shortcode_private_value_timestamp
- test_stk_request_requires_https_callback_url
- test_daraja_client_masks_sensitive_values_in_logs
- test_success_callback_extracts_receipt_and_amount
- test_failed_callback_without_metadata_is_handled
- test_duplicate_callback_does_not_double_credit_order
- test_unauthorized_response_refreshes_session_once
- test_production_endpoint_requires_explicit_opt_in


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)