{"id":"bfd0d4b4-30a0-42ce-9342-1e9c1797e7d9","entityType":"agent","slug":"clawhub-nevilpaulo-mpesa-daraja","name":"M-Pesa Daraja","canonicalUrl":"https://www.xpersona.co/agent/clawhub-nevilpaulo-mpesa-daraja","canonicalPath":"/agent/clawhub-nevilpaulo-mpesa-daraja","generatedAt":"2026-10-13T15:26:42.843Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T23:15:27.288Z","emptyReason":null},"description":"Advisory reference for Safaricom M-Pesa Daraja integration planning, reviews, sandbox testing, callbacks, reconciliation, and production-readiness. It provid...","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 978 downloads reported by the source. Last updated 10/11/2026.","installCommand":"clawhub skill install s176qs36hypcd7sgpevsvh7fd186rydm:mpesa-daraja","sourceUrl":"https://clawhub.ai/nevilpaulo/mpesa-daraja","homepage":"https://clawhub.ai/nevilpaulo/skills/mpesa-daraja","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/nevilpaulo/mpesa-daraja","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/nevilpaulo/skills/mpesa-daraja","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":60,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"M-Pesa Daraja technical dossier on Xpersona with agent coverage, OPENCLEW support, and live trust metadata."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-11T23:15:27.288Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T23:15:27.288Z","emptyReason":null},"stars":null,"forks":null,"downloads":978,"likes":null,"task":null,"library":null,"packageName":null,"latestVersion":"1.2.2","tractionLabel":"978 downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T23:15:27.189Z","emptyReason":null},"lastUpdatedAt":"2026-10-11T23:15:27.288Z","lastCrawledAt":"2026-10-11T23:15:27.189Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-12T23:15:27.189Z","lastVerifiedAt":null,"highlights":[{"version":"1.2.2","createdAt":"2026-05-25T08:13:01.042Z","changelog":"Clarify that the skill is advisory-only and does not require external account access to install or use. Soften marketplace-facing wording that ClawHub inferred as required credentials while preserving Daraja planning, review, sandbox, callback, and production-readiness guidance.","fileCount":10,"zipByteSize":13196},{"version":"1.2.1","createdAt":"2026-05-25T07:50:30.792Z","changelog":"Remove optional Daraja env var metadata so ClawHub does not present the advisory skill as requiring API keys. No payment guidance or API behavior changed.","fileCount":11,"zipByteSize":14669},{"version":"1.2.0","createdAt":"2026-05-25T07:42:37.433Z","changelog":"mpesa-daraja v1.2.0\\n\\nChanged:\\n- Added current Daraja endpoint inventory reference covering STK Push, query, transaction status, reversal, account balance, C2B, and B2C.\\n- Updated workflow guidance to point to the endpoint reference.\\n- Bumped the skill from 1.1.0 to 1.2.0.\\n\\nSources checked:\\n- Safaricom Daraja developer portal: https://developer.safaricom.co.ke/\\n- Official portal API catalogue checked during the 2026-05-25 maintenance run.\\n\\nValidation:\\n- python3 scripts/scan_skill_safety.py .\\n- python3 -m py_compile scripts/*.py\\n- Fixture validation skipped because no fixture JSON files exist.\\n\\nNotes:\\n- No production credentials or live payment calls used.","fileCount":11,"zipByteSize":14956},{"version":"1.1.0","createdAt":"2026-05-21T09:14:11.592Z","changelog":"Add STK Push guidance, production-readiness checks, maintenance workflow, and pre-publish safety scanner.","fileCount":10,"zipByteSize":13757},{"version":"1.0.0","createdAt":"2026-05-20T20:15:15.176Z","changelog":"Initial release: Safaricom M-Pesa Daraja integration planning, implementation guidance, sandbox testing, callback idempotency checks, and production-readiness review.","fileCount":6,"zipByteSize":8032}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s176qs36hypcd7sgpevsvh7fd186rydm:mpesa-daraja","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s176qs36hypcd7sgpevsvh7fd186rydm:mpesa-daraja` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/nevilpaulo/mpesa-daraja before using production credentials."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-nevilpaulo-mpesa-daraja/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-nevilpaulo-mpesa-daraja/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-nevilpaulo-mpesa-daraja/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-nevilpaulo-mpesa-daraja/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-nevilpaulo-mpesa-daraja/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-nevilpaulo-mpesa-daraja/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-13T15:26:42.842Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-nevilpaulo-mpesa-daraja/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-nevilpaulo-mpesa-daraja/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-nevilpaulo-mpesa-daraja/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-nevilpaulo-mpesa-daraja/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T23:15:27.288Z","emptyReason":null},"readme":"Skill: M-Pesa Daraja\n\nOwner: nevilpaulo\n\nSummary: Advisory reference for Safaricom M-Pesa Daraja integration planning, reviews, sandbox testing, callbacks, reconciliation, and production-readiness. It provid...\n\nTags: latest:1.2.2\n\nVersion history:\n\nv1.2.2 | 2026-05-25T08:13:01.042Z | user\n\nClarify that the skill is advisory-only and does not require external account access to install or use. Soften marketplace-facing wording that ClawHub inferred as required credentials while preserving Daraja planning, review, sandbox, callback, and production-readiness guidance.\n\nv1.2.1 | 2026-05-25T07:50:30.792Z | user\n\nRemove optional Daraja env var metadata so ClawHub does not present the advisory skill as requiring API keys. No payment guidance or API behavior changed.\n\nv1.2.0 | 2026-05-25T07:42:37.433Z | user\n\nmpesa-daraja v1.2.0\\n\\nChanged:\\n- Added current Daraja endpoint inventory reference covering STK Push, query, transaction status, reversal, account balance, C2B, and B2C.\\n- Updated workflow guidance to point to the endpoint reference.\\n- Bumped the skill from 1.1.0 to 1.2.0.\\n\\nSources checked:\\n- Safaricom Daraja developer portal: https://developer.safaricom.co.ke/\\n- Official portal API catalogue checked during the 2026-05-25 maintenance run.\\n\\nValidation:\\n- python3 scripts/scan_skill_safety.py .\\n- python3 -m py_compile scripts/*.py\\n- Fixture validation skipped because no fixture JSON files exist.\\n\\nNotes:\\n- No production credentials or live payment calls used.\n\nv1.1.0 | 2026-05-21T09:14:11.592Z | user\n\nAdd STK Push guidance, production-readiness checks, maintenance workflow, and pre-publish safety scanner.\n\nv1.0.0 | 2026-05-20T20:15:15.176Z | user\n\nInitial release: Safaricom M-Pesa Daraja integration planning, implementation guidance, sandbox testing, callback idempotency checks, and production-readiness review.\n\nArchive index:\n\nArchive v1.2.2: 10 files, 13196 bytes\n\nFiles: agents/openai.yaml (235b), references/api-endpoints.md (2303b), references/examples.md (4054b), references/maintenance.md (2002b), references/production-readiness.md (2363b), references/stk-push.md (3327b), references/test-cases.md (4081b), skill-card.md (3023b), SKILL.md (4614b), _meta.json (131b)\n\nFile v1.2.2:SKILL.md\n\n---\nname: mpesa-daraja\ndescription: 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.\nversion: 1.2.2\n---\n\n# M-Pesa Daraja\n\nThis is an advisory/reference skill. It does not call Safaricom APIs, initiate payments, or require external account access to install or use.\n\n## Operating Rules\n\n- Treat this as payment-integration guidance, not permission to move money.\n- Default to Daraja sandbox endpoints and placeholder configuration unless the user explicitly asks for production guidance.\n- Never store sensitive Daraja values, callback payloads with phone numbers, or transaction IDs in committed files.\n- Ask before calling any live/production endpoint or sending any request that could trigger a payment prompt, payout, reversal, or customer-facing callback.\n- Prefer environment variables and protected deployment settings over \\`.env\\` files in shared repos. If \\`.env.example\\` is needed, use placeholder values only.\n- Make callbacks idempotent: verify request shape, persist raw event safely, deduplicate by CheckoutRequestID/ConversationID/TransactionID, then process business state transitions.\n\n## Workflow\n\n1. Identify the payment flow:\n   - **STK Push / Lipa na M-Pesa Online** for customer-initiated checkout.\n   - **C2B** for paybill/till customer payments with validation and confirmation callbacks.\n   - **B2C** for business payouts to customers or agents.\n   - **Transaction Status / Reversal** for reconciliation and recovery.\n2. Confirm runtime context: language/framework, sandbox vs production, callback URL availability, persistence layer, and compliance constraints.\n3. Design the integration around these boundaries:\n   - Daraja session setup and caching\n   - request authorization value generation\n   - outbound Daraja client\n   - callback receiver\n   - idempotency/reconciliation\n   - observability without leaking personal or payment data\n4. Use \\`references/examples.md\\` when the user asks for sample OpenClaw prompts, implementation examples, or architecture snippets.\n5. Use \\`references/test-cases.md\\` when the user asks for QA scenarios, unit tests, integration tests, sandbox tests, or production-readiness checks.\n6. Use \\`references/stk-push.md\\` for STK Push/Lipa na M-Pesa Online implementation details.\n7. Use \\`references/api-endpoints.md\\` when the user needs current Daraja endpoint names, versions, or flow coverage.\n8. Use \\`references/production-readiness.md\\` before production-readiness reviews, client handoff, or launch checklists.\n9. 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.\n\n## Implementation Guidance\n\n- Keep Daraja client code small and injectable so app tests can mock HTTP calls.\n- Follow Safaricom's current STK authorization formula with timestamp format \\`YYYYMMDDHHmmss\\`.\n- Cache Daraja authorization sessions until shortly before expiry.\n- Expose public HTTPS callback URLs in sandbox using a stable tunnel or deployed test environment; avoid local-only callback URLs.\n- Return fast from callbacks after durable persistence; do heavier fulfillment asynchronously when possible.\n- Log correlation IDs, response codes, and internal order IDs. Mask phone numbers, names, and all sensitive Daraja values.\n- Model payment state explicitly, for example: \\`pending\\`, \\`prompt_sent\\`, \\`paid\\`, \\`failed\\`, \\`cancelled\\`, \\`expired\\`, \\`reversed\\`, \\`manual_review\\`.\n\n## Common OpenClaw Requests\n\n- \"Use $mpesa-daraja to add STK Push checkout to this Django app.\"\n- \"Use $mpesa-daraja to write sandbox test cases for our M-Pesa checkout.\"\n- \"Use $mpesa-daraja to review this Daraja callback handler for idempotency and sensitive-data leakage.\"\n- \"Use $mpesa-daraja to create an OpenClaw automation that drafts M-Pesa reconciliation reports without touching production APIs.\"\n\n## Deliverables To Prefer\n\n- For implementation: client module, callback route, typed request/response models, masked logging, tests, and \\`.env.example\\`.\n- For review: findings ordered by payment risk, security risk, idempotency/reconciliation risk, then code quality.\n- For planning: flow diagram in text, endpoint list, configuration list, callback contract, test plan, and production checklist.\n- For troubleshooting: exact failing step, expected Daraja response, likely causes, and the smallest safe verification step.\n\nFile v1.2.2:_meta.json\n\n{\n  \"ownerId\": \"kn72x5vtcasyn6bm1yyg4xqv0s86rdry\",\n  \"slug\": \"mpesa-daraja\",\n  \"version\": \"1.2.2\",\n  \"publishedAt\": 1779696781042\n}\n\nFile v1.2.2:references/api-endpoints.md\n\n# Daraja Endpoint Reference\n\nUse 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.\n\n## Official Source Snapshot\n\nChecked on 2026-05-25 against:\n\n- Safaricom Daraja developer portal: https://developer.safaricom.co.ke/\n- Safaricom developer API catalogue GraphQL query \\`getApis\\` via https://developer.safaricom.co.ke/api/graphql\n- Portal documentation records through \\`populateApi\\` for STK Push, STK Query, Transaction Status, Reversal, and Account Balance\n\nThe 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.\n\n## Core Sandbox Endpoints\n\n| Flow | Method | Sandbox endpoint |\n| --- | --- | --- |\n| Daraja authorization session | GET | Check the current Daraja portal for the session endpoint before implementation. |\n| STK Push / Lipa na M-Pesa Online | POST | \\`https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest\\` |\n| STK Push query | POST | \\`https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query\\` |\n| C2B register URL | POST | \\`https://sandbox.safaricom.co.ke/mpesa/c2b/v2/registerurl\\` |\n| B2C payment request | POST | \\`https://sandbox.safaricom.co.ke/mpesa/b2c/v3/paymentrequest\\` |\n| Transaction status | POST | \\`https://sandbox.safaricom.co.ke/mpesa/transactionstatus/v1/query\\` |\n| Account balance | POST | \\`https://sandbox.safaricom.co.ke/mpesa/accountbalance/v1/query\\` |\n| Reversal | POST | \\`https://sandbox.safaricom.co.ke/mpesa/reversal/v1/request\\` |\n\n## Guidance\n\n- Use sandbox endpoints by default. Switch to production hostnames only when the user explicitly asks for production guidance, and ask before any live call.\n- Re-check the official catalogue before writing production instructions. Endpoint versions can differ from older examples and SDK snippets.\n- 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.\n- If the catalogue and a detailed documentation page disagree, report the disagreement and avoid claiming a migration is required without confirmation from Safaricom.\n\nFile v1.2.2:references/examples.md\n\n# M-Pesa Daraja Examples\n\n## OpenClaw Usage Examples\n\nUse these as realistic prompts for another OpenClaw/Codex session:\n\n- \"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.\"\n- \"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.\"\n- \"Use $mpesa-daraja to create a Django model and service layer for tracking M-Pesa payments from pending to paid/failed/reversed.\"\n- \"Use $mpesa-daraja to draft a client implementation plan for Paybill C2B validation and confirmation callbacks.\"\n- \"Use $mpesa-daraja to generate QA test cases for STK Push before we request production access.\"\n\n## Minimal STK Push Architecture\n\nRecommended components:\n\n- MpesaClient: handles Daraja session setup, STK authorization value generation, and Daraja HTTP requests.\n- PaymentService: creates internal payment intent records and calls MpesaClient.\n- CallbackController: receives Daraja callbacks, validates payload shape, persists raw event, and invokes reconciliation.\n- PaymentRepository: enforces idempotency with unique CheckoutRequestID/MerchantRequestID/TransactionID constraints.\n- ReconciliationJob: checks pending/unclear payments using transaction status or internal review queues.\n\nState flow:\n\n    order_created -> payment_pending -> stk_prompt_sent -> paid\n                                          |-> failed\n                                          |-> cancelled\n                                          |-> expired\n                                          |-> manual_review\n    paid -> reversed\n\n## Environment Variables\n\nUse placeholders in examples:\n\n    MPESA_ENV=sandbox\n    MPESA_CONSUMER_KEY=replace-me\n    MPESA_PRIVATE_APP_VALUE=replace-me\n    MPESA_SHORTCODE=174379\n    MPESA_STK_AUTH_VALUE=replace-me\n    MPESA_CALLBACK_URL=https://example.test/api/mpesa/stk-callback\n    MPESA_INITIATOR_NAME=replace-me\n    MPESA_PROTECTED_OPERATOR_VALUE=replace-me\n\nDo not commit real values.\n\n## Python STK Authorization Value Example\n\n    import base64\n    from datetime import datetime, timezone\n\n\n    def daraja_timestamp() -> str:\n        return datetime.now(timezone.utc).strftime(\"%Y%m%d%H%M%S\")\n\n\n    def stk_authorization_value(shortcode: str, private_value: str, timestamp: str) -> str:\n        raw = f\"{shortcode}{private_value}{timestamp}\".encode(\"utf-8\")\n        return base64.b64encode(raw).decode(\"utf-8\")\n\n## STK Push Request Shape\n\n    {\n      \"BusinessShortCode\": \"174379\",\n      \"Password\": \"base64-shortcode-private-value-timestamp\",\n      \"Timestamp\": \"20260520101630\",\n      \"TransactionType\": \"CustomerPayBillOnline\",\n      \"Amount\": 100,\n      \"PartyA\": \"254708374149\",\n      \"PartyB\": \"174379\",\n      \"PhoneNumber\": \"254708374149\",\n      \"CallBackURL\": \"https://example.test/api/mpesa/stk-callback\",\n      \"AccountReference\": \"ORDER-1001\",\n      \"TransactionDesc\": \"Order payment\"\n    }\n\n## Successful Callback Shape\n\n    {\n      \"Body\": {\n        \"stkCallback\": {\n          \"MerchantRequestID\": \"29115-34620561-1\",\n          \"CheckoutRequestID\": \"ws_CO_20052026101630000000\",\n          \"ResultCode\": 0,\n          \"ResultDesc\": \"The service request is processed successfully.\",\n          \"CallbackMetadata\": {\n            \"Item\": [\n              { \"Name\": \"Amount\", \"Value\": 100 },\n              { \"Name\": \"MpesaReceiptNumber\", \"Value\": \"TEK7X12345\" },\n              { \"Name\": \"TransactionDate\", \"Value\": 20260520101701 },\n              { \"Name\": \"PhoneNumber\", \"Value\": 254708374149 }\n            ]\n          }\n        }\n      }\n    }\n\n## Failed Callback Shape\n\n    {\n      \"Body\": {\n        \"stkCallback\": {\n          \"MerchantRequestID\": \"29115-34620561-2\",\n          \"CheckoutRequestID\": \"ws_CO_20052026102030000000\",\n          \"ResultCode\": 1032,\n          \"ResultDesc\": \"Request cancelled by user\"\n        }\n      }\n    }\n\nFile v1.2.2:references/maintenance.md\n\n# Skill Maintenance\n\nUse this reference when updating mpesa-daraja or preparing a ClawHub release.\n\n## Source Update Check\n\nBefore changing examples, production guidance, endpoint assumptions, or publishing a new version, check current official sources:\n\n- Safaricom Daraja developer portal: https://developer.safaricom.co.ke/\n- The specific Daraja API page for the flow being updated: STK Push, C2B, B2C, Transaction Status, Reversal, or Account Balance.\n- Safaricom developer announcements or release notes when available.\n\nPrefer 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.\n\n## Versioning\n\nUse semantic versioning:\n\n- Patch: typo fixes, safer wording, small test fixture updates. Example: 1.0.1.\n- Minor: new references, scripts, flow coverage, or reusable examples. Example: 1.1.0.\n- Major: breaking changes in skill behavior, trigger scope, file structure, or safety policy. Example: 2.0.0.\n\nAlways update the version field in SKILL.md before publishing to ClawHub.\n\n## Pre-Publish Validation\n\nRun:\n\n    python3 scripts/scan_skill_safety.py .\n    python3 scripts/validate_daraja_fixture.py stk-request path/to/request.json\n    python3 scripts/validate_daraja_fixture.py stk-callback path/to/callback.json\n\nOnly run fixture validation when fixture files exist. The safety scan should run every time.\n\n## Release Notes Template\n\n    mpesa-daraja vX.Y.Z\n\n    Changed:\n    - ...\n\n    Sources checked:\n    - Safaricom Daraja portal\n    - ...\n\n    Validation:\n    - scan_skill_safety.py\n    - fixture validation, if applicable\n\n    Notes:\n    - No production account values or live payment calls used.\n\n## Publishing Rule\n\nDo 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.\n\nFile v1.2.2:references/production-readiness.md\n\n# Production Readiness\n\nUse this reference for launch reviews, client handoff, production-access preparation, or audits of a Daraja integration.\n\n## Hard Gates\n\n- Fresh user approval is required before calling production endpoints or triggering payment prompts, payouts, reversals, or customer-facing payment activity.\n- Real sensitive Daraja values must live in protected deployment settings, not in repository files.\n- Callback URLs must be public HTTPS endpoints owned by the deployment.\n- Fulfillment must be idempotent and coupled to confirmed payment state transitions.\n- Operators need a manual reconciliation path before production traffic.\n\n## Security\n\n- Mask phone numbers, sensitive Daraja values, receipt numbers, and raw callback payloads in logs.\n- Protect callback endpoints with rate limits, request size limits, and structured payload validation.\n- Keep raw callback persistence access-controlled and retention-limited.\n- Separate sandbox and production settings.\n- Add a deny-by-default guard so tests cannot accidentally hit production.\n\n## Reliability\n\n- Cache Daraja authorization sessions until shortly before expiry.\n- Retry only safe transient failures, such as timeouts, 429, and 5xx, according to a bounded policy.\n- Do not blindly retry validation errors or duplicate callbacks.\n- Alert on callback failures, stuck prompt_sent payments, repeated Daraja errors, and reconciliation mismatches.\n- Store enough correlation data to investigate without storing sensitive raw values in logs.\n\n## Data Model\n\nRecommended uniqueness constraints:\n\n- CheckoutRequestID\n- MerchantRequestID\n- MpesaReceiptNumber when present\n- Internal order/payment intent ID\n\nRecommended audit fields:\n\n- internal payment ID\n- Daraja request IDs\n- normalized state\n- masked phone number\n- amount and currency\n- callback received timestamp\n- reconciliation status\n- operator/manual-review notes\n\n## Launch Checklist\n\n- Official Daraja docs checked for current endpoints and fields.\n- Sandbox happy path and failure path tested.\n- Duplicate callback test passes.\n- Timeout/cancellation path tested.\n- Production config cannot be enabled in local/test by accident.\n- .env.example contains placeholders only.\n- Logs reviewed for sensitive-value and personal-data leakage.\n- Run scripts/scan_skill_safety.py on skill examples and project fixtures before sharing code.\n\nFile v1.2.2:references/stk-push.md\n\n# STK Push / Lipa na M-Pesa Online\n\nUse this reference when implementing, reviewing, or testing customer-initiated checkout through Daraja STK Push.\n\n## Official Source Check\n\nBefore implementation or publishing skill updates, verify the current endpoint names, field names, result codes, and sandbox behavior against official Safaricom Daraja documentation:\n\n- Daraja portal: https://developer.safaricom.co.ke/\n- STK Push / Lipa na M-Pesa Online docs in the Daraja portal\n- Safaricom developer release notes, notices, or portal announcements when available\n\nDo not copy sensitive Daraja values, real phone numbers, receipt numbers, or production callback payloads from docs, dashboards, logs, or client systems into generated code.\n\n## Implementation Shape\n\nPrefer these boundaries:\n\n- MpesaClient: Daraja session setup, STK authorization value, outbound request, response mapping.\n- PaymentIntent or Payment: internal state and idempotency keys.\n- PaymentService: creates intent, validates amount/order state, calls MpesaClient.\n- CallbackRoute: validates callback shape, stores raw event safely, returns fast.\n- ReconciliationJob: checks stale or ambiguous payments.\n\nKeep HTTP calls injectable so tests can mock Daraja without touching external endpoints.\n\n## Request Rules\n\n- Generate timestamp as YYYYMMDDHHmmss.\n- Generate the STK authorization value using Safaricom's current shortcode/private-value/timestamp formula.\n- Use CustomerPayBillOnline for PayBill flows unless the product intentionally supports a different Daraja-supported transaction type.\n- Normalize phone numbers only through a deliberate, tested formatter. Reject ambiguous or non-Kenyan MSISDN input.\n- Require HTTPS callback URLs outside local tests.\n- Treat MerchantRequestID and CheckoutRequestID as correlation identifiers, not proof of payment.\n\n## Callback Rules\n\n- Return HTTP 200 quickly after durable persistence.\n- Deduplicate by CheckoutRequestID; also store MerchantRequestID and receipt number when present.\n- Successful callbacks must not double-credit on retries.\n- Failed callbacks may omit CallbackMetadata; do not assume it exists.\n- Mask PhoneNumber and all sensitive Daraja values in logs.\n- Map known ResultCodes into explicit internal states, but preserve unknown codes for manual review.\n\n## State Mapping\n\nRecommended internal states:\n\n- pending: internal payment intent exists.\n- prompt_sent: Daraja accepted the STK request.\n- paid: successful callback with receipt metadata.\n- failed: clear unsuccessful callback.\n- cancelled: user cancellation, commonly ResultCode 1032.\n- expired: timeout or no response after the product-defined window.\n- manual_review: ambiguous response, unknown result, duplicate conflict, or reconciliation mismatch.\n- reversed: confirmed reversal after operator-approved or compliant reversal flow.\n\n## Review Checklist\n\n- Sensitive settings are environment-backed and never committed.\n- Production endpoint usage requires explicit opt-in.\n- Daraja session caching respects expiry and handles 401 with one safe refresh retry.\n- Checkout creation and callback processing are transactionally safe.\n- Callback handler has shape validation, idempotency, masked logging, and durable event storage.\n- Tests cover success, cancellation, timeout, duplicate callback, malformed callback, and Daraja outage.\n\nFile v1.2.2:references/test-cases.md\n\n# M-Pesa Daraja Test Cases\n\n## Unit Tests\n\n- Daraja session client builds the correct authorization header from placeholder app settings.\n- Daraja session client never logs sensitive app values or session strings.\n- STK authorization value generation follows Safaricom's current shortcode/private-value/timestamp formula.\n- Timestamp uses Daraja format YYYYMMDDHHmmss and is generated in a consistent timezone.\n- STK request rejects non-positive amount values.\n- STK request rejects invalid MSISDN format; normalize local 07xx numbers to 2547xx only when the product explicitly allows it.\n- STK request uses PartyB equal to the configured shortcode.\n- STK request requires an HTTPS callback URL outside local unit tests.\n- Daraja client maps network timeout to retryable internal error.\n- Daraja client maps Daraja ResponseCode/ResultCode into explicit internal states.\n- Callback parser extracts Amount, MpesaReceiptNumber, TransactionDate, and PhoneNumber from CallbackMetadata when ResultCode is 0.\n- Callback parser handles failed callbacks that do not include CallbackMetadata.\n- Callback parser rejects payloads missing Body.stkCallback.\n- Callback handler masks phone numbers in logs.\n- Callback handler is idempotent for duplicate CheckoutRequestID.\n\n## Integration Tests With HTTP Mocking\n\n- Daraja session endpoint returns a session string and expiry; client caches it and reuses it before expiry.\n- Daraja session refresh happens when cached session data is near expiry.\n- STK Push request sends the expected Authorization header.\n- STK Push request includes BusinessShortCode, Password, Timestamp, TransactionType, Amount, PartyA, PartyB, PhoneNumber, CallBackURL, AccountReference, and TransactionDesc.\n- Daraja 401 causes one session refresh and one retry, then fails cleanly if still unauthorized.\n- Daraja 429 or 5xx is retried according to the app's retry policy.\n- Daraja validation error is not retried.\n- Callback route persists raw callback once and returns HTTP 200 quickly.\n- Duplicate successful callback does not double-credit an order.\n- Failed callback changes payment state to failed/cancelled/expired according to ResultCode mapping.\n\n## Sandbox End-to-End Tests\n\n- Start checkout for a sandbox order and verify a MerchantRequestID and CheckoutRequestID are stored.\n- Simulate a successful callback and verify order/payment state becomes paid.\n- Simulate user cancellation ResultCode 1032 and verify state becomes cancelled.\n- Simulate timeout/expiry and verify state becomes expired or manual_review.\n- Verify reconciliation job can find stale prompt_sent payments.\n- Verify public callback URL is reachable over HTTPS from outside the local machine.\n- Verify logs contain internal order IDs and Daraja request IDs but no sensitive Daraja values or full phone number.\n\n## Production Readiness Checks\n\n- Production base URL is gated by explicit environment configuration and cannot be enabled accidentally in tests.\n- All real sensitive values are loaded from protected deployment settings, not committed files.\n- Callback endpoint has rate limiting or upstream protection appropriate for the deployment.\n- Database has uniqueness constraints for CheckoutRequestID, MerchantRequestID, and MpesaReceiptNumber where applicable.\n- Payment fulfillment is idempotent and transactionally coupled to payment-state changes.\n- Reversal and refund processes require explicit operator approval unless the user has built a compliant automated workflow.\n- Monitoring alerts on repeated Daraja failures, callback failures, and stuck pending payments.\n- Manual reconciliation path exists for ambiguous states.\n\n## Example Pytest Names\n\n- test_stk_authorization_value_uses_shortcode_private_value_timestamp\n- test_stk_request_requires_https_callback_url\n- test_daraja_client_masks_sensitive_values_in_logs\n- test_success_callback_extracts_receipt_and_amount\n- test_failed_callback_without_metadata_is_handled\n- test_duplicate_callback_does_not_double_credit_order\n- test_unauthorized_response_refreshes_session_once\n- test_production_endpoint_requires_explicit_opt_in\n\nFile v1.2.2:skill-card.md\n\n## Description:\n\nAdvisory reference for Safaricom M-Pesa Daraja integration planning, reviews, sandbox testing, callbacks, reconciliation, and production-readiness.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[nevilpaulo](https://clawhub.ai/user/nevilpaulo)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and engineers use this skill to plan, implement, review, test, and prepare Safaricom M-Pesa Daraja payment integrations. It focuses on sandbox-first workflows, callback handling, reconciliation, and production-readiness guidance.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Payment-integration guidance could be applied to live payment, payout, reversal, or customer-facing flows without sufficient approval.\n\nMitigation: Require explicit approval before any live Daraja request and default to sandbox endpoints and placeholder configuration.\n\nRisk: Real Daraja credentials, phone numbers, transaction IDs, receipt numbers, or callback payloads could leak through generated examples, logs, or committed files.\n\nMitigation: Keep sensitive values in protected deployment settings, use placeholders in examples, and mask payment and personal data in logs.\n\nRisk: Callback retries or duplicate Daraja events could double-credit or incorrectly transition payment state.\n\nMitigation: Persist callbacks durably, deduplicate by Daraja request and transaction identifiers, and couple fulfillment to confirmed idempotent state transitions.\n\nRisk: Endpoint names, versions, fields, or sandbox behavior may drift from the bundled reference snapshot.\n\nMitigation: Verify current Safaricom Daraja documentation before production work or skill updates.\n\n## Reference(s):\n\n- [ClawHub Skill Page](https://clawhub.ai/nevilpaulo/skills/mpesa-daraja)\n- [Daraja Endpoint Reference](references/api-endpoints.md)\n- [M-Pesa Daraja Examples](references/examples.md)\n- [STK Push / Lipa na M-Pesa Online](references/stk-push.md)\n- [Production Readiness](references/production-readiness.md)\n- [M-Pesa Daraja Test Cases](references/test-cases.md)\n- [Skill Maintenance](references/maintenance.md)\n- [Safaricom Daraja Developer Portal](https://developer.safaricom.co.ke/)\n- [Safaricom Developer API Catalogue](https://developer.safaricom.co.ke/api/graphql)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Markdown, Code, Shell commands, Configuration, Guidance]\n\n**Output Format:** [Markdown with inline code blocks and configuration snippets]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Advisory output only; live payment, payout, reversal, or customer-facing requests require explicit approval.]\n\n## Skill Version(s):\n\n1.2.2 (source: frontmatter and server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v1.2.2:agents/openai.yaml\n\ninterface:\n  display_name: \"M-Pesa Daraja\"\n  short_description: \"Build and test Safaricom M-Pesa Daraja integrations.\"\n  default_prompt: \"Use the M-Pesa Daraja skill to design, implement, or test an M-Pesa payment integration safely.\"\n\nArchive v1.2.1: 11 files, 14669 bytes\n\nFiles: agents/openai.yaml (235b), references/api-endpoints.md (2298b), references/examples.md (3955b), references/maintenance.md (1999b), references/production-readiness.md (2397b), references/stk-push.md (3269b), references/test-cases.md (4031b), scripts/scan_skill_safety.py (2621b), scripts/validate_daraja_fixture.py (3384b), SKILL.md (5043b), _meta.json (131b)\n\nFile v1.2.1:SKILL.md\n\n---\nname: mpesa-daraja\ndescription: Build, review, test, or explain Safaricom M-Pesa Daraja API integrations for STK Push/Lipa na M-Pesa Online, C2B validation and confirmation, B2C payouts, transaction status, reversals, callbacks, credential setup, sandbox testing, and production-readiness. Use when Codex needs to create implementation plans, code, examples, mocks, test cases, troubleshooting steps, or security checks for M-Pesa payments in OpenClaw or client applications.\nversion: 1.2.1\n---\n\n# M-Pesa Daraja\n\n## Operating Rules\n\n- Treat this as a payment-integration skill, not permission to move money.\n- Default to Daraja sandbox endpoints and test credentials unless the user explicitly asks for production guidance.\n- Never store consumer secrets, passkeys, initiator passwords, security credentials, access tokens, callback payloads with phone numbers, or transaction IDs in committed files.\n- Ask before calling any live/production endpoint or sending any request that could trigger a payment prompt, payout, reversal, or customer-facing callback.\n- Prefer environment variables and secret managers over \\`.env\\` files in shared repos. If \\`.env.example\\` is needed, use placeholder values only.\n- Make callbacks idempotent: verify request shape, persist raw event safely, deduplicate by CheckoutRequestID/ConversationID/TransactionID, then process business state transitions.\n\n## Workflow\n\n1. Identify the payment flow:\n   - **STK Push / Lipa na M-Pesa Online** for customer-initiated checkout.\n   - **C2B** for paybill/till customer payments with validation and confirmation callbacks.\n   - **B2C** for business payouts to customers or agents.\n   - **Transaction Status / Reversal** for reconciliation and recovery.\n2. Confirm runtime context: language/framework, sandbox vs production, callback URL availability, persistence layer, and compliance constraints.\n3. Design the integration around these boundaries:\n   - token generation and caching\n   - request signing/password generation\n   - outbound Daraja client\n   - callback receiver\n   - idempotency/reconciliation\n   - observability without leaking personal or payment data\n4. Use \\`references/examples.md\\` when the user asks for sample OpenClaw prompts, implementation examples, or architecture snippets.\n5. Use \\`references/test-cases.md\\` when the user asks for QA scenarios, unit tests, integration tests, sandbox tests, or production-readiness checks.\n6. Use \\`references/stk-push.md\\` for STK Push/Lipa na M-Pesa Online implementation details.\n7. Use \\`references/api-endpoints.md\\` when the user needs current Daraja endpoint names, versions, or flow coverage.\n8. Use \\`references/production-readiness.md\\` before production-readiness reviews, client handoff, or launch checklists.\n9. 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.\n10. Use \\`scripts/validate_daraja_fixture.py\\` to sanity-check example STK Push request and callback JSON fixtures before writing tests around them.\n11. Use \\`scripts/scan_skill_safety.py\\` before publishing or sharing this skill to detect real-looking secrets, live tokens, and unsafe examples.\n\n## Implementation Guidance\n\n- Keep Daraja client code small and injectable so app tests can mock HTTP calls.\n- Generate the STK password as \\`base64(shortcode + passkey + timestamp)\\` with timestamp format \\`YYYYMMDDHHmmss\\`.\n- Use OAuth access tokens from Daraja and cache them until shortly before expiry.\n- Expose public HTTPS callback URLs in sandbox using a stable tunnel or deployed test environment; avoid local-only callback URLs.\n- Return fast from callbacks after durable persistence; do heavier fulfillment asynchronously when possible.\n- Log correlation IDs, response codes, and internal order IDs. Mask phone numbers, names, access tokens, credentials, and raw security credentials.\n- Model payment state explicitly, for example: \\`pending\\`, \\`prompt_sent\\`, \\`paid\\`, \\`failed\\`, \\`cancelled\\`, \\`expired\\`, \\`reversed\\`, \\`manual_review\\`.\n\n## Common OpenClaw Requests\n\n- \"Use $mpesa-daraja to add STK Push checkout to this Django app.\"\n- \"Use $mpesa-daraja to write sandbox test cases for our M-Pesa checkout.\"\n- \"Use $mpesa-daraja to review this Daraja callback handler for idempotency and secret leakage.\"\n- \"Use $mpesa-daraja to create an OpenClaw automation that drafts M-Pesa reconciliation reports without touching production APIs.\"\n\n## Deliverables To Prefer\n\n- For implementation: client module, callback route, typed request/response models, masked logging, tests, and \\`.env.example\\`.\n- For review: findings ordered by payment risk, security risk, idempotency/reconciliation risk, then code quality.\n- For planning: flow diagram in text, endpoint list, secret list, callback contract, test plan, and production checklist.\n- For troubleshooting: exact failing step, expected Daraja response, likely causes, and the smallest safe verification step.\n\nFile v1.2.1:_meta.json\n\n{\n  \"ownerId\": \"kn72x5vtcasyn6bm1yyg4xqv0s86rdry\",\n  \"slug\": \"mpesa-daraja\",\n  \"version\": \"1.2.1\",\n  \"publishedAt\": 1779695430792\n}\n\nFile v1.2.1:references/api-endpoints.md\n\n# Daraja Endpoint Reference\n\nUse 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.\n\n## Official Source Snapshot\n\nChecked on 2026-05-25 against:\n\n- Safaricom Daraja developer portal: https://developer.safaricom.co.ke/\n- Safaricom developer API catalogue GraphQL query \\`getApis\\` via https://developer.safaricom.co.ke/api/graphql\n- Portal documentation records through \\`populateApi\\` for STK Push, STK Query, Transaction Status, Reversal, and Account Balance\n\nThe 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.\n\n## Core Sandbox Endpoints\n\n| Flow | Method | Sandbox endpoint |\n| --- | --- | --- |\n| OAuth authorization | GET | \\`https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials\\` |\n| STK Push / Lipa na M-Pesa Online | POST | \\`https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest\\` |\n| STK Push query | POST | \\`https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query\\` |\n| C2B register URL | POST | \\`https://sandbox.safaricom.co.ke/mpesa/c2b/v2/registerurl\\` |\n| B2C payment request | POST | \\`https://sandbox.safaricom.co.ke/mpesa/b2c/v3/paymentrequest\\` |\n| Transaction status | POST | \\`https://sandbox.safaricom.co.ke/mpesa/transactionstatus/v1/query\\` |\n| Account balance | POST | \\`https://sandbox.safaricom.co.ke/mpesa/accountbalance/v1/query\\` |\n| Reversal | POST | \\`https://sandbox.safaricom.co.ke/mpesa/reversal/v1/request\\` |\n\n## Guidance\n\n- Use sandbox endpoints by default. Switch to production hostnames only when the user explicitly asks for production guidance, and ask before any live call.\n- Re-check the official catalogue before writing production instructions. Endpoint versions can differ from older examples and SDK snippets.\n- 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.\n- If the catalogue and a detailed documentation page disagree, report the disagreement and avoid claiming a migration is required without confirmation from Safaricom.\n\nFile v1.2.1:references/examples.md\n\n# M-Pesa Daraja Examples\n\n## OpenClaw Usage Examples\n\nUse these as realistic prompts for another OpenClaw/Codex session:\n\n- \"Use $mpesa-daraja to add sandbox STK Push checkout to my FastAPI app. Keep all credentials in env vars and add tests for token generation, password generation, outbound request shape, and callback idempotency.\"\n- \"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.\"\n- \"Use $mpesa-daraja to create a Django model and service layer for tracking M-Pesa payments from pending to paid/failed/reversed.\"\n- \"Use $mpesa-daraja to draft a client implementation plan for Paybill C2B validation and confirmation callbacks.\"\n- \"Use $mpesa-daraja to generate QA test cases for STK Push before we request production access.\"\n\n## Minimal STK Push Architecture\n\nRecommended components:\n\n- MpesaClient: handles OAuth, password generation, and Daraja HTTP requests.\n- PaymentService: creates internal payment intent records and calls MpesaClient.\n- CallbackController: receives Daraja callbacks, validates payload shape, persists raw event, and invokes reconciliation.\n- PaymentRepository: enforces idempotency with unique CheckoutRequestID/MerchantRequestID/TransactionID constraints.\n- ReconciliationJob: checks pending/unclear payments using transaction status or internal review queues.\n\nState flow:\n\n    order_created -> payment_pending -> stk_prompt_sent -> paid\n                                          |-> failed\n                                          |-> cancelled\n                                          |-> expired\n                                          |-> manual_review\n    paid -> reversed\n\n## Environment Variables\n\nUse placeholders in examples:\n\n    MPESA_ENV=sandbox\n    MPESA_CONSUMER_KEY=replace-me\n    MPESA_CONSUMER_SECRET=replace-me\n    MPESA_SHORTCODE=174379\n    MPESA_PASSKEY=replace-me\n    MPESA_CALLBACK_URL=https://example.test/api/mpesa/stk-callback\n    MPESA_INITIATOR_NAME=replace-me\n    MPESA_SECURITY_CREDENTIAL=replace-me\n\nDo not commit real values.\n\n## Python Password Generation Example\n\n    import base64\n    from datetime import datetime, timezone\n\n\n    def daraja_timestamp() -> str:\n        return datetime.now(timezone.utc).strftime(\"%Y%m%d%H%M%S\")\n\n\n    def stk_password(shortcode: str, passkey: str, timestamp: str) -> str:\n        raw = f\"{shortcode}{passkey}{timestamp}\".encode(\"utf-8\")\n        return base64.b64encode(raw).decode(\"utf-8\")\n\n## STK Push Request Shape\n\n    {\n      \"BusinessShortCode\": \"174379\",\n      \"Password\": \"base64-shortcode-passkey-timestamp\",\n      \"Timestamp\": \"20260520101630\",\n      \"TransactionType\": \"CustomerPayBillOnline\",\n      \"Amount\": 100,\n      \"PartyA\": \"254708374149\",\n      \"PartyB\": \"174379\",\n      \"PhoneNumber\": \"254708374149\",\n      \"CallBackURL\": \"https://example.test/api/mpesa/stk-callback\",\n      \"AccountReference\": \"ORDER-1001\",\n      \"TransactionDesc\": \"Order payment\"\n    }\n\n## Successful Callback Shape\n\n    {\n      \"Body\": {\n        \"stkCallback\": {\n          \"MerchantRequestID\": \"29115-34620561-1\",\n          \"CheckoutRequestID\": \"ws_CO_20052026101630000000\",\n          \"ResultCode\": 0,\n          \"ResultDesc\": \"The service request is processed successfully.\",\n          \"CallbackMetadata\": {\n            \"Item\": [\n              { \"Name\": \"Amount\", \"Value\": 100 },\n              { \"Name\": \"MpesaReceiptNumber\", \"Value\": \"TEK7X12345\" },\n              { \"Name\": \"TransactionDate\", \"Value\": 20260520101701 },\n              { \"Name\": \"PhoneNumber\", \"Value\": 254708374149 }\n            ]\n          }\n        }\n      }\n    }\n\n## Failed Callback Shape\n\n    {\n      \"Body\": {\n        \"stkCallback\": {\n          \"MerchantRequestID\": \"29115-34620561-2\",\n          \"CheckoutRequestID\": \"ws_CO_20052026102030000000\",\n          \"ResultCode\": 1032,\n          \"ResultDesc\": \"Request cancelled by user\"\n        }\n      }\n    }\n\nFile v1.2.1:references/maintenance.md\n\n# Skill Maintenance\n\nUse this reference when updating mpesa-daraja or preparing a ClawHub release.\n\n## Source Update Check\n\nBefore changing examples, production guidance, endpoint assumptions, or publishing a new version, check current official sources:\n\n- Safaricom Daraja developer portal: https://developer.safaricom.co.ke/\n- The specific Daraja API page for the flow being updated: STK Push, C2B, B2C, Transaction Status, Reversal, or Account Balance.\n- Safaricom developer announcements or release notes when available.\n\nPrefer 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.\n\n## Versioning\n\nUse semantic versioning:\n\n- Patch: typo fixes, safer wording, small test fixture updates. Example: 1.0.1.\n- Minor: new references, scripts, flow coverage, or reusable examples. Example: 1.1.0.\n- Major: breaking changes in skill behavior, trigger scope, file structure, or safety policy. Example: 2.0.0.\n\nAlways update the version field in SKILL.md before publishing to ClawHub.\n\n## Pre-Publish Validation\n\nRun:\n\n    python3 scripts/scan_skill_safety.py .\n    python3 scripts/validate_daraja_fixture.py stk-request path/to/request.json\n    python3 scripts/validate_daraja_fixture.py stk-callback path/to/callback.json\n\nOnly run fixture validation when fixture files exist. The safety scan should run every time.\n\n## Release Notes Template\n\n    mpesa-daraja vX.Y.Z\n\n    Changed:\n    - ...\n\n    Sources checked:\n    - Safaricom Daraja portal\n    - ...\n\n    Validation:\n    - scan_skill_safety.py\n    - fixture validation, if applicable\n\n    Notes:\n    - No production credentials or live payment calls used.\n\n## Publishing Rule\n\nDo 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.\n\nFile v1.2.1:references/production-readiness.md\n\n# Production Readiness\n\nUse this reference for launch reviews, client handoff, production-access preparation, or audits of a Daraja integration.\n\n## Hard Gates\n\n- Fresh user approval is required before calling production endpoints or triggering payment prompts, payouts, reversals, or customer-facing payment activity.\n- Real credentials must live in a secret manager or deployment environment, not in repository files.\n- Callback URLs must be public HTTPS endpoints owned by the deployment.\n- Fulfillment must be idempotent and coupled to confirmed payment state transitions.\n- Operators need a manual reconciliation path before production traffic.\n\n## Security\n\n- Mask phone numbers, access tokens, passkeys, initiator credentials, security credentials, receipt numbers, and raw callback payloads in logs.\n- Protect callback endpoints with rate limits, request size limits, and structured payload validation.\n- Keep raw callback persistence access-controlled and retention-limited.\n- Separate sandbox and production credentials and config.\n- Add a deny-by-default guard so tests cannot accidentally hit production.\n\n## Reliability\n\n- Cache OAuth tokens until shortly before expiry.\n- Retry only safe transient failures, such as timeouts, 429, and 5xx, according to a bounded policy.\n- Do not blindly retry validation errors or duplicate callbacks.\n- Alert on callback failures, stuck prompt_sent payments, repeated Daraja errors, and reconciliation mismatches.\n- Store enough correlation data to investigate without storing sensitive raw values in logs.\n\n## Data Model\n\nRecommended uniqueness constraints:\n\n- CheckoutRequestID\n- MerchantRequestID\n- MpesaReceiptNumber when present\n- Internal order/payment intent ID\n\nRecommended audit fields:\n\n- internal payment ID\n- Daraja request IDs\n- normalized state\n- masked phone number\n- amount and currency\n- callback received timestamp\n- reconciliation status\n- operator/manual-review notes\n\n## Launch Checklist\n\n- Official Daraja docs checked for current endpoints and fields.\n- Sandbox happy path and failure path tested.\n- Duplicate callback test passes.\n- Timeout/cancellation path tested.\n- Production config cannot be enabled in local/test by accident.\n- .env.example contains placeholders only.\n- Logs reviewed for secret and personal-data leakage.\n- Run scripts/scan_skill_safety.py on skill examples and project fixtures before sharing code.\n\nFile v1.2.1:references/stk-push.md\n\n# STK Push / Lipa na M-Pesa Online\n\nUse this reference when implementing, reviewing, or testing customer-initiated checkout through Daraja STK Push.\n\n## Official Source Check\n\nBefore implementation or publishing skill updates, verify the current endpoint names, field names, result codes, and sandbox behavior against official Safaricom Daraja documentation:\n\n- Daraja portal: https://developer.safaricom.co.ke/\n- STK Push / Lipa na M-Pesa Online docs in the Daraja portal\n- Safaricom developer release notes, notices, or portal announcements when available\n\nDo not copy secrets, access tokens, real phone numbers, receipt numbers, or production callback payloads from docs, dashboards, logs, or client systems into generated code.\n\n## Implementation Shape\n\nPrefer these boundaries:\n\n- MpesaClient: OAuth token, STK password, outbound request, response mapping.\n- PaymentIntent or Payment: internal state and idempotency keys.\n- PaymentService: creates intent, validates amount/order state, calls MpesaClient.\n- CallbackRoute: validates callback shape, stores raw event safely, returns fast.\n- ReconciliationJob: checks stale or ambiguous payments.\n\nKeep HTTP calls injectable so tests can mock Daraja without touching external endpoints.\n\n## Request Rules\n\n- Generate timestamp as YYYYMMDDHHmmss.\n- Generate STK password as base64(shortcode + passkey + timestamp).\n- Use CustomerPayBillOnline for PayBill flows unless the product intentionally supports a different Daraja-supported transaction type.\n- Normalize phone numbers only through a deliberate, tested formatter. Reject ambiguous or non-Kenyan MSISDN input.\n- Require HTTPS callback URLs outside local tests.\n- Treat MerchantRequestID and CheckoutRequestID as correlation identifiers, not proof of payment.\n\n## Callback Rules\n\n- Return HTTP 200 quickly after durable persistence.\n- Deduplicate by CheckoutRequestID; also store MerchantRequestID and receipt number when present.\n- Successful callbacks must not double-credit on retries.\n- Failed callbacks may omit CallbackMetadata; do not assume it exists.\n- Mask PhoneNumber, access tokens, passkeys, and security credentials in logs.\n- Map known ResultCodes into explicit internal states, but preserve unknown codes for manual review.\n\n## State Mapping\n\nRecommended internal states:\n\n- pending: internal payment intent exists.\n- prompt_sent: Daraja accepted the STK request.\n- paid: successful callback with receipt metadata.\n- failed: clear unsuccessful callback.\n- cancelled: user cancellation, commonly ResultCode 1032.\n- expired: timeout or no response after the product-defined window.\n- manual_review: ambiguous response, unknown result, duplicate conflict, or reconciliation mismatch.\n- reversed: confirmed reversal after operator-approved or compliant reversal flow.\n\n## Review Checklist\n\n- Credentials are environment-backed and never committed.\n- Production endpoint usage requires explicit opt-in.\n- Token caching respects expiry and handles 401 with one safe refresh retry.\n- Checkout creation and callback processing are transactionally safe.\n- Callback handler has shape validation, idempotency, masked logging, and durable event storage.\n- Tests cover success, cancellation, timeout, duplicate callback, malformed callback, and Daraja outage.\n\nFile v1.2.1:references/test-cases.md\n\n# M-Pesa Daraja Test Cases\n\n## Unit Tests\n\n- OAuth token client builds the correct Basic Auth header from consumer key and secret.\n- OAuth token client never logs consumer secret, bearer token, passkey, or security credential.\n- STK password generation returns base64(shortcode + passkey + timestamp).\n- Timestamp uses Daraja format YYYYMMDDHHmmss and is generated in a consistent timezone.\n- STK request rejects non-positive amount values.\n- STK request rejects invalid MSISDN format; normalize local 07xx numbers to 2547xx only when the product explicitly allows it.\n- STK request uses PartyB equal to the configured shortcode.\n- STK request requires an HTTPS callback URL outside local unit tests.\n- Daraja client maps network timeout to retryable internal error.\n- Daraja client maps Daraja ResponseCode/ResultCode into explicit internal states.\n- Callback parser extracts Amount, MpesaReceiptNumber, TransactionDate, and PhoneNumber from CallbackMetadata when ResultCode is 0.\n- Callback parser handles failed callbacks that do not include CallbackMetadata.\n- Callback parser rejects payloads missing Body.stkCallback.\n- Callback handler masks phone numbers in logs.\n- Callback handler is idempotent for duplicate CheckoutRequestID.\n\n## Integration Tests With HTTP Mocking\n\n- Token endpoint returns access_token and expires_in; client caches token and reuses it before expiry.\n- Token refresh happens when cached token is near expiry.\n- STK Push request sends Authorization: Bearer access_token.\n- STK Push request includes BusinessShortCode, Password, Timestamp, TransactionType, Amount, PartyA, PartyB, PhoneNumber, CallBackURL, AccountReference, and TransactionDesc.\n- Daraja 401 causes one token refresh and one retry, then fails cleanly if still unauthorized.\n- Daraja 429 or 5xx is retried according to the app's retry policy.\n- Daraja validation error is not retried.\n- Callback route persists raw callback once and returns HTTP 200 quickly.\n- Duplicate successful callback does not double-credit an order.\n- Failed callback changes payment state to failed/cancelled/expired according to ResultCode mapping.\n\n## Sandbox End-to-End Tests\n\n- Start checkout for a sandbox order and verify a MerchantRequestID and CheckoutRequestID are stored.\n- Simulate a successful callback and verify order/payment state becomes paid.\n- Simulate user cancellation ResultCode 1032 and verify state becomes cancelled.\n- Simulate timeout/expiry and verify state becomes expired or manual_review.\n- Verify reconciliation job can find stale prompt_sent payments.\n- Verify public callback URL is reachable over HTTPS from outside the local machine.\n- Verify logs contain internal order IDs and Daraja request IDs but no access token, passkey, security credential, or full phone number.\n\n## Production Readiness Checks\n\n- Production base URL is gated by explicit environment configuration and cannot be enabled accidentally in tests.\n- All real credentials are loaded from secure environment or secret manager, not committed files.\n- Callback endpoint has rate limiting or upstream protection appropriate for the deployment.\n- Database has uniqueness constraints for CheckoutRequestID, MerchantRequestID, and MpesaReceiptNumber where applicable.\n- Payment fulfillment is idempotent and transactionally coupled to payment-state changes.\n- Reversal and refund processes require explicit operator approval unless the user has built a compliant automated workflow.\n- Monitoring alerts on repeated Daraja failures, callback failures, and stuck pending payments.\n- Manual reconciliation path exists for ambiguous states.\n\n## Example Pytest Names\n\n- test_stk_password_uses_shortcode_passkey_timestamp\n- test_stk_request_requires_https_callback_url\n- test_token_client_masks_secrets_in_logs\n- test_success_callback_extracts_receipt_and_amount\n- test_failed_callback_without_metadata_is_handled\n- test_duplicate_callback_does_not_double_credit_order\n- test_unauthorized_response_refreshes_token_once\n- test_production_endpoint_requires_explicit_opt_in\n\nFile v1.2.1:agents/openai.yaml\n\ninterface:\n  display_name: \"M-Pesa Daraja\"\n  short_description: \"Build and test Safaricom M-Pesa Daraja integrations.\"\n  default_prompt: \"Use the M-Pesa Daraja skill to design, implement, or test an M-Pesa payment integration safely.\"\n\nArchive v1.2.0: 11 files, 14956 bytes\n\nFiles: agents/openai.yaml (235b), references/api-endpoints.md (2298b), references/examples.md (3955b), references/maintenance.md (1999b), references/production-readiness.md (2397b), references/stk-push.md (3269b), references/test-cases.md (4031b), scripts/scan_skill_safety.py (2621b), scripts/validate_daraja_fixture.py (3384b), SKILL.md (6241b), _meta.json (131b)\n\nFile v1.2.0:SKILL.md\n\n---\nname: mpesa-daraja\ndescription: Build, review, test, or explain Safaricom M-Pesa Daraja API integrations for STK Push/Lipa na M-Pesa Online, C2B validation and confirmation, B2C payouts, transaction status, reversals, callbacks, credential setup, sandbox testing, and production-readiness. Use when Codex needs to create implementation plans, code, examples, mocks, test cases, troubleshooting steps, or security checks for M-Pesa payments in OpenClaw or client applications.\nversion: 1.2.0\nmetadata:\n  openclaw:\n    envVars:\n      - name: MPESA_ENV\n        required: false\n        description: Optional Daraja environment selector, usually sandbox or production.\n      - name: MPESA_CONSUMER_KEY\n        required: false\n        description: Optional Daraja API consumer key for implementation examples.\n      - name: MPESA_CONSUMER_SECRET\n        required: false\n        description: Optional Daraja API consumer secret for implementation examples.\n      - name: MPESA_SHORTCODE\n        required: false\n        description: Optional Paybill or Till shortcode for Daraja examples.\n      - name: MPESA_PASSKEY\n        required: false\n        description: Optional STK Push passkey for Daraja examples.\n      - name: MPESA_CALLBACK_URL\n        required: false\n        description: Optional public HTTPS callback URL for Daraja sandbox or production testing.\n      - name: MPESA_INITIATOR_NAME\n        required: false\n        description: Optional Daraja initiator name for B2C, reversal, and transaction status flows.\n      - name: MPESA_SECURITY_CREDENTIAL\n        required: false\n        description: Optional encrypted security credential for B2C, reversal, and transaction status flows.\n---\n\n# M-Pesa Daraja\n\n## Operating Rules\n\n- Treat this as a payment-integration skill, not permission to move money.\n- Default to Daraja sandbox endpoints and test credentials unless the user explicitly asks for production guidance.\n- Never store consumer secrets, passkeys, initiator passwords, security credentials, access tokens, callback payloads with phone numbers, or transaction IDs in committed files.\n- Ask before calling any live/production endpoint or sending any request that could trigger a payment prompt, payout, reversal, or customer-facing callback.\n- Prefer environment variables and secret managers over \\`.env\\` files in shared repos. If \\`.env.example\\` is needed, use placeholder values only.\n- Make callbacks idempotent: verify request shape, persist raw event safely, deduplicate by CheckoutRequestID/ConversationID/TransactionID, then process business state transitions.\n\n## Workflow\n\n1. Identify the payment flow:\n   - **STK Push / Lipa na M-Pesa Online** for customer-initiated checkout.\n   - **C2B** for paybill/till customer payments with validation and confirmation callbacks.\n   - **B2C** for business payouts to customers or agents.\n   - **Transaction Status / Reversal** for reconciliation and recovery.\n2. Confirm runtime context: language/framework, sandbox vs production, callback URL availability, persistence layer, and compliance constraints.\n3. Design the integration around these boundaries:\n   - token generation and caching\n   - request signing/password generation\n   - outbound Daraja client\n   - callback receiver\n   - idempotency/reconciliation\n   - observability without leaking personal or payment data\n4. Use \\`references/examples.md\\` when the user asks for sample OpenClaw prompts, implementation examples, or architecture snippets.\n5. Use \\`references/test-cases.md\\` when the user asks for QA scenarios, unit tests, integration tests, sandbox tests, or production-readiness checks.\n6. Use \\`references/stk-push.md\\` for STK Push/Lipa na M-Pesa Online implementation details.\n7. Use \\`references/api-endpoints.md\\` when the user needs current Daraja endpoint names, versions, or flow coverage.\n8. Use \\`references/production-readiness.md\\` before production-readiness reviews, client handoff, or launch checklists.\n9. 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.\n10. Use \\`scripts/validate_daraja_fixture.py\\` to sanity-check example STK Push request and callback JSON fixtures before writing tests around them.\n11. Use \\`scripts/scan_skill_safety.py\\` before publishing or sharing this skill to detect real-looking secrets, live tokens, and unsafe examples.\n\n## Implementation Guidance\n\n- Keep Daraja client code small and injectable so app tests can mock HTTP calls.\n- Generate the STK password as \\`base64(shortcode + passkey + timestamp)\\` with timestamp format \\`YYYYMMDDHHmmss\\`.\n- Use OAuth access tokens from Daraja and cache them until shortly before expiry.\n- Expose public HTTPS callback URLs in sandbox using a stable tunnel or deployed test environment; avoid local-only callback URLs.\n- Return fast from callbacks after durable persistence; do heavier fulfillment asynchronously when possible.\n- Log correlation IDs, response codes, and internal order IDs. Mask phone numbers, names, access tokens, credentials, and raw security credentials.\n- Model payment state explicitly, for example: \\`pending\\`, \\`prompt_sent\\`, \\`paid\\`, \\`failed\\`, \\`cancelled\\`, \\`expired\\`, \\`reversed\\`, \\`manual_review\\`.\n\n## Common OpenClaw Requests\n\n- \"Use $mpesa-daraja to add STK Push checkout to this Django app.\"\n- \"Use $mpesa-daraja to write sandbox test cases for our M-Pesa checkout.\"\n- \"Use $mpesa-daraja to review this Daraja callback handler for idempotency and secret leakage.\"\n- \"Use $mpesa-daraja to create an OpenClaw automation that drafts M-Pesa reconciliation reports without touching production APIs.\"\n\n## Deliverables To Prefer\n\n- For implementation: client module, callback route, typed request/response models, masked logging, tests, and \\`.env.example\\`.\n- For review: findings ordered by payment risk, security risk, idempotency/reconciliation risk, then code quality.\n- For planning: flow diagram in text, endpoint list, secret list, callback contract, test plan, and production checklist.\n- For troubleshooting: exact failing step, expected Daraja response, likely causes, and the smallest safe verification step.\n\nFile v1.2.0:_meta.json\n\n{\n  \"ownerId\": \"kn72x5vtcasyn6bm1yyg4xqv0s86rdry\",\n  \"slug\": \"mpesa-daraja\",\n  \"version\": \"1.2.0\",\n  \"publishedAt\": 1779694957433\n}\n\nFile v1.2.0:references/api-endpoints.md\n\n# Daraja Endpoint Reference\n\nUse 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.\n\n## Official Source Snapshot\n\nChecked on 2026-05-25 against:\n\n- Safaricom Daraja developer portal: https://developer.safaricom.co.ke/\n- Safaricom developer API catalogue GraphQL query \\`getApis\\` via https://developer.safaricom.co.ke/api/graphql\n- Portal documentation records through \\`populateApi\\` for STK Push, STK Query, Transaction Status, Reversal, and Account Balance\n\nThe 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.\n\n## Core Sandbox Endpoints\n\n| Flow | Method | Sandbox endpoint |\n| --- | --- | --- |\n| OAuth authorization | GET | \\`https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials\\` |\n| STK Push / Lipa na M-Pesa Online | POST | \\`https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest\\` |\n| STK Push query | POST | \\`https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query\\` |\n| C2B register URL | POST | \\`https://sandbox.safaricom.co.ke/mpesa/c2b/v2/registerurl\\` |\n| B2C payment request | POST | \\`https://sandbox.safaricom.co.ke/mpesa/b2c/v3/paymentrequest\\` |\n| Transaction status | POST | \\`https://sandbox.safaricom.co.ke/mpesa/transactionstatus/v1/query\\` |\n| Account balance | POST | \\`https://sandbox.safaricom.co.ke/mpesa/accountbalance/v1/query\\` |\n| Reversal | POST | \\`https://sandbox.safaricom.co.ke/mpesa/reversal/v1/request\\` |\n\n## Guidance\n\n- Use sandbox endpoints by default. Switch to production hostnames only when the user explicitly asks for production guidance, and ask before any live call.\n- Re-check the official catalogue before writing production instructions. Endpoint versions can differ from older examples and SDK snippets.\n- 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.\n- If the catalogue and a detailed documentation page disagree, report the disagreement and avoid claiming a migration is required without confirmation from Safaricom.\n\nFile v1.2.0:references/examples.md\n\n# M-Pesa Daraja Examples\n\n## OpenClaw Usage Examples\n\nUse these as realistic prompts for another OpenClaw/Codex session:\n\n- \"Use $mpesa-daraja to add sandbox STK Push checkout to my FastAPI app. Keep all credentials in env vars and add tests for token generation, password generation, outbound request shape, and callback idempotency.\"\n- \"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.\"\n- \"Use $mpesa-daraja to create a Django model and service layer for tracking M-Pesa payments from pending to paid/failed/reversed.\"\n- \"Use $mpesa-daraja to draft a client implementation plan for Paybill C2B validation and confirmation callbacks.\"\n- \"Use $mpesa-daraja to generate QA test cases for STK Push before we request production access.\"\n\n## Minimal STK Push Architecture\n\nRecommended components:\n\n- MpesaClient: handles OAuth, password generation, and Daraja HTTP requests.\n- PaymentService: creates internal payment intent records and calls MpesaClient.\n- CallbackController: receives Daraja callbacks, validates payload shape, persists raw event, and invokes reconciliation.\n- PaymentRepository: enforces idempotency with unique CheckoutRequestID/MerchantRequestID/TransactionID constraints.\n- ReconciliationJob: checks pending/unclear payments using transaction status or internal review queues.\n\nState flow:\n\n    order_created -> payment_pending -> stk_prompt_sent -> paid\n                                          |-> failed\n                                          |-> cancelled\n                                          |-> expired\n                                          |-> manual_review\n    paid -> reversed\n\n## Environment Variables\n\nUse placeholders in examples:\n\n    MPESA_ENV=sandbox\n    MPESA_CONSUMER_KEY=replace-me\n    MPESA_CONSUMER_SECRET=replace-me\n    MPESA_SHORTCODE=174379\n    MPESA_PASSKEY=replace-me\n    MPESA_CALLBACK_URL=https://example.test/api/mpesa/stk-callback\n    MPESA_INITIATOR_NAME=replace-me\n    MPESA_SECURITY_CREDENTIAL=replace-me\n\nDo not commit real values.\n\n## Python Password Generation Example\n\n    import base64\n    from datetime import datetime, timezone\n\n\n    def daraja_timestamp() -> str:\n        return datetime.now(timezone.utc).strftime(\"%Y%m%d%H%M%S\")\n\n\n    def stk_password(shortcode: str, passkey: str, timestamp: str) -> str:\n        raw = f\"{shortcode}{passkey}{timestamp}\".encode(\"utf-8\")\n        return base64.b64encode(raw).decode(\"utf-8\")\n\n## STK Push Request Shape\n\n    {\n      \"BusinessShortCode\": \"174379\",\n      \"Password\": \"base64-shortcode-passkey-timestamp\",\n      \"Timestamp\": \"20260520101630\",\n      \"TransactionType\": \"CustomerPayBillOnline\",\n      \"Amount\": 100,\n      \"PartyA\": \"254708374149\",\n      \"PartyB\": \"174379\",\n      \"PhoneNumber\": \"254708374149\",\n      \"CallBackURL\": \"https://example.test/api/mpesa/stk-callback\",\n      \"AccountReference\": \"ORDER-1001\",\n      \"TransactionDesc\": \"Order payment\"\n    }\n\n## Successful Callback Shape\n\n    {\n      \"Body\": {\n        \"stkCallback\": {\n          \"MerchantRequestID\": \"29115-34620561-1\",\n          \"CheckoutRequestID\": \"ws_CO_20052026101630000000\",\n          \"ResultCode\": 0,\n          \"ResultDesc\": \"The service request is processed successfully.\",\n          \"CallbackMetadata\": {\n            \"Item\": [\n              { \"Name\": \"Amount\", \"Value\": 100 },\n              { \"Name\": \"MpesaReceiptNumber\", \"Value\": \"TEK7X12345\" },\n              { \"Name\": \"TransactionDate\", \"Value\": 20260520101701 },\n              { \"Name\": \"PhoneNumber\", \"Value\": 254708374149 }\n            ]\n          }\n        }\n      }\n    }\n\n## Failed Callback Shape\n\n    {\n      \"Body\": {\n        \"stkCallback\": {\n          \"MerchantRequestID\": \"29115-34620561-2\",\n          \"CheckoutRequestID\": \"ws_CO_20052026102030000000\",\n          \"ResultCode\": 1032,\n          \"ResultDesc\": \"Request cancelled by user\"\n        }\n      }\n    }\n\nFile v1.2.0:references/maintenance.md\n\n# Skill Maintenance\n\nUse this reference when updating mpesa-daraja or preparing a ClawHub release.\n\n## Source Update Check\n\nBefore changing examples, production guidance, endpoint assumptions, or publishing a new version, check current official sources:\n\n- Safaricom Daraja developer portal: https://developer.safaricom.co.ke/\n- The specific Daraja API page for the flow being updated: STK Push, C2B, B2C, Transaction Status, Reversal, or Account Balance.\n- Safaricom developer announcements or release notes when available.\n\nPrefer 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.\n\n## Versioning\n\nUse semantic versioning:\n\n- Patch: typo fixes, safer wording, small test fixture updates. Example: 1.0.1.\n- Minor: new references, scripts, flow coverage, or reusable examples. Example: 1.1.0.\n- Major: breaking changes in skill behavior, trigger scope, file structure, or safety policy. Example: 2.0.0.\n\nAlways update the version field in SKILL.md before publishing to ClawHub.\n\n## Pre-Publish Validation\n\nRun:\n\n    python3 scripts/scan_skill_safety.py .\n    python3 scripts/validate_daraja_fixture.py stk-request path/to/request.json\n    python3 scripts/validate_daraja_fixture.py stk-callback path/to/callback.json\n\nOnly run fixture validation when fixture files exist. The safety scan should run every time.\n\n## Release Notes Template\n\n    mpesa-daraja vX.Y.Z\n\n    Changed:\n    - ...\n\n    Sources checked:\n    - Safaricom Daraja portal\n    - ...\n\n    Validation:\n    - scan_skill_safety.py\n    - fixture validation, if applicable\n\n    Notes:\n    - No production credentials or live payment calls used.\n\n## Publishing Rule\n\nDo 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.\n\nFile v1.2.0:references/production-readiness.md\n\n# Production Readiness\n\nUse this reference for launch reviews, client handoff, production-access preparation, or audits of a Daraja integration.\n\n## Hard Gates\n\n- Fresh user approval is required before calling production endpoints or triggering payment prompts, payouts, reversals, or customer-facing payment activity.\n- Real credentials must live in a secret manager or deployment environment, not in repository files.\n- Callback URLs must be public HTTPS endpoints owned by the deployment.\n- Fulfillment must be idempotent and coupled to confirmed payment state transitions.\n- Operators need a manual reconciliation path before production traffic.\n\n## Security\n\n- Mask phone numbers, access tokens, passkeys, initiator credentials, security credentials, receipt numbers, and raw callback payloads in logs.\n- Protect callback endpoints with rate limits, request size limits, and structured payload validation.\n- Keep raw callback persistence access-controlled and retention-limited.\n- Separate sandbox and production credentials and config.\n- Add a deny-by-default guard so tests cannot accidentally hit production.\n\n## Reliability\n\n- Cache OAuth tokens until shortly before expiry.\n- Retry only safe transient failures, such as timeouts, 429, and 5xx, according to a bounded policy.\n- Do not blindly retry validation errors or duplicate callbacks.\n- Alert on callback failures, stuck prompt_sent payments, repeated Daraja errors, and reconciliation mismatches.\n- Store enough correlation data to investigate without storing sensitive raw values in logs.\n\n## Data Model\n\nRecommended uniqueness constraints:\n\n- CheckoutRequestID\n- MerchantRequestID\n- MpesaReceiptNumber when present\n- Internal order/payment intent ID\n\nRecommended audit fields:\n\n- internal payment ID\n- Daraja request IDs\n- normalized state\n- masked phone number\n- amount and currency\n- callback received timestamp\n- reconciliation status\n- operator/manual-review notes\n\n## Launch Checklist\n\n- Official Daraja docs checked for current endpoints and fields.\n- Sandbox happy path and failure path tested.\n- Duplicate callback test passes.\n- Timeout/cancellation path tested.\n- Production config cannot be enabled in local/test by accident.\n- .env.example contains placeholders only.\n- Logs reviewed for secret and personal-data leakage.\n- Run scripts/scan_skill_safety.py on skill examples and project fixtures before sharing code.\n\nFile v1.2.0:references/stk-push.md\n\n# STK Push / Lipa na M-Pesa Online\n\nUse this reference when implementing, reviewing, or testing customer-initiated checkout through Daraja STK Push.\n\n## Official Source Check\n\nBefore implementation or publishing skill updates, verify the current endpoint names, field names, result codes, and sandbox behavior against official Safaricom Daraja documentation:\n\n- Daraja portal: https://developer.safaricom.co.ke/\n- STK Push / Lipa na M-Pesa Online docs in the Daraja portal\n- Safaricom developer release notes, notices, or portal announcements when available\n\nDo not copy secrets, access tokens, real phone numbers, receipt numbers, or production callback payloads from docs, dashboards, logs, or client systems into generated code.\n\n## Implementation Shape\n\nPrefer these boundaries:\n\n- MpesaClient: OAuth token, STK password, outbound request, response mapping.\n- PaymentIntent or Payment: internal state and idempotency keys.\n- PaymentService: creates intent, validates amount/order state, calls MpesaClient.\n- CallbackRoute: validates callback shape, stores raw event safely, returns fast.\n- ReconciliationJob: checks stale or ambiguous payments.\n\nKeep HTTP calls injectable so tests can mock Daraja without touching external endpoints.\n\n## Request Rules\n\n- Generate timestamp as YYYYMMDDHHmmss.\n- Generate STK password as base64(shortcode + passkey + timestamp).\n- Use CustomerPayBillOnline for PayBill flows unless the product intentionally supports a different Daraja-supported transaction type.\n- Normalize phone numbers only through a deliberate, tested formatter. Reject ambiguous or non-Kenyan MSISDN input.\n- Require HTTPS callback URLs outside local tests.\n- Treat MerchantRequestID and CheckoutRequestID as correlation identifiers, not proof of payment.\n\n## Callback Rules\n\n- Return HTTP 200 quickly after durable persistence.\n- Deduplicate by CheckoutRequestID; also store MerchantRequestID and receipt number when present.\n- Successful callbacks must not double-credit on retries.\n- Failed callbacks may omit CallbackMetadata; do not assume it exists.\n- Mask PhoneNumber, access tokens, passkeys, and security credentials in logs.\n- Map known ResultCodes into explicit internal states, but preserve unknown codes for manual review.\n\n## State Mapping\n\nRecommended internal states:\n\n- pending: internal payment intent exists.\n- prompt_sent: Daraja accepted the STK request.\n- paid: successful callback with receipt metadata.\n- failed: clear unsuccessful callback.\n- cancelled: user cancellation, commonly ResultCode 1032.\n- expired: timeout or no response after the product-defined window.\n- manual_review: ambiguous response, unknown result, duplicate conflict, or reconciliation mismatch.\n- reversed: confirmed reversal after operator-approved or compliant reversal flow.\n\n## Review Checklist\n\n- Credentials are environment-backed and never committed.\n- Production endpoint usage requires explicit opt-in.\n- Token caching respects expiry and handles 401 with one safe refresh retry.\n- Checkout creation and callback processing are transactionally safe.\n- Callback handler has shape validation, idempotency, masked logging, and durable event storage.\n- Tests cover success, cancellation, timeout, duplicate callback, malformed callback, and Daraja outage.\n\nFile v1.2.0:references/test-cases.md\n\n# M-Pesa Daraja Test Cases\n\n## Unit Tests\n\n- OAuth token client builds the correct Basic Auth header from consumer key and secret.\n- OAuth token client never logs consumer secret, bearer token, passkey, or security credential.\n- STK password generation returns base64(shortcode + passkey + timestamp).\n- Timestamp uses Daraja format YYYYMMDDHHmmss and is generated in a consistent timezone.\n- STK request rejects non-positive amount values.\n- STK request rejects invalid MSISDN format; normalize local 07xx numbers to 2547xx only when the product explicitly allows it.\n- STK request uses PartyB equal to the configured shortcode.\n- STK request requires an HTTPS callback URL outside local unit tests.\n- Daraja client maps network timeout to retryable internal error.\n- Daraja client maps Daraja ResponseCode/ResultCode into explicit internal states.\n- Callback parser extracts Amount, MpesaReceiptNumber, TransactionDate, and PhoneNumber from CallbackMetadata when ResultCode is 0.\n- Callback parser handles failed callbacks that do not include CallbackMetadata.\n- Callback parser rejects payloads missing Body.stkCallback.\n- Callback handler masks phone numbers in logs.\n- Callback handler is idempotent for duplicate CheckoutRequestID.\n\n## Integration Tests With HTTP Mocking\n\n- Token endpoint returns access_token and expires_in; client caches token and reuses it before expiry.\n- Token refresh happens when cached token is near expiry.\n- STK Push request sends Authorization: Bearer access_token.\n- STK Push request includes BusinessShortCode, Password, Timestamp, TransactionType, Amount, PartyA, PartyB, PhoneNumber, CallBackURL, AccountReference, and TransactionDesc.\n- Daraja 401 causes one token refresh and one retry, then fails cleanly if still unauthorized.\n- Daraja 429 or 5xx is retried according to the app's retry policy.\n- Daraja validation error is not retried.\n- Callback route persists raw callback once and returns HTTP 200 quickly.\n- Duplicate successful callback does not double-credit an order.\n- Failed callback changes payment state to failed/cancelled/expired according to ResultCode mapping.\n\n## Sandbox End-to-End Tests\n\n- Start checkout for a sandbox order and verify a MerchantRequestID and CheckoutRequestID are stored.\n- Simulate a successful callback and verify order/payment state becomes paid.\n- Simulate user cancellation ResultCode 1032 and verify state becomes cancelled.\n- Simulate timeout/expiry and verify state becomes expired or manual_review.\n- Verify reconciliation job can find stale prompt_sent payments.\n- Verify public callback URL is reachable over HTTPS from outside the local machine.\n- Verify logs contain internal order IDs and Daraja request IDs but no access token, passkey, security credential, or full phone number.\n\n## Production Readiness Checks\n\n- Production base URL is gated by explicit environment configuration and cannot be enabled accidentally in tests.\n- All real credentials are loaded from secure environment or secret manager, not committed files.\n- Callback endpoint has rate limiting or upstream protection appropriate for the deployment.\n- Database has uniqueness constraints for CheckoutRequestID, MerchantRequestID, and MpesaReceiptNumber where applicable.\n- Payment fulfillment is idempotent and transactionally coupled to payment-state changes.\n- Reversal and refund processes require explicit operator approval unless the user has built a compliant automated workflow.\n- Monitoring alerts on repeated Daraja failures, callback failures, and stuck pending payments.\n- Manual reconciliation path exists for ambiguous states.\n\n## Example Pytest Names\n\n- test_stk_password_uses_shortcode_passkey_timestamp\n- test_stk_request_requires_https_callback_url\n- test_token_client_masks_secrets_in_logs\n- test_success_callback_extracts_receipt_and_amount\n- test_failed_callback_without_metadata_is_handled\n- test_duplicate_callback_does_not_double_credit_order\n- test_unauthorized_response_refreshes_token_once\n- test_production_endpoint_requires_explicit_opt_in\n\nFile v1.2.0:agents/openai.yaml\n\ninterface:\n  display_name: \"M-Pesa Daraja\"\n  short_description: \"Build and test Safaricom M-Pesa Daraja integrations.\"\n  default_prompt: \"Use the M-Pesa Daraja skill to design, implement, or test an M-Pesa payment integration safely.\"\n\nArchive v1.1.0: 10 files, 13757 bytes\n\nFiles: agents/openai.yaml (235b), references/examples.md (3955b), references/maintenance.md (1999b), references/production-readiness.md (2397b), references/stk-push.md (3269b), references/test-cases.md (4031b), scripts/scan_skill_safety.py (2621b), scripts/validate_daraja_fixture.py (3384b), SKILL.md (6122b), _meta.json (131b)\n\nFile v1.1.0:SKILL.md\n\n---\nname: mpesa-daraja\ndescription: Build, review, test, or explain Safaricom M-Pesa Daraja API integrations for STK Push/Lipa na M-Pesa Online, C2B validation and confirmation, B2C payouts, transaction status, reversals, callbacks, credential setup, sandbox testing, and production-readiness. Use when Codex needs to create implementation plans, code, examples, mocks, test cases, troubleshooting steps, or security checks for M-Pesa payments in OpenClaw or client applications.\nversion: 1.1.0\nmetadata:\n  openclaw:\n    envVars:\n      - name: MPESA_ENV\n        required: false\n        description: Optional Daraja environment selector, usually sandbox or production.\n      - name: MPESA_CONSUMER_KEY\n        required: false\n        description: Optional Daraja API consumer key for implementation examples.\n      - name: MPESA_CONSUMER_SECRET\n        required: false\n        description: Optional Daraja API consumer secret for implementation examples.\n      - name: MPESA_SHORTCODE\n        required: false\n        description: Optional Paybill or Till shortcode for Daraja examples.\n      - name: MPESA_PASSKEY\n        required: false\n        description: Optional STK Push passkey for Daraja examples.\n      - name: MPESA_CALLBACK_URL\n        required: false\n        description: Optional public HTTPS callback URL for Daraja sandbox or production testing.\n      - name: MPESA_INITIATOR_NAME\n        required: false\n        description: Optional Daraja initiator name for B2C, reversal, and transaction status flows.\n      - name: MPESA_SECURITY_CREDENTIAL\n        required: false\n        description: Optional encrypted security credential for B2C, reversal, and transaction status flows.\n---\n\n# M-Pesa Daraja\n\n## Operating Rules\n\n- Treat this as a payment-integration skill, not permission to move money.\n- Default to Daraja sandbox endpoints and test credentials unless the user explicitly asks for production guidance.\n- Never store consumer secrets, passkeys, initiator passwords, security credentials, access tokens, callback payloads with phone numbers, or transaction IDs in committed files.\n- Ask before calling any live/production endpoint or sending any request that could trigger a payment prompt, payout, reversal, or customer-facing callback.\n- Prefer environment variables and secret managers over \\`.env\\` files in shared repos. If \\`.env.example\\` is needed, use placeholder values only.\n- Make callbacks idempotent: verify request shape, persist raw event safely, deduplicate by CheckoutRequestID/ConversationID/TransactionID, then process business state transitions.\n\n## Workflow\n\n1. Identify the payment flow:\n   - **STK Push / Lipa na M-Pesa Online** for customer-initiated checkout.\n   - **C2B** for paybill/till customer payments with validation and confirmation callbacks.\n   - **B2C** for business payouts to customers or agents.\n   - **Transaction Status / Reversal** for reconciliation and recovery.\n2. Confirm runtime context: language/framework, sandbox vs production, callback URL availability, persistence layer, and compliance constraints.\n3. Design the integration around these boundaries:\n   - token generation and caching\n   - request signing/password generation\n   - outbound Daraja client\n   - callback receiver\n   - idempotency/reconciliation\n   - observability without leaking personal or payment data\n4. Use \\`references/examples.md\\` when the user asks for sample OpenClaw prompts, implementation examples, or architecture snippets.\n5. Use \\`references/test-cases.md\\` when the user asks for QA scenarios, unit tests, integration tests, sandbox tests, or production-readiness checks.\n6. Use \\`references/stk-push.md\\` for STK Push/Lipa na M-Pesa Online implementation details.\n7. Use \\`references/production-readiness.md\\` before production-readiness reviews, client handoff, or launch checklists.\n8. 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.\n9. Use \\`scripts/validate_daraja_fixture.py\\` to sanity-check example STK Push request and callback JSON fixtures before writing tests around them.\n10. Use \\`scripts/scan_skill_safety.py\\` before publishing or sharing this skill to detect real-looking secrets, live tokens, and unsafe examples.\n\n## Implementation Guidance\n\n- Keep Daraja client code small and injectable so app tests can mock HTTP calls.\n- Generate the STK password as \\`base64(shortcode + passkey + timestamp)\\` with timestamp format \\`YYYYMMDDHHmmss\\`.\n- Use OAuth access tokens from Daraja and cache them until shortly before expiry.\n- Expose public HTTPS callback URLs in sandbox using a stable tunnel or deployed test environment; avoid local-only callback URLs.\n- Return fast from callbacks after durable persistence; do heavier fulfillment asynchronously when possible.\n- Log correlation IDs, response codes, and internal order IDs. Mask phone numbers, names, access tokens, credentials, and raw security credentials.\n- Model payment state explicitly, for example: \\`pending\\`, \\`prompt_sent\\`, \\`paid\\`, \\`failed\\`, \\`cancelled\\`, \\`expired\\`, \\`reversed\\`, \\`manual_review\\`.\n\n## Common OpenClaw Requests\n\n- \"Use $mpesa-daraja to add STK Push checkout to this Django app.\"\n- \"Use $mpesa-daraja to write sandbox test cases for our M-Pesa checkout.\"\n- \"Use $mpesa-daraja to review this Daraja callback handler for idempotency and secret leakage.\"\n- \"Use $mpesa-daraja to create an OpenClaw automation that drafts M-Pesa reconciliation reports without touching production APIs.\"\n\n## Deliverables To Prefer\n\n- For implementation: client module, callback route, typed request/response models, masked logging, tests, and \\`.env.example\\`.\n- For review: findings ordered by payment risk, security risk, idempotency/reconciliation risk, then code quality.\n- For planning: flow diagram in text, endpoint list, secret list, callback contract, test plan, and production checklist.\n- For troubleshooting: exact failing step, expected Daraja response, likely causes, and the smallest safe verification step.\n\nFile v1.1.0:_meta.json\n\n{\n  \"ownerId\": \"kn72x5vtcasyn6bm1yyg4xqv0s86rdry\",\n  \"slug\": \"mpesa-daraja\",\n  \"version\": \"1.1.0\",\n  \"publishedAt\": 1779354851592\n}\n\nFile v1.1.0:references/examples.md\n\n# M-Pesa Daraja Examples\n\n## OpenClaw Usage Examples\n\nUse these as realistic prompts for another OpenClaw/Codex session:\n\n- \"Use $mpesa-daraja to add sandbox STK Push checkout to my FastAPI app. Keep all credentials in env vars and add tests for token generation, password generation, outbound request shape, and callback idempotency.\"\n- \"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.\"\n- \"Use $mpesa-daraja to create a Django model and service layer for tracking M-Pesa payments from pending to paid/failed/reversed.\"\n- \"Use $mpesa-daraja to draft a client implementation plan for Paybill C2B validation and confirmation callbacks.\"\n- \"Use $mpesa-daraja to generate QA test cases for STK Push before we request production access.\"\n\n## Minimal STK Push Architecture\n\nRecommended components:\n\n- MpesaClient: handles OAuth, password generation, and Daraja HTTP requests.\n- PaymentService: creates internal payment intent records and calls MpesaClient.\n- CallbackController: receives Daraja callbacks, validates payload shape, persists raw event, and invokes reconciliation.\n- PaymentRepository: enforces idempotency with unique CheckoutRequestID/MerchantRequestID/TransactionID constraints.\n- ReconciliationJob: checks pending/unclear payments using transaction status or internal review queues.\n\nState flow:\n\n    order_created -> payment_pending -> stk_prompt_sent -> paid\n                                          |-> failed\n                                          |-> cancelled\n                                          |-> expired\n                                          |-> manual_review\n    paid -> reversed\n\n## Environment Variables\n\nUse placeholders in examples:\n\n    MPESA_ENV=sandbox\n    MPESA_CONSUMER_KEY=replace-me\n    MPESA_CONSUMER_SECRET=replace-me\n    MPESA_SHORTCODE=174379\n    MPESA_PASSKEY=replace-me\n    MPESA_CALLBACK_URL=https://example.test/api/mpesa/stk-callback\n    MPESA_INITIATOR_NAME=replace-me\n    MPESA_SECURITY_CREDENTIAL=replace-me\n\nDo not commit real values.\n\n## Python Password Generation Example\n\n    import base64\n    from datetime import datetime, timezone\n\n\n    def daraja_timestamp() -> str:\n        return datetime.now(timezone.utc).strftime(\"%Y%m%d%H%M%S\")\n\n\n    def stk_password(shortcode: str, passkey: str, timestamp: str) -> str:\n        raw = f\"{shortcode}{passkey}{timestamp}\".encode(\"utf-8\")\n        return base64.b64encode(raw).decode(\"utf-8\")\n\n## STK Push Request Shape\n\n    {\n      \"BusinessShortCode\": \"174379\",\n      \"Password\": \"base64-shortcode-passkey-timestamp\",\n      \"Timestamp\": \"20260520101630\",\n      \"TransactionType\": \"CustomerPayBillOnline\",\n      \"Amount\": 100,\n      \"PartyA\": \"254708374149\",\n      \"PartyB\": \"174379\",\n      \"PhoneNumber\": \"254708374149\",\n      \"CallBackURL\": \"https://example.test/api/mpesa/stk-callback\",\n      \"AccountReference\": \"ORDER-1001\",\n      \"TransactionDesc\": \"Order payment\"\n    }\n\n## Successful Callback Shape\n\n    {\n      \"Body\": {\n        \"stkCallback\": {\n          \"MerchantRequestID\": \"29115-34620561-1\",\n          \"CheckoutRequestID\": \"ws_CO_20052026101630000000\",\n          \"ResultCode\": 0,\n          \"ResultDesc\": \"The service request is processed successfully.\",\n          \"CallbackMetadata\": {\n            \"Item\": [\n              { \"Name\": \"Amount\", \"Value\": 100 },\n              { \"Name\": \"MpesaReceiptNumber\", \"Value\": \"TEK7X12345\" },\n              { \"Name\": \"TransactionDate\", \"Value\": 20260520101701 },\n              { \"Name\": \"PhoneNumber\", \"Value\": 254708374149 }\n            ]\n          }\n        }\n      }\n    }\n\n## Failed Callback Shape\n\n    {\n      \"Body\": {\n        \"stkCallback\": {\n          \"MerchantRequestID\": \"29115-34620561-2\",\n          \"CheckoutRequestID\": \"ws_CO_20052026102030000000\",\n          \"ResultCode\": 1032,\n          \"ResultDesc\": \"Request cancelled by user\"\n        }\n      }\n    }\n\nFile v1.1.0:references/maintenance.md\n\n# Skill Maintenance\n\nUse this reference when updating mpesa-daraja or preparing a ClawHub release.\n\n## Source Update Check\n\nBefore changing examples, production guidance, endpoint assumptions, or publishing a new version, check current official sources:\n\n- Safaricom Daraja developer portal: https://developer.safaricom.co.ke/\n- The specific Daraja API page for the flow being updated: STK Push, C2B, B2C, Transaction Status, Reversal, or Account Balance.\n- Safaricom developer announcements or release notes when available.\n\nPrefer 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.\n\n## Versioning\n\nUse semantic versioning:\n\n- Patch: typo fixes, safer wording, small test fixture updates. Example: 1.0.1.\n- Minor: new references, scripts, flow coverage, or reusable examples. Example: 1.1.0.\n- Major: breaking changes in skill behavior, trigger scope, file structure, or safety policy. Example: 2.0.0.\n\nAlways update the version field in SKILL.md before publishing to ClawHub.\n\n## Pre-Publish Validation\n\nRun:\n\n    python3 scripts/scan_skill_safety.py .\n    python3 scripts/validate_daraja_fixture.py stk-request path/to/request.json\n    python3 scripts/validate_daraja_fixture.py stk-callback path/to/callback.json\n\nOnly run fixture validation when fixture files exist. The safety scan should run every time.\n\n## Release Notes Template\n\n    mpesa-daraja vX.Y.Z\n\n    Changed:\n    - ...\n\n    Sources checked:\n    - Safaricom Daraja portal\n    - ...\n\n    Validation:\n    - scan_skill_safety.py\n    - fixture validation, if applicable\n\n    Notes:\n    - No production credentials or live payment calls used.\n\n## Publishing Rule\n\nDo 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.\n\nFile v1.1.0:references/production-readiness.md\n\n# Production Readiness\n\nUse this reference for launch reviews, client handoff, production-access preparation, or audits of a Daraja integration.\n\n## Hard Gates\n\n- Fresh user approval is required before calling production endpoints or triggering payment prompts, payouts, reversals, or customer-facing payment activity.\n- Real credentials must live in a secret manager or deployment environment, not in repository files.\n- Callback URLs must be public HTTPS endpoints owned by the deployment.\n- Fulfillment must be idempotent and coupled to confirmed payment state transitions.\n- Operators need a manual reconciliation path before production traffic.\n\n## Security\n\n- Mask phone numbers, access tokens, passkeys, initiator credentials, security credentials, receipt numbers, and raw callback payloads in logs.\n- Protect callback endpoints with rate limits, request size limits, and structured payload validation.\n- Keep raw callback persistence access-controlled and retention-limited.\n- Separate sandbox and production credentials and config.\n- Add a deny-by-default guard so tests cannot accidentally hit production.\n\n## Reliability\n\n- Cache OAuth tokens until shortly before expiry.\n- Retry only safe transient failures, such as timeouts, 429, and 5xx, according to a bounded policy.\n- Do not blindly retry validation errors or duplicate callbacks.\n- Alert on callback failures, stuck prompt_sent payments, repeated Daraja errors, and reconciliation mismatches.\n- Store enough correlation data to investigate without storing sensitive raw values in logs.\n\n## Data Model\n\nRecommended uniqueness constraints:\n\n- CheckoutRequestID\n- MerchantRequestID\n- MpesaReceiptNumber when present\n- Internal order/payment intent ID\n\nRecommended audit fields:\n\n- internal payment ID\n- Daraja request IDs\n- normalized state\n- masked phone number\n- amount and currency\n- callback received timestamp\n- reconciliation status\n- operator/manual-review notes\n\n## Launch Checklist\n\n- Official Daraja docs checked for current endpoints and fields.\n- Sandbox happy path and failure path tested.\n- Duplicate callback test passes.\n- Timeout/cancellation path tested.\n- Production config cannot be enabled in local/test by accident.\n- .env.example contains placeholders only.\n- Logs reviewed for secret and personal-data leakage.\n- Run scripts/scan_skill_safety.py on skill examples and project fixtures before sharing code.\n\nFile v1.1.0:references/stk-push.md\n\n# STK Push / Lipa na M-Pesa Online\n\nUse this reference when implementing, reviewing, or testing customer-initiated checkout through Daraja STK Push.\n\n## Official Source Check\n\nBefore implementation or publishing skill updates, verify the current endpoint names, field names, result codes, and sandbox behavior against official Safaricom Daraja documentation:\n\n- Daraja portal: https://developer.safaricom.co.ke/\n- STK Push / Lipa na M-Pesa Online docs in the Daraja portal\n- Safaricom developer release notes, notices, or portal announcements when available\n\nDo not copy secrets, access tokens, real phone numbers, receipt numbers, or production callback payloads from docs, dashboards, logs, or client systems into generated code.\n\n## Implementation Shape\n\nPrefer these boundaries:\n\n- MpesaClient: OAuth token, STK password, outbound request, response mapping.\n- PaymentIntent or Payment: internal state and idempotency keys.\n- PaymentService: creates intent, validates amount/order state, calls MpesaClient.\n- CallbackRoute: validates callback shape, stores raw event safely, returns fast.\n- ReconciliationJob: checks stale or ambiguous payments.\n\nKeep HTTP calls injectable so tests can mock Daraja without touching external endpoints.\n\n## Request Rules\n\n- Generate timestamp as YYYYMMDDHHmmss.\n- Generate STK password as base64(shortcode + passkey + timestamp).\n- Use CustomerPayBillOnline for PayBill flows unless the product intentionally supports a different Daraja-supported transaction type.\n- Normalize phone numbers only through a deliberate, tested formatter. Reject ambiguous or non-Kenyan MSISDN input.\n- Require HTTPS callback URLs outside local tests.\n- Treat MerchantRequestID and CheckoutRequestID as correlation identifiers, not proof of payment.\n\n## Callback Rules\n\n- Return HTTP 200 quickly after durable persistence.\n- Deduplicate by CheckoutRequestID; also store MerchantRequestID and receipt number when present.\n- Successful callbacks must not double-credit on retries.\n- Failed callbacks may omit CallbackMetadata; do not assume it exists.\n- Mask PhoneNumber, access tokens, passkeys, and security credentials in logs.\n- Map known ResultCodes into explicit internal states, but preserve unknown codes for manual review.\n\n## State Mapping\n\nRecommended internal states:\n\n- pending: internal payment intent exists.\n- prompt_sent: Daraja accepted the STK request.\n- paid: successful callback with receipt metadata.\n- failed: clear unsuccessful callback.\n- cancelled: user cancellation, commonly ResultCode 1032.\n- expired: timeout or no response after the product-defined window.\n- manual_review: ambiguous response, unknown result, duplicate conflict, or reconciliation mismatch.\n- reversed: confirmed reversal after operator-approved or compliant reversal flow.\n\n## Review Checklist\n\n- Credentials are environment-backed and never committed.\n- Production endpoint usage requires explicit opt-in.\n- Token caching respects expiry and handles 401 with one safe refresh retry.\n- Checkout creation and callback processing are transactionally safe.\n- Callback handler has shape validation, idempotency, masked logging, and durable event storage.\n- Tests cover success, cancellation, timeout, duplicate callback, malformed callback, and Daraja outage.\n\nFile v1.1.0:references/test-cases.md\n\n# M-Pesa Daraja Test Cases\n\n## Unit Tests\n\n- OAuth token client builds the correct Basic Auth header from consumer key and secret.\n- OAuth token client never logs consumer secret, bearer token, passkey, or security credential.\n- STK password generation returns base64(shortcode + passkey + timestamp).\n- Timestamp uses Daraja format YYYYMMDDHHmmss and is generated in a consistent timezone.\n- STK request rejects non-positive amount values.\n- STK request rejects invalid MSISDN format; normalize local 07xx numbers to 2547xx only when the product explicitly allows it.\n- STK request uses PartyB equal to the configured shortcode.\n- STK request requires an HTTPS callback URL outside local unit tests.\n- Daraja client maps network timeout to retryable internal error.\n- Daraja client maps Daraja ResponseCode/ResultCode into explicit internal states.\n- Callback parser extracts Amount, MpesaReceiptNumber, TransactionDate, and PhoneNumber from CallbackMetadata when ResultCode is 0.\n- Callback parser handles failed callbacks that do not include CallbackMetadata.\n- Callback parser rejects payloads missing Body.stkCallback.\n- Callback handler masks phone numbers in logs.\n- Callback handler is idempotent for duplicate CheckoutRequestID.\n\n## Integration Tests With HTTP Mocking\n\n- Token endpoint returns access_token and expires_in; client caches token and reuses it before expiry.\n- Token refresh happens when cached token is near expiry.\n- STK Push request sends Authorization: Bearer access_token.\n- STK Push request includes BusinessShortCode, Password, Timestamp, TransactionType, Amount, PartyA, PartyB, PhoneNumber, CallBackURL, AccountReference, and TransactionDesc.\n- Daraja 401 causes one token refresh and one retry, then fails cleanly if still unauthorized.\n- Daraja 429 or 5xx is retried according to the app's retry policy.\n- Daraja validation error is not retried.\n- Callback route persists raw callback once and returns HTTP 200 quickly.\n- Duplicate successful callback does not double-credit an order.\n- Failed callback changes payment state to failed/cancelled/expired according to ResultCode mapping.\n\n## Sandbox End-to-End Tests\n\n- Start checkout for a sandbox order and verify a MerchantRequestID and CheckoutRequestID are stored.\n- Simulate a successful callback and verify order/payment state becomes paid.\n- Simulate user cancellation ResultCode 1032 and verify state becomes cancelled.\n- Simulate timeout/expiry and verify state becomes expired or manual_review.\n- Verify reconciliation job can find stale prompt_sent payments.\n- Verify public callback URL is reachable over HTTPS from outside the local machine.\n- Verify logs contain internal order IDs and Daraja request IDs but no access token, passkey, security credential, or full phone number.\n\n## Production Readiness Checks\n\n- Production base URL is gated by explicit environment configuration and cannot be enabled accidentally in tests.\n- All real credentials are loaded from secure environment or secret manager, not committed files.\n- Callback endpoint has rate limiting or upstream protection appropriate for the deployment.\n- Database has uniqueness constraints for CheckoutRequestID, MerchantRequestID, and MpesaReceiptNumber where applicable.\n- Payment fulfillment is idempotent and transactionally coupled to payment-state changes.\n- Reversal and refund processes require explicit operator approval unless the user has built a compliant automated workflow.\n- Monitoring alerts on repeated Daraja failures, callback failures, and stuck pending payments.\n- Manual reconciliation path exists for ambiguous states.\n\n## Example Pytest Names\n\n- test_stk_password_uses_shortcode_passkey_timestamp\n- test_stk_request_requires_https_callback_url\n- test_token_client_masks_secrets_in_logs\n- test_success_callback_extracts_receipt_and_amount\n- test_failed_callback_without_metadata_is_handled\n- test_duplicate_callback_does_not_double_credit_order\n- test_unauthorized_response_refreshes_token_once\n- test_production_endpoint_requires_explicit_opt_in\n\nFile v1.1.0:agents/openai.yaml\n\ninterface:\n  display_name: \"M-Pesa Daraja\"\n  short_description: \"Build and test Safaricom M-Pesa Daraja integrations.\"\n  default_prompt: \"Use the M-Pesa Daraja skill to design, implement, or test an M-Pesa payment integration safely.\"\n\nArchive v1.0.0: 6 files, 8032 bytes\n\nFiles: agents/openai.yaml (235b), references/examples.md (3955b), references/test-cases.md (4031b), scripts/validate_daraja_fixture.py (3384b), SKILL.md (5552b), _meta.json (131b)\n\nFile v1.0.0:SKILL.md\n\n---\nname: mpesa-daraja\ndescription: Build, review, test, or explain Safaricom M-Pesa Daraja API integrations for STK Push/Lipa na M-Pesa Online, C2B validation and confirmation, B2C payouts, transaction status, reversals, callbacks, credential setup, sandbox testing, and production-readiness. Use when Codex needs to create implementation plans, code, examples, mocks, test cases, troubleshooting steps, or security checks for M-Pesa payments in OpenClaw or client applications.\nversion: 1.0.0\nmetadata:\n  openclaw:\n    envVars:\n      - name: MPESA_ENV\n        required: false\n        description: Optional Daraja environment selector, usually sandbox or production.\n      - name: MPESA_CONSUMER_KEY\n        required: false\n        description: Optional Daraja API consumer key for implementation examples.\n      - name: MPESA_CONSUMER_SECRET\n        required: false\n        description: Optional Daraja API consumer secret for implementation examples.\n      - name: MPESA_SHORTCODE\n        required: false\n        description: Optional Paybill or Till shortcode for Daraja examples.\n      - name: MPESA_PASSKEY\n        required: false\n        description: Optional STK Push passkey for Daraja examples.\n      - name: MPESA_CALLBACK_URL\n        required: false\n        description: Optional public HTTPS callback URL for Daraja sandbox or production testing.\n      - name: MPESA_INITIATOR_NAME\n        required: false\n        description: Optional Daraja initiator name for B2C, reversal, and transaction status flows.\n      - name: MPESA_SECURITY_CREDENTIAL\n        required: false\n        description: Optional encrypted security credential for B2C, reversal, and transaction status flows.\n---\n\n# M-Pesa Daraja\n\n## Operating Rules\n\n- Treat this as a payment-integration skill, not permission to move money.\n- Default to Daraja sandbox endpoints and test credentials unless the user explicitly asks for production guidance.\n- Never store consumer secrets, passkeys, initiator passwords, security credentials, access tokens, callback payloads with phone numbers, or transaction IDs in committed files.\n- Ask before calling any live/production endpoint or sending any request that could trigger a payment prompt, payout, reversal, or customer-facing callback.\n- Prefer environment variables and secret managers over \\`.env\\` files in shared repos. If \\`.env.example\\` is needed, use placeholder values only.\n- Make callbacks idempotent: verify request shape, persist raw event safely, deduplicate by CheckoutRequestID/ConversationID/TransactionID, then process business state transitions.\n\n## Workflow\n\n1. Identify the payment flow:\n   - **STK Push / Lipa na M-Pesa Online** for customer-initiated checkout.\n   - **C2B** for paybill/till customer payments with validation and confirmation callbacks.\n   - **B2C** for business payouts to customers or agents.\n   - **Transaction Status / Reversal** for reconciliation and recovery.\n2. Confirm runtime context: language/framework, sandbox vs production, callback URL availability, persistence layer, and compliance constraints.\n3. Design the integration around these boundaries:\n   - token generation and caching\n   - request signing/password generation\n   - outbound Daraja client\n   - callback receiver\n   - idempotency/reconciliation\n   - observability without leaking personal or payment data\n4. Use \\`references/examples.md\\` when the user asks for sample OpenClaw prompts, implementation examples, or architecture snippets.\n5. Use \\`references/test-cases.md\\` when the user asks for QA scenarios, unit tests, integration tests, sandbox tests, or production-readiness checks.\n6. Use \\`scripts/validate_daraja_fixture.py\\` to sanity-check example STK Push request and callback JSON fixtures before writing tests around them.\n\n## Implementation Guidance\n\n- Keep Daraja client code small and injectable so app tests can mock HTTP calls.\n- Generate the STK password as \\`base64(shortcode + passkey + timestamp)\\` with timestamp format \\`YYYYMMDDHHmmss\\`.\n- Use OAuth access tokens from Daraja and cache them until shortly before expiry.\n- Expose public HTTPS callback URLs in sandbox using a stable tunnel or deployed test environment; avoid local-only callback URLs.\n- Return fast from callbacks after durable persistence; do heavier fulfillment asynchronously when possible.\n- Log correlation IDs, response codes, and internal order IDs. Mask phone numbers, names, access tokens, credentials, and raw security credentials.\n- Model payment state explicitly, for example: \\`pending\\`, \\`prompt_sent\\`, \\`paid\\`, \\`failed\\`, \\`cancelled\\`, \\`expired\\`, \\`reversed\\`, \\`manual_review\\`.\n\n## Common OpenClaw Requests\n\n- \"Use $mpesa-daraja to add STK Push checkout to this Django app.\"\n- \"Use $mpesa-daraja to write sandbox test cases for our M-Pesa checkout.\"\n- \"Use $mpesa-daraja to review this Daraja callback handler for idempotency and secret leakage.\"\n- \"Use $mpesa-daraja to create an OpenClaw automation that drafts M-Pesa reconciliation reports without touching production APIs.\"\n\n## Deliverables To Prefer\n\n- For implementation: client module, callback route, typed request/response models, masked logging, tests, and \\`.env.example\\`.\n- For review: findings ordered by payment risk, security risk, idempotency/reconciliation risk, then code quality.\n- For planning: flow diagram in text, endpoint list, secret list, callback contract, test plan, and production checklist.\n- For troubleshooting: exact failing step, expected Daraja response, likely causes, and the smallest safe verification step.\n\nFile v1.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn72x5vtcasyn6bm1yyg4xqv0s86rdry\",\n  \"slug\": \"mpesa-daraja\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1779308115176\n}\n\nFile v1.0.0:references/examples.md\n\n# M-Pesa Daraja Examples\n\n## OpenClaw Usage Examples\n\nUse these as realistic prompts for another OpenClaw/Codex session:\n\n- \"Use $mpesa-daraja to add sandbox STK Push checkout to my FastAPI app. Keep all credentials in env vars and add tests for token generation, password generation, outbound request shape, and callback idempotency.\"\n- \"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.\"\n- \"Use $mpesa-daraja to create a Django model and service layer for tracking M-Pesa payments from pending to paid/failed/reversed.\"\n- \"Use $mpesa-daraja to draft a client implementation plan for Paybill C2B validation and confirmation callbacks.\"\n- \"Use $mpesa-daraja to generate QA test cases for STK Push before we request production access.\"\n\n## Minimal STK Push Architecture\n\nRecommended components:\n\n- MpesaClient: handles OAuth, password generation, and Daraja HTTP requests.\n- PaymentService: creates internal payment intent records and calls MpesaClient.\n- CallbackController: receives Daraja callbacks, validates payload shape, persists raw event, and invokes reconciliation.\n- PaymentRepository: enforces idempotency with unique CheckoutRequestID/MerchantRequestID/TransactionID constraints.\n- ReconciliationJob: checks pending/unclear payments using transaction status or internal review queues.\n\nState flow:\n\n    order_created -> payment_pending -> stk_prompt_sent -> paid\n                                          |-> failed\n                                          |-> cancelled\n                                          |-> expired\n                                          |-> manual_review\n    paid -> reversed\n\n## Environment Variables\n\nUse placeholders in examples:\n\n    MPESA_ENV=sandbox\n    MPESA_CONSUMER_KEY=replace-me\n    MPESA_CONSUMER_SECRET=replace-me\n    MPESA_SHORTCODE=174379\n    MPESA_PASSKEY=replace-me\n    MPESA_CALLBACK_URL=https://example.test/api/mpesa/stk-callback\n    MPESA_INITIATOR_NAME=replace-me\n    MPESA_SECURITY_CREDENTIAL=replace-me\n\nDo not commit real values.\n\n## Python Password Generation Example\n\n    import base64\n    from datetime import datetime, timezone\n\n\n    def daraja_timestamp() -> str:\n        return datetime.now(timezone.utc).strftime(\"%Y%m%d%H%M%S\")\n\n\n    def stk_password(shortcode: str, passkey: str, timestamp: str) -> str:\n        raw = f\"{shortcode}{passkey}{timestamp}\".encode(\"utf-8\")\n        return base64.b64encode(raw).decode(\"utf-8\")\n\n## STK Push Request Shape\n\n    {\n      \"BusinessShortCode\": \"174379\",\n      \"Password\": \"base64-shortcode-passkey-timestamp\",\n      \"Timestamp\": \"20260520101630\",\n      \"TransactionType\": \"CustomerPayBillOnline\",\n      \"Amount\": 100,\n      \"PartyA\": \"254708374149\",\n      \"PartyB\": \"174379\",\n      \"PhoneNumber\": \"254708374149\",\n      \"CallBackURL\": \"https://example.test/api/mpesa/stk-callback\",\n      \"AccountReference\": \"ORDER-1001\",\n      \"TransactionDesc\": \"Order payment\"\n    }\n\n## Successful Callback Shape\n\n    {\n      \"Body\": {\n        \"stkCallback\": {\n          \"MerchantRequestID\": \"29115-34620561-1\",\n          \"CheckoutRequestID\": \"ws_CO_20052026101630000000\",\n          \"ResultCode\": 0,\n          \"ResultDesc\": \"The service request is processed successfully.\",\n          \"CallbackMetadata\": {\n            \"Item\": [\n              { \"Name\": \"Amount\", \"Value\": 100 },\n              { \"Name\": \"MpesaReceiptNumber\", \"Value\": \"TEK7X12345\" },\n              { \"Name\": \"TransactionDate\", \"Value\": 20260520101701 },\n              { \"Name\": \"PhoneNumber\", \"Value\": 254708374149 }\n            ]\n          }\n        }\n      }\n    }\n\n## Failed Callback Shape\n\n    {\n      \"Body\": {\n        \"stkCallback\": {\n          \"MerchantRequestID\": \"29115-34620561-2\",\n          \"CheckoutRequestID\": \"ws_CO_20052026102030000000\",\n          \"ResultCode\": 1032,\n          \"ResultDesc\": \"Request cancelled by user\"\n        }\n      }\n    }\n\nFile v1.0.0:references/test-cases.md\n\n# M-Pesa Daraja Test Cases\n\n## Unit Tests\n\n- OAuth token client builds the correct Basic Auth header from consumer key and secret.\n- OAuth token client never logs consumer secret, bearer token, passkey, or security credential.\n- STK password generation returns base64(shortcode + passkey + timestamp).\n- Timestamp uses Daraja format YYYYMMDDHHmmss and is generated in a consistent timezone.\n- STK request rejects non-positive amount values.\n- STK request rejects invalid MSISDN format; normalize local 07xx numbers to 2547xx only when the product explicitly allows it.\n- STK request uses PartyB equal to the configured shortcode.\n- STK request requires an HTTPS callback URL outside local unit tests.\n- Daraja client maps network timeout to retryable internal error.\n- Daraja client maps Daraja ResponseCode/ResultCode into explicit internal states.\n- Callback parser extracts Amount, MpesaReceiptNumber, TransactionDate, and PhoneNumber from CallbackMetadata when ResultCode is 0.\n- Callback parser handles failed callbacks that do not include CallbackMetadata.\n- Callback parser rejects payloads missing Body.stkCallback.\n- Callback handler masks phone numbers in logs.\n- Callback handler is idempotent for duplicate CheckoutRequestID.\n\n## Integration Tests With HTTP Mocking\n\n- Token endpoint returns access_token and expires_in; client caches token and reuses it before expiry.\n- Token refresh happens when cached token is near expiry.\n- STK Push request sends Authorization: Bearer access_token.\n- STK Push request includes BusinessShortCode, Password, Timestamp, TransactionType, Amount, PartyA, PartyB, PhoneNumber, CallBackURL, AccountReference, and TransactionDesc.\n- Daraja 401 causes one token refresh and one retry, then fails cleanly if still unauthorized.\n- Daraja 429 or 5xx is retried according to the app's retry policy.\n- Daraja validation error is not retried.\n- Callback route persists raw callback once and returns HTTP 200 quickly.\n- Duplicate successful callback does not double-credit an order.\n- Failed callback changes payment state to failed/cancelled/expired according to ResultCode mapping.\n\n## Sandbox End-to-End Tests\n\n- Start checkout for a sandbox order and verify a MerchantRequestID and CheckoutRequestID are stored.\n- Simulate a successful callback and verify order/payment state becomes paid.\n- Simulate user cancellation ResultCode 1032 and verify state becomes cancelled.\n- Simulate timeout/expiry and verify state becomes expired or manual_review.\n- Verify reconciliation job can find stale prompt_sent payments.\n- Verify public callback URL is reachable over HTTPS from outside the local machine.\n- Verify logs contain internal order IDs and Daraja request IDs but no access token, passkey, security credential, or full phone number.\n\n## Production Readiness Checks\n\n- Production base URL is gated by explicit environment configuration and cannot be enabled accidentally in tests.\n- All real credentials are loaded from secure environment or secret manager, not committed files.\n- Callback endpoint has rate limiting or upstream protection appropriate for the deployment.\n- Database has uniqueness constraints for CheckoutRequestID, MerchantRequestID, and MpesaReceiptNumber where applicable.\n- Payment fulfillment is idempotent and transactionally coupled to payment-state changes.\n- Reversal and refund processes require explicit operator approval unless the user has built a compliant automated workflow.\n- Monitoring alerts on repeated Daraja failures, callback failures, and stuck pending payments.\n- Manual reconciliation path exists for ambiguous states.\n\n## Example Pytest Names\n\n- test_stk_password_uses_shortcode_passkey_timestamp\n- test_stk_request_requires_https_callback_url\n- test_token_client_masks_secrets_in_logs\n- test_success_callback_extracts_receipt_and_amount\n- test_failed_callback_without_metadata_is_handled\n- test_duplicate_callback_does_not_double_credit_order\n- test_unauthorized_response_refreshes_token_once\n- test_production_endpoint_requires_explicit_opt_in\n\nFile v1.0.0:agents/openai.yaml\n\ninterface:\n  display_name: \"M-Pesa Daraja\"\n  short_description: \"Build and test Safaricom M-Pesa Daraja integrations.\"\n  default_prompt: \"Use the M-Pesa Daraja skill to design, implement, or test an M-Pesa payment integration safely.\"","readmeExcerpt":"Skill: M-Pesa Daraja Owner: nevilpaulo Summary: Advisory reference for Safaricom M-Pesa Daraja integration planning, reviews, sandbox testing, callbacks, reconciliation, and production-readiness. It provid... Tags: latest:1.2.2 Version history: v1.2.2 | 2026-05-25T08:13:01.042Z | user Clarify that the skill is advisory-only and does not require external account access to install or use. Soften marketplace-facing word","codeSnippets":[],"executableExamples":[],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: mpesa-daraja\ndescription: 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.\nversion: 1.2.2\n---\n\n# M-Pesa Daraja\n\nThis is an advisory/reference skill. It does not call Safaricom APIs, initiate payments, or require external account access to install or use.\n\n## Operating Rules\n\n- Treat this as payment-integration guidance, not permission to move money.\n- Default to Daraja sandbox endpoints and placeholder configuration unless the user explicitly asks for production guidance.\n- Never store sensitive Daraja values, callback payloads with phone numbers, or transaction IDs in committed files.\n- Ask before calling any live/production endpoint or sending any request that could trigger a payment prompt, payout, reversal, or customer-facing callback.\n- Prefer environment variables and protected deployment settings over \\`.env\\` files in shared repos. If \\`.env.example\\` is needed, use placeholder values only.\n- Make callbacks idempotent: verify request shape, persist raw event safely, deduplicate by CheckoutRequestID/ConversationID/TransactionID, then process business state transitions.\n\n## Workflow\n\n1. Identify the payment flow:\n   - **STK Push / Lipa na M-Pesa Online** for customer-initiated checkout.\n   - **C2B** for paybill/till customer payments with validation and confirmation callbacks.\n   - **B2C** for business payouts to customers or agents.\n   - **Transaction Status / Reversal** for reconciliation and recovery.\n2. Confirm runtime context: language/framework, sandbox vs production, callback URL availability, persistence layer, and compliance constraints.\n3. Design the integration around these boundaries:\n   - Daraja session setup and caching\n   - request authorization value generation\n   - outbound Daraja client\n   - callback receiver\n   - idempotency/reconciliation\n   - observability without leaking personal or payment data\n4. Use \\`references/examples.md\\` when the user asks for sample OpenClaw prompts, implementation examples, or architecture snippets.\n5. Use \\`references/test-cases.md\\` when the user asks for QA scenarios, unit tests, integration tests, sandbox tests, or production-readiness checks.\n6. Use \\`references/stk-push.md\\` for STK Push/Lipa na M-Pesa Online implementation details.\n7. Use \\`references/api-endpoints.md\\` when the user needs current Daraja endpoint names, versions, or flow coverage.\n8. Use \\`references/production-readiness.md\\` before production-readiness reviews, client handoff, or launch checklists.\n9. 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.\n\n## Implementation Guidance\n\n- Keep Daraja client code small and injectable so app tests can mock HTTP calls.\n- Follo"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn72x5vtcasyn6bm1yyg4xqv0s86rdry\",\n  \"slug\": \"mpesa-daraja\",\n  \"version\": \"1.2.2\",\n  \"publishedAt\": 1779696781042\n}"},{"path":"references/api-endpoints.md","content":"# Daraja Endpoint Reference\n\nUse 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.\n\n## Official Source Snapshot\n\nChecked on 2026-05-25 against:\n\n- Safaricom Daraja developer portal: https://developer.safaricom.co.ke/\n- Safaricom developer API catalogue GraphQL query \\`getApis\\` via https://developer.safaricom.co.ke/api/graphql\n- Portal documentation records through \\`populateApi\\` for STK Push, STK Query, Transaction Status, Reversal, and Account Balance\n\nThe 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.\n\n## Core Sandbox Endpoints\n\n| Flow | Method | Sandbox endpoint |\n| --- | --- | --- |\n| Daraja authorization session | GET | Check the current Daraja portal for the session endpoint before implementation. |\n| STK Push / Lipa na M-Pesa Online | POST | \\`https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest\\` |\n| STK Push query | POST | \\`https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query\\` |\n| C2B register URL | POST | \\`https://sandbox.safaricom.co.ke/mpesa/c2b/v2/registerurl\\` |\n| B2C payment request | POST | \\`https://sandbox.safaricom.co.ke/mpesa/b2c/v3/paymentrequest\\` |\n| Transaction status | POST | \\`https://sandbox.safaricom.co.ke/mpesa/transactionstatus/v1/query\\` |\n| Account balance | POST | \\`https://sandbox.safaricom.co.ke/mpesa/accountbalance/v1/query\\` |\n| Reversal | POST | \\`https://sandbox.safaricom.co.ke/mpesa/reversal/v1/request\\` |\n\n## Guidance\n\n- Use sandbox endpoints by default. Switch to production hostnames only when the user explicitly asks for production guidance, and ask before any live call.\n- Re-check the official catalogue before writing production instructions. Endpoint versions can differ from older examples and SDK snippets.\n- 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.\n- 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\n\n## OpenClaw Usage Examples\n\nUse these as realistic prompts for another OpenClaw/Codex session:\n\n- \"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.\"\n- \"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.\"\n- \"Use $mpesa-daraja to create a Django model and service layer for tracking M-Pesa payments from pending to paid/failed/reversed.\"\n- \"Use $mpesa-daraja to draft a client implementation plan for Paybill C2B validation and confirmation callbacks.\"\n- \"Use $mpesa-daraja to generate QA test cases for STK Push before we request production access.\"\n\n## Minimal STK Push Architecture\n\nRecommended components:\n\n- MpesaClient: handles Daraja session setup, STK authorization value generation, and Daraja HTTP requests.\n- PaymentService: creates internal payment intent records and calls MpesaClient.\n- CallbackController: receives Daraja callbacks, validates payload shape, persists raw event, and invokes reconciliation.\n- PaymentRepository: enforces idempotency with unique CheckoutRequestID/MerchantRequestID/TransactionID constraints.\n- ReconciliationJob: checks pending/unclear payments using transaction status or internal review queues.\n\nState flow:\n\n    order_created -> payment_pending -> stk_prompt_sent -> paid\n                                          |-> failed\n                                          |-> cancelled\n                                          |-> expired\n                                          |-> manual_review\n    paid -> reversed\n\n## Environment Variables\n\nUse placeholders in examples:\n\n    MPESA_ENV=sandbox\n    MPESA_CONSUMER_KEY=replace-me\n    MPESA_PRIVATE_APP_VALUE=replace-me\n    MPESA_SHORTCODE=174379\n    MPESA_STK_AUTH_VALUE=replace-me\n    MPESA_CALLBACK_URL=https://example.test/api/mpesa/stk-callback\n    MPESA_INITIATOR_NAME=replace-me\n    MPESA_PROTECTED_OPERATOR_VALUE=replace-me\n\nDo not commit real values.\n\n## Python STK Authorization Value Example\n\n    import base64\n    from datetime import datetime, timezone\n\n\n    def daraja_timestamp() -> str:\n        return datetime.now(timezone.utc).strftime(\"%Y%m%d%H%M%S\")\n\n\n    def stk_authorization_value(shortcode: str, private_value: str, timestamp: str) -> str:\n        raw = f\"{shortcode}{private_value}{timestamp}\".encode(\"utf-8\")\n        return base64.b64encode(raw).decode(\"utf-8\")\n\n## STK Push Request Shape\n\n    {\n      \"BusinessShortCode\": \"174379\",\n      \"Password\": \"base64-shortcode-private-value-timestamp\",\n      \"Timestamp\": \"20260520101630\",\n      \"TransactionType\": \"CustomerPayBillOnline\",\n      \"Amount\": 100,\n      \"PartyA\": \"254708374149\",\n      \"PartyB\": \"174379\",\n      \"PhoneNumber\": \"254708374149\",\n      \"CallBackURL\": \"https://example.test"},{"path":"references/maintenance.md","content":"# Skill Maintenance\n\nUse this reference when updating mpesa-daraja or preparing a ClawHub release.\n\n## Source Update Check\n\nBefore changing examples, production guidance, endpoint assumptions, or publishing a new version, check current official sources:\n\n- Safaricom Daraja developer portal: https://developer.safaricom.co.ke/\n- The specific Daraja API page for the flow being updated: STK Push, C2B, B2C, Transaction Status, Reversal, or Account Balance.\n- Safaricom developer announcements or release notes when available.\n\nPrefer 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.\n\n## Versioning\n\nUse semantic versioning:\n\n- Patch: typo fixes, safer wording, small test fixture updates. Example: 1.0.1.\n- Minor: new references, scripts, flow coverage, or reusable examples. Example: 1.1.0.\n- Major: breaking changes in skill behavior, trigger scope, file structure, or safety policy. Example: 2.0.0.\n\nAlways update the version field in SKILL.md before publishing to ClawHub.\n\n## Pre-Publish Validation\n\nRun:\n\n    python3 scripts/scan_skill_safety.py .\n    python3 scripts/validate_daraja_fixture.py stk-request path/to/request.json\n    python3 scripts/validate_daraja_fixture.py stk-callback path/to/callback.json\n\nOnly run fixture validation when fixture files exist. The safety scan should run every time.\n\n## Release Notes Template\n\n    mpesa-daraja vX.Y.Z\n\n    Changed:\n    - ...\n\n    Sources checked:\n    - Safaricom Daraja portal\n    - ...\n\n    Validation:\n    - scan_skill_safety.py\n    - fixture validation, if applicable\n\n    Notes:\n    - No production account values or live payment calls used.\n\n## Publishing Rule\n\nDo 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."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":1872,"uniquenessScore":40,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-11T23:15:27.288Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-11T23:15:27.288Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-13T15:26:42.843Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}